Apollo Federation (Gateway): how to send different "keys" to different implementing microservices? - apollo

I am trying to compose a federated apollo service with a gateway and 3 implementing microservices.
MS1:
type Movie #key(fields: "id name") {
id: String!
name: String
}
MS2:
type Location #key(fields: "id") {
id: String!
longitude: Float!
latitude: Float!
}
type Movie #key(fields: "id") #extends {
id: String! #external
location: Location
}
MS3:
type Award #key(fields: "name"){
name: String!
count: Int
}
type Movie #key(fields: "name") #extends {
name: String! #external
award: Award
}
MS1 provides a name and an id.
name shall be send to MS3 for the resolving process and id shall be used by MS2 for the resolving process.
This approach worked with an older version of apollo-gateway / apollo-server, but with the latest versions I receive the following error messages.
{"stack":"Error: A valid schema couldn't be composed. The following composition errors were found:\n\t[ms2] Movie -> extends from ms1 but specifies an invalid #key directive. Valid #key directives are specified by the originating type. Available #key directives for this type are:\n\t#key(fields: \"id name\")\n ...}
An approach to provide the keys like this didn't work either:
type Movie #key(fields: "id") #key(fields: "name") {
What does the schema have to look like to provide the described use case?

Related

Unknown directive "connection" -- AWS Appsync graphql [duplicate]

I am currently developing an eCommerce App in React Native Expo that utilizes AWS amplify and I am trying to connect the API and database to it.
After running 'amplify push', I get the following error message:
� An error occurred during the push operation: Unknown directive 'connection'. Either remove the directive from the schema or add a transformer to handle it.
For some reason, it doesn't recognize #connection as its own directive.
The block of code in the schema.graphql that utilizes this directive is the following:
type CartProduct #model #auth(rules: [{allow: public}]){
id: ID!
userSub: String!
quantity: Int!
option: String
productID: ID!
product: Product #connection(fields: ["productID"])
}
I haven't found a solution anywhere and I appreciate anyone who can help me with this.
This github issue in the aws-amplify repo helped me solve a similar error. The gist of the problem is the graphql transformer version that interprets the schema definition was upgraded to v2 and the #key and #connection directives are in a version 1 directives.
For version 2 I used #hasMany and #hasOne directives.
type PurchaseOrder #model #auth(rules: [{ allow: owner }]) {
id: ID!
poNumber: String
dateOrdered: AWSDateTime
dateRequested: AWSDateTime
dateReceived: AWSDateTime
notes: String
subtotal: Float
taxRate: Float
salesTax: Float
priceAdjustment: Float
discount: Float
total: Float
requestor: String
paymentTerms: String
shipping: Float
comments: String
billingAddress: Address #hasOne
shippingAddress: Address #hasOne
shippingInformation: String
customerId: String
createdAt: AWSDateTime!
updatedAt: AWSDateTime!
poItems: [PoItem] #hasMany
attachments: [Attachment] #hasMany
vendorID: ID!
vendor: Vendor #hasOne
}

AppSync - unable to create a schema with an array when the array property has `#key` directive associated with it

I recently added a #key directive to a schema field so that I could search by that field, although I now get an error implying the previously correct data type is not what is expected. It says it expects a string but got a list, although the field is a list containing an enum.
Is it possible to query by a specific field without using #key? If not, how can I create records when the #key (seemingly) changes the expected type of a list to a string?
Below is my code, I added the #key to the categories list, but now when I try to create a new Journey I get the error shown below.
This is the schema:
type Journey #model
#auth(rules: [{ allow: owner, operations: [create, delete, update] }])
#key(name: "getJourneysByCategories", fields: ["categories"], queryField: "getJourneysByCategories")
#key(name: "getJourneysByName", fields: ["name"], queryField: "getJourneysByName") {
id: ID!
name: String!
description: String
coverImage: String
isPrivate: Boolean!
members: [JourneyUsersBridge] #connection(name: "JourneyUsers")
moderators: [JourneyModeratorsBridge] #connection(name: "ModeratedBy")
creator: User! #connection(name: "JourneyCreator")
goals: [Goal] #connection(name: "JourneyGoals")
posts: [Post] #connection(name: "JourneyPosts")
categories: [JourneyCategory!]!
}
This is an example of the code used to create a Journey:
type CreateJourneyInput = {
name: string;
description?: string | null;
coverImage?: string | null;
isPrivate: boolean;
categories: Array<JourneyCategory | null>;
journeyCreatorId: string;
};
const journey: CreateJourneyInput = {
name: 'My GraphQL Journey',
description: 'This Journey was made using GraphQL minus DataStore!',
isPrivate: false,
categories: [JourneyCategory.EXMAMPLE],
journeyCreatorId: userId
};
const result = await API.graphql(
graphqlOperation(mutations.createJourney, { input: journey })
);
This is the full error:
"One or more parameter values were invalid: Type mismatch for Index Key categories Expected: S Actual: L IndexName: getJourneysByCategories (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ValidationException; Request ID: U4EB7HPO1GU5LHVAOPMGAJJHANVV4KQNSO5AEMVJF66Q9ASUAAJG)"
So I removed the getJourneysByCategories key and the upload worked fine. What's happening here and why?
I just want to be able to query by categories - perhaps #key wasn't the right route to take? Does the key directive not work with lists?

User connection to another User as a friend relationship - AWS GraphQL Transform

I'm using AWS GraphQL Transform to build my schema, and I can't find any resources that point me to how I make a connection between one user and another without creating another type object.
type User #model)
{
id: ID!
email: String!
password: String!
friends: [User] #connection(name: "UserFriendConn")
blockedUsers: [User] #connection(name: "UserBlockedConn")
friendRequests: [User] #connection(name: "UserFriendRequestConn")
}
Calling amplify api gql-compile will throw error: InvalidDirectiveError: Found one half of connection "UserFriendConn" at User.friends but no related field on type User.
Am I applying an entirely wrong pattern here?
This is what your connections should look like. Given that your other types are:
Friend
BlockedUser
FriendRequest
type User #model) {
id: ID!
email: String!
password: String!
friends: [Friend] #connection(name: "UserFriendConn")
blockedUsers: [BlockedUser] #connection(name: "UserBlockedConn")
friendRequests: [FriendRequest] #connection(name: "UserFriendRequestConn")
}

AWS Amplify not generating proper graphql input depth

I am new to both graphql & AWS Amplify, so please forgive any ignorance :)
I have a graphql schema like this:
type Location #model #auth(rules: [{allow: owner}]){
street: String
city: String
state: String
zip: String
}
type Trip #model #auth(rules: [{allow: owner}]){
id: String!
...
location: Location
}
I'm trying to create both the location and the trip at the same time with a mutation request like this:
mutation {
createTrip(input: {
id: "someIdentifier",
location: {
street: "somewhere"
}
}) {
id
location {
street
}
}
}
But I'm getting an error like this:
{
"data": null,
"errors": [
{
"path": null,
"locations": [
{
"line": 2,
"column": 21,
"sourceName": null
}
],
"message": "Validation error of type WrongType: argument 'input' with value '...' contains a field not in 'CreateTripInput': 'location' # 'createTrip'"
}
]
}
Checking the generated schema.graphql file, I see that there is indeed no location object on the input model:
input CreateTripInput {
id: String!
...
}
How can I have amplify generate the proper input schema so that I can create both the Trip and the location objects at the same time?
I was able to get an answer from the aws-amplify team here. To summarize:
Both Trip and Location have model directive. There isn't a #connection directive connecting the Trip with Location. The two options to "resolving" this is:
Update the schema connecting the models if you want them to be in 2 separate tables and want the ability to query Trip based on Location. Using 2 separate table you won't be able to create both Trip and Location in a single mutation, though. For example:
type Location #model #auth(rules: [{allow: owner}]){
street: String
city: String
state: String
zip: String
trips: Trip #connection(name:"TripLocation")
}
type Trip #model #auth(rules: [{allow: owner}]){
id: String!
location: Location #connection(name:"TripLocation")
}
The second option, if the Location data is very specific to a trip and you don't want to create a separate table, then get rid of #model directive from your Location type. Doing so would allow you to create Location as a part of same mutation.
type Location {
street: String
city: String
state: String
zip: String
}
type Trip #model #auth(rules: [{allow: owner}]){
id: String!
location: Location
}
The later was the solution that I moved forward with.

Graphql Update Syntax issue with App sync

I am not familiar with appsync's syntax with graphql. I am trying to update one of my entities using app sync. I used Amazon's option to automatically allocate resources and connect them to DynamoDB. Here is my entity:
type Property {
id: ID!
address: String!
listedDate: AWSDate!
notes: String
homeType: String!
tenantName: String!
ownerName: String!
leaseExpDate: AWSDate!
}
Inside of my mutations, I have this:
type Mutation {
updateProperty(input: UpdatePropertyInput!): Property
}
Along with this input:
input UpdatePropertyInput {
id: ID!
address: String
listedDate: AWSDate
notes: String
homeType: String
tenantName: String
ownerName: String
leaseExpDate: AWSDate
}
Here is my attempt at the mutation to update the given property:
mutation updateProperty {
updateProperty(input: UpdatePropertyInput(id: 'myID')) {
address: String!
}
}
The closest implementation that I found in the appsync's docs can be found here.
Input objects are constructed using brackets like below:
mutation updateProperty {
updateProperty(input: {
id: "myID",
address: "myAddress"
}) {
address
}
}
Here is some additional documentation on input objects can be found here:
https://graphql.org/learn/schema/#input-types
https://graphql.org/graphql-js/mutations-and-input-types/