How to add business keys/unique constraint in GraphQL - AppSync AWS - amazon-dynamodb

Here is my definition of an entity/model in GraphQL SDL that is used by AWS Amplify to create an App Sync API and its table in DynamoDB.
I want to make the name and author as the business keys. Is there a way to achieve this? I believe Prism has #Unique directive for the same. Or if it is not possible, can a constraint be added in the table in DynamoDB?
type Post
#model
#versioned
#auth(rules: [{allow: owner}])
{
id: ID!
name: String!
author: String!
description: String
}

Related

AWS Amplify Build Issue - StackUpdateComplete

When running amplify push -y in the CLI, my project errors with this message:
["Index: 0 State: {\"deploy\":\"waitingForDeployment\"} Message: Resource is not in the state stackUpdateComplete"]
How do I resolve this error?
The "Resource is not in the state stackUpdateComplete" is the message that comes from the root CloudFormation stack associated with the Amplify App ID. The Amplify CLI is just surfacing the error message that comes from the update stack operation. This indicates that the Amplify's CloudFormation stack may have been still be in progress or stuck.
Solution 1 – “deployment-state.json”:
To fix this issue, go to the S3 bucket containing project settings and deleted the “deployment-state.json” file in root folder as this file holds the app deployment states. The bucket should end with, or contain the word “deployment”.
Solution 2 – “Requested resource not found”:
Check the status of the CloudFormation stack and see if you can notice that the stack failed because of a “Requested resource not found” error indicating that the DynamoDB table “tableID” was missing and confirm that you have deleted it (possibly accidentally). Manually create the above DynamoDB table and retry to push again.
Solution 3A - “#auth directive with 'apiKey':
If you recieve an error stating that “#auth directive with 'apiKey' provider found, but the project has no API Key authentication provider configured”. This error appears when you define a public authorisation in your GraphQL schema without specifying a provider. The public authorization specifies that everyone will be allowed to access the API, behind the scenes the API will be protected with an API Key. To be able to use the public API you must have API Key configured.
The #auth directive allows the override of the default provider for a given authorization mode. To fix the issue specify “IAM” as the provider which allows to use an "Unauthenticated Role" from Cognito Identity Pools for public access instead of an API Key.
Below is the sample code for public authorisation rule:
type Todo #model #auth(rules: [{ allow: public, provider: iam, operations: [create, read, update, delete] }]) {
id: ID!
name: String!
description: String
}
After making the above changes, you can run “amplify update api” and add a IAM auth provider, the CLI generated scoped down IAM policies for the "UnAuthenticated" role automatically.
Solution 3B - Parameters: [AuthCognitoUserPoolId] must have values:
Another issue could occur here, where the default authorization type is API Key when you run the command “amplify add api” without specifying the API type. To fix this issue, follow these steps:
Deleted the the API
Recreate a new one by specifying the “Amazon Cognito user pool” as the authorization mode
Add IAM as an additional authorization type
Re-enable #auth directive in the newly created API Schema
Run “amplify push”
Documentation:
Public Authorisation
Troubleshoot CloudFormation stack issues in my AWS Amplify project

AWS AppSync Null #connection query result

I'm working to create a web app using Amplify, and would like to create a relationship between two tables to keep things DRY. I have 2 tables: Listing and Service. Each Listing should be related to one Service. My (abbreviated) schema looks like this. All queries / resolvers are auto-generated by amplify push
type Listing
#model
#auth(rules: [{ allow: public, operations: [read] }])
{
id: ID!
status: Status
source: String
service: String
serviceDetail: Service #connection (keyName: "service")
}
type Service #model {
id: ID!
name: String
homepage: AWSURL
logo: String
}
To eliminate any other issues, I'm running my tests in the AppSync Console.
Running the ListServices query returns all expected data from the Service table without any errors. Running a getService query on a specific id returns the expected data without any errors.
Running the ListListings query returns with no errors and all expected data from the Listings table. The serviceDetail field only contains null.
I've been digging through the documentation all morning, and can't figure out what's wrong.
So, I defined the #connection wrong. The correct syntax is:
ServiceDetail: Service #connection (fields: ["service"])

Api-Platform with multiple data sources, how to map entity with Doctrine OR ElasticSearch

Api-Platform: v2.5.6, Symfony: v4.4.11
I am building an API with multiple DataSource. For now, one from Postgres, another from ElasticSearch.
For example, I have an Organization entity from postgres and a Stats entity from ElasticSearch.
If ElasticSearch service isn't reachable, I would like the /organizations route to be ok because they don't need ElasticSearch to work.
For now I get "No alive nodes found in your cluster" because it tries to communicate with the "/_cat/indices/organization" route of ElasticSearch.
This dives me to a questions, if it tries everytime to communicate to both ElasticSearch and Postgres, whatever the entity.
How to prevent this, for example can I say that an entity is mapped with a DataSource and not another ?
Or maybe at least set a priority ?
My app/config/packages/api_platform.yaml
api_platform:
allow_plain_identifiers: true
title: HeyAPI Documentation
version: 2.0.0
formats:
jsonld: ['application/ld+json']
json: ['application/json']
graphql: ['application/graphql'] #https://graphql.org/learn/serving-over-http/#post-request
mapping:
paths:
- '%kernel.project_dir%/config/api_platform'
patch_formats:
json: ['application/merge-patch+json']
elasticsearch:
hosts: ['%env(ELASTICSEARCH_URL)%']
swagger:
versions: [3]
api_keys:
Bearer:
name: Authorization
type: header

Using AWS AppSync (with amplify), how does one allow authenticated users read-only access, but only allow mutations for object owners?

I'm using Cognito User Pools as the default authentication method. I'm also using iam for my lambda backend. I'm using an aws appsync client in the lambda function for some custom resolvers.
let's assume I have a User object type that fundamentally looks like this:
type User {
id: ID!
displayName: String!
}
What I want to be able to do:
Allow full read/write access for the object owner.
Allow the lambda function (with iam) full read/write access.
Allow read-only access for users who are authenticated through cognito user pools, but are not the owner of the object.
I've been picking and prodding with the #auth directive attempting to get the results I'm looking for but nothing has been able to work. I've looked at the documentation at AWS GraphQL Transform Docs and I seem to be a bit confused.
Here's what I've tried:
type User
#model
#auth(rules: [
{ allow: owner, operations: [create, update, delete] }
{ allow: private, provider: iam, operations: [update, delete] }
]) {
id: ID!
displayName: String!
}
To my understanding, by removing read from the operations list in the #auth directive removes the check on get and list queries. What am I doing wrong? How do I achieved my desired results?
EDIT: To clarify, I've already enabled multiple authorization types. (cognito user pools by default and iam for the lambda resolvers). My question is: How do I use the #auth directive to get the intended results?
AuthProvider { apiKey iam oidc userPools }
So, I hope this can help you(its worked for me :-))
type User #model #auth(rules: [
{ allow: owner ,operations: [create, update, delete]},
{ allow: private, provider: iam, operations: [read, update, delete] }
{ allow: private, provider: userPools, operations: [read] }
]) {
id: ID!
name: String!
}
To enable both IAM and Cognito, you'll need to follow the instructions on using multiple authorization types: https://aws.amazon.com/blogs/mobile/using-multiple-authorization-types-with-aws-appsync-graphql-apis/
E.g. specifying #aws_iam or #aws_cognito_user_pools on the respective fields
For your use case, you'll probably then also need to add some code in your resolvers to control which users can perform which actions, as described in: https://docs.aws.amazon.com/appsync/latest/devguide/security-authorization-use-cases.html
E.g. along the lines of #if($context.result["Owner"] == $context.identity.username)
If you're looking to do this purely through the Amplify CLI, this person seems to have worked through a very similar problem: https://github.com/aws-amplify/amplify-cli/issues/2694

FOSElasticaBundle - JMSSerializerBundle exclusion strategies not functioning on FOSUserBundle User entity

I'm using the FOSElasticaBundle with the orm persistence driver and the JMSSerializerBundle serializer.
My users are managed by the FOSUserBundle and I'm trying to index these but exclude some fields such as password.
The JMSSerializerBundle exclusion policies such as #ExclusionPolicy("all") #Expose #Exclude are are not having any affect when added to my User entity, which extends FOS\UserBundle\Model\User. However these strategies work as expected on my other entities.
How do I stop fields such as 'salt' and 'password' from being added to the Elasticsearch index?
As a workaround I'm using the 'query_builder_method' FOSElasticaBundle configuration setting to call a method that creates a queryBuilder, which uses DQL Partial Object Syntax to only select the fields that I want to be indexed.
Here's some helpful links:
DQL Partial Object Syntax:
http://docs.doctrine-project.org/en/2.1/reference/dql-doctrine-query-language.html#partial-object-syntax
Use a Custom Doctrine Query Builder:
https://github.com/FriendsOfSymfony/FOSElasticaBundle#use-a-custom-doctrine-query-builder
This is a know thing with Serializer and annotations. You have to define the exclusion strategy also on the parent (=FOS UserBundle) model.
I have solved it with additional yaml configuration:
This is my config:
jms_serializer:
metadata:
directories:
- { path: %kernel.root_dir%/Resources/serializer/FOS, namespace_prefix: 'FOS\UserBundle' }
And within that folder I have for example a User.Model.yml with this content:
FOS\UserBundle\Model\User:
exclusion_policy: ALL
properties:
email:
expose: true

Resources