Dynamoose: No table has been registered - dynamodb-queries

I have a Dynamodb table that is being queried using Dynamoose but I am getting an error when the query is being run:
CustomError [OtherError]: No table has been registered for dev-production-history-table model. Use `new dynamoose.Table` to register a table for this model.
Here is the first part of the code:
"id": String,
"medicationCode": String,
"createdYear": { hashKey: true, type: String },
"createdDateComposite": { rangeKey: true, type: String },
"createdDateTime": String,
"serviceUserId": String,
"createdBy": String,
"updatedBy": String,
"startDate": String,
"numberOfDaysToProduce": Number,
"recurrence": String,
"additionalInfo": String,
"days": String,
"recurrenceValue": String,
"status": String
}, {
saveUnknown: true,
timestamps: true,
}, );
const model = dynamoose.model<ProductionHistory>(DatabaseTableNames.productionHistoryTable,
schema);```
Could someone please explain how I register the table for the model as I am finding it hard to find any documentation with an example?
Thanks.

Related

Partial Update Mutations(GraphQL)

How can I be able to update a node with only one field change and leave the rest of the fields alone?
My User Type
type User {
id: ID!
user_id: String!
username: String!
email: String!
role: Role!
isVerified: Boolean!
}
My Input Types
input UserUpdateInput {
user_id: String
username: String
email: String
password: String
role: Role
isVerified: Boolean
}
input UserWhereUniqueInput {
id: ID
user_id: String
email: String
}
My Mutation type
type Mutation {
updateUser(data: UserUpdateInput!, where: UserWhereUniqueInput): User
}
My Resolver
function updateUser(root, args, context, info){
return context.db.mutation.updateUser({
data: args.data,
where: {
id: args.where.id
}
}, info)
}
This is the request am sending on the GraphQL playground
mutation{
updateUser(
data: {
isVerified: true
}
where:{
user_id : "afc485b"
}
)
{
isVerified
}
}
This is the error am getting
{
"errors": [
{
"message": "Cannot read property 'mutation' of undefined",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"updateUser"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"TypeError: Cannot read property 'mutation' of undefined"
Someone help me. What am I missing?
After updating my server as suggested by Daniel Rearden on the answer section, am getting a new error
{
"message": "Cannot read property 'updateUser' of undefined",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"updateUser"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"TypeError: Cannot read property 'updateUser' of undefined"
The error is the result of not correctly adding the db property to you context. Assuming you're still using version 1, your code should look something like this:
const { prisma } = require('./generated/prisma-client')
const server = new ApolloServer({
...
context: {
db: prisma,
},
})
First thing I notice is you GQL query is not correct.
Yours:
mutation{
updateUser(
data: {
isVerified: true
}
where:{
user_id : "afc485b"
}
)
{
isVerified
}
}
after the word "mutation" you set a name to the call, i.e.
"UpdateUser" but can literally be anything. for each part of the
where clause you need to make the check value an object, i.e.
where: { myProperty: {eq: "some value"}}
So your query should be more like this:
mutation UpdateUser {
updateUser(
data: {isVerified: true}
where:{user_id : {eq: "afc485b"}}
)
{
isVerified
}
}
Hope that helps a little... I didn't fully read the rest but thought this would help with the initial error you were getting.

Problem to structure property of an object using [apollo / graphql]

Problem
Hello friends,
I am working on an api using Apollo Server.
I am having the problem of how to display the nextEpisodeDate property only once. My solution shows nextEpisodeDate in all sub-array in the episodes property and it shouldn't be like that.
I hope someone can help me !
JSON Example
"episodes": [
{
"nextEpisodeDate": "2020-01-17"
},
{
"episode": 3,
"id": "53789/dorohedoro-3",
"imagePreview": "https://cdn.animeflv.net/screenshots/3274/3/th_3.jpg"
},
{
"episode": 2,
"id": "53755/dorohedoro-2",
"imagePreview": "https://cdn.animeflv.net/screenshots/3274/2/th_3.jpg"
},
{
"episode": 1,
"id": "53705/dorohedoro-1",
"imagePreview": "https://cdn.animeflv.net/screenshots/3274/1/th_3.jpg"
}
]
typeDefs
const resolvers = require('./resolvers');
const {gql} = require('apollo-server');
const typeDefs = gql `
extend type Query{
latest_anime: [Animes]
}
type Animes{
title: String
poster: String
synopsis: String
debut: String
type: String
rating: String
genres: [String]
episodes: [Episodes]
}
type Episodes{
nextEpisodeDate: String
episode: String
id: String
imagePreview: String
}
`
module.exports = {
typeDefs,
resolvers
};
Apollo Playground
query{
latest_anime{
title
poster
synopsis
debut
type
rating
genres
episodes{
nextEpisodeDate
episode
id
imagePreview
}
}
}
Apollo Playground Output
{
"data": {
"latest_anime": [
{
"title": "Tsugumomo OVA",
"poster": "https://animeflv.net/uploads/animes/covers/3275.jpg",
"synopsis": "OVA 4.6Kazuya Kagami nunca va a ningún lado sin su preciada “Sakura Obi” que su madre le regaló. Un día, una hermosa chica vestida con un kimono llamada Kiriha aparece ante él. Naturalmente, ella comienza a vivir en su habitación. ¿Naturalmente? ¡Esto solo es el inicio de la embarazosa y confusa...",
"debut": null,
"type": "OVA",
"rating": "4.6",
"genres": [
"accion",
"comedia",
"ecchi",
"escolares",
"seinen",
"sobrenatural"
],
"episodes": [
{
"nextEpisodeDate": null,
"episode": null,
"id": null,
"imagePreview": null
},
{
"nextEpisodeDate": null,
"episode": "1",
"id": "53753/tsugumomo-ova-1",
"imagePreview": "https://cdn.animeflv.net/screenshots/3275/1/th_3.jpg"
}
]
},
]
}
}
The only way you can get the desired response structure is to have two separate types. A field must have exactly one type, but you can use an abstract type like a union or interface in order to have each individual item in the list resolve to one of multiple types at runtime.
type AiredEpisode implements Episode {
id: String
episode: String
imagePreview: String
}
type UpcomingEpisode implements Episode {
id: String
nextEpisodeDate: String
}
interface Episode {
id: String
}
type Anime {
episodes: [Episode]
# other fields
}
You would then query the episodes like this:
query {
latest_anime {
episodes {
# fields on the interface itself like id are common to all
# implementing types so they don't need to be inside a fragment
id
# fields specific to one of the types need to be inside a fragment
... on UpcomingEpisode {
nextEpisodeDate
}
... on AiredEpisode {
id
episode
imagePreview
}
}
}
}
Side note: if your API doesn't return an id for the upcoming episodes, you should still provide one (you could use the show's id, for example, you just want to make sure it's unique). This will ensure that you don't run into caching issues if you use a client like Apollo on the front end.

Aws AppSync Query erring out while using a resolver

Im new to AWS AppSync however its been pretty easy to learn and understand.
Im trying to create a resolver that when the user runs getChore(id: "") it will return all the chore information. Which its successfully doing, the problem is within the chore there are two fields: createdBy & assignedTo which are linked to a user type.
type Chore {
id: ID!
title: String
desc: String
status: String
reward: Float
retryDeduction: Float
required: Boolean
createdDate: AWSDateTime
date: AWSDateTime
interval: String
assignedTo: User
createdBy: User
}
type User {
id: ID!
age: Int
f_name: String
l_name: String
type: Int
admin: Boolean
family: Family
}
within aws appsync in trying to attach a resolver to assignedTo: User and createdBy: User so my query will look like:
query getChore {
getChore(id: "36d597c8-2c7e-4f63-93ee-38e5aa8f1d5b") {
id
...
...
assignedTo {
id
f_name
l_name
}
createdBy {
id
f_name
l_name
}
}
}
however when i fire off this query im getting an error:
The provided key element does not match the schema (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: ValidationException;
which i have researched and cant seem to find the correct soltuion.
The resolver im using is:
{
"version": "2017-02-28",
"operation": "GetItem",
"key": {
"id": $util.dynamodb.toDynamoDBJson($ctx.args.id),
}
}
return:
$util.toJson($ctx.result)
When you get the The provided key element does not match the schema error, it's because your request mapping template key doesn't match the primary key in DynamoDB. You can enable CloudWatch Logs in your Application settings to see exactly what was sent to DynamoDB.
I'm not able to know what's wrong with your template because your sample lacks some information, if you can answers the questions pertaining to your application:
- Where are the users stored? Are they stored in their own DDB table separate from the chores, and is the hash key on the users table id as well?
- In the chores table how do you know which user your chore is assignedTo or createdBy? Is there a user id stored on the chore DDB item?
- Is the request mapping template you posted corresponding to the resolver attached to Chore.assignedTo? If yes, using $ctx.args.id will actually do a GetItem based on the chore id not the user it's assigned to.
Finally, I reproduced your application and I was able to make it work with a few changes.
Prerequisites:
I have a chores and a users DynamoDB table with both having id as hash key. These two tables are mapped as datasources in AppSync.
I have one chore in the chores tables that looks like
{
"assignedTo": "1",
"createdBy": "2",
"id": "36d597c8-2c7e-4f63-93ee-38e5aa8f1d5b",
"title": "Chore1"
}
and two users in the users table:
{
"f_name": "Alice",
"id": "2",
"l_name": "Wonderland"
}
and
{
"f_name": "John",
"id": "1",
"l_name": "McCain"
}
I used your GraphQL schema
Resolvers
Resolver on Query.getChore pointing to the chores table:
{
"version": "2017-02-28",
"operation": "GetItem",
"key": {
"id": $util.dynamodb.toDynamoDBJson($ctx.args.id),
}
}
Resolver on Chore.assignedTo pointing to the users table (note the $ctx.source.assignedTo instead of $ctx.args)
{
"version": "2017-02-28",
"operation": "GetItem",
"key": {
"id": $util.dynamodb.toDynamoDBJson($ctx.source.assignedTo),
}
}
Similarly, resolver on Chore.createdBy pointing to the users table:
{
"version": "2017-02-28",
"operation": "GetItem",
"key": {
"id": $util.dynamodb.toDynamoDBJson($ctx.source.createdBy),
}
}
All resolvers response mapping template use the pass-through.
Running the query
Finally, when running your query:
query getChore {
getChore(id: "36d597c8-2c7e-4f63-93ee-38e5aa8f1d5b") {
id
assignedTo {
id
f_name
l_name
}
createdBy {
id
f_name
l_name
}
}
}
I get the following results:
{
"data": {
"getChore": {
"id": "36d597c8-2c7e-4f63-93ee-38e5aa8f1d5b",
"assignedTo": {
"id": "1",
"f_name": "John",
"l_name": "McCain"
},
"createdBy": {
"id": "2",
"f_name": "Alice",
"l_name": "Wonderland"
}
}
}
}
Hope it helps!

AWS AppSync: pass arguments from parent resolver to children

In AWS AppSync, arguments send on the main query don't seem to be forwarded to all children resolvers.
type Query {
article(id: String!, consistentRead: Boolean): Article
book(id: String!, consistentRead: Boolean): Book
}
type Article {
title: String!
id: String!
}
type Book {
articleIds: [String]!
articles: [Article]!
id: String!
}
when I call:
query GetBook {
book(id: 123, consistentRead: true) {
articles {
title
}
}
}
the first query to get the book receives the consistentRead param in $context.arguments, but the subsequent query to retrieve the article does not. ($context.arguments is empty)
I also tried articles(consistentRead: Boolean): [Article]! inside book but no luck.
Does anyone know if it's possible in AppSync to pass arguments to all queries part of the same request?
It is possible to pass arguments from parent to child via the response. Let me explain ...
AppSync has several containers inside $context:
arguments
stash
source
arguments and stash are always cleared before invoking a child resolver as evident from these Cloudwatch logs:
At the very end of the parent execution - arguments and stash data are present.
{
"errors": [],
"mappingTemplateType": "After Mapping",
"path": "[getLatestDeviceState]",
"resolverArn": "arn:aws:appsync:us-east-1:xxx:apis/yyy/types/Query/fields/getLatestDeviceState",
"context": {
"arguments": {
"device": "ddddd"
},
"prev": {
"result": {
"items": [
{
"version": "849",
"device": "ddddd",
"timestamp": "2019-01-29T12:18:34.504+13:00"
}
]
}
},
"stash": {"testKey": "testValue"},
"outErrors": []
},
"fieldInError": false
}
and then at the very beginning of the child resolver - arguments and stash are always blank.
{
"errors": [],
"mappingTemplateType": "Before Mapping",
"path": "[getLatestDeviceState, media]",
"resolverArn": "arn:aws:appsync:us-east-1:yyy:apis/xxx/types/DeviceStatePRODConnection/fields/media",
"context": {
"arguments": {},
"source": {
"items": [
{
"version": "849",
"device": "ddddd",
"timestamp": "2019-01-29T12:18:34.504+13:00"
}
]
},
"stash": {},
"outErrors": []
},
"fieldInError": false
}
Workaround 1 - get the argument from the previous result.
In the example above device is always present in the response of the parent resolver, so I inserted
#set($device = $util.defaultIfNullOrBlank($ctx.args.device, $ctx.source.items[0].device))
into the request mapping template of the child resolver. It will try to get the ID it needs from the arguments and then fall back onto the previous result.
Workaround 2 - add the argument to the parent response
Modify your parent resolver response template to include the arguments:
{
"items": $utils.toJson($context.result.items),
"device": "${ctx.args.device}"
}
and then retrieve it in the request mapping template of the child the same way as in the first workaround.
To achieve availability across all related resolvers (nested or those collection-entity related) for me was fine Workaround 2 (tnx Max for such a good answer) but just for child resolvers.
In another case when I needed to resolve entities from collection query (contains other fields besides entity) property added to response mapping template wasn't available anymore.
So my solution was to set it to request headers:
##Set parent query profile parameter to headers to achieve availability accross related resolvers.
#set( $headers = $context.request.headers )
$util.qr($headers.put("profile", $util.defaultIfNullOrBlank($context.args.profile, "default")))
And read this value from your nested/other request mapping templates:
#set($profile = $ctx.request.headers.profile)
This makes the parent argument available wherever I need it between related resolvers. In your case, it would be 'device' and some default value or without that part if not needed.
Add this to BookQuery Response Mapping Template
#set( $book = $ctx.result )
#set($Articles = []);
#foreach($article in $book.articles)
#set( $newArticle = $article )
$util.qr($newArticle.put("bookID", $book.id))
$util.qr($Articles.add($newArticle))
#end
$util.qr($book.put("articles", $Articles))
$util.toJson($book)
Now, every article will have bookID
You should be able to find consistentRead in $context.info.variables ($context.info.variables.consistentRead):
https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference.html#aws-appsync-resolver-context-reference-info
You don't need to pass arguments to sub-query. Base on your schema and use-case, I think you can adjust your schema like below to have a relationship between Author and Book
type Author {
# parent's id
bookID: ID!
# author id
id: ID!
name: String!
}
type Book {
id: ID!
title: String!
author: [Author]!
}
type Mutation {
insertAuthor(bookID: ID!, id: ID!, name: String!): Author
insertBook(id: ID!, title: String!): Book
}
type Query {
getBook(id: ID!): Book
}
- Create table Author with Author.bookID as a primary key and Author.id as a sort key
- Create table Book with Book.id as a primary key
Then, you have to attach a resolver for Book.author
And here is a resolver for insertAuthor mutation
{
"version" : "2017-02-28",
"operation" : "PutItem",
"key" : {
"bookID" : $util.dynamodb.toDynamoDBJson($ctx.args.bookID),
"id" : $util.dynamodb.toDynamoDBJson($ctx.args.id)
},
"attributeValues" : {
"name" : $util.dynamodb.toDynamoDBJson($ctx.args.name)
}
}
And when you do query getBook you will get a list of author that has the same book id as below
Simply in the child use $ctx.source.id where id is the parameter you need reference from the parent.

How can I convert the object array to GraphQL format in Javascript?

I'm working with React, and I send this information:
const imageServicesClean = JSON.stringify(imageServices);
const query = `
mutation {
companyUpdate(
idCompany:${idCompany},
name:${nameClean},
imageServices:${imageServicesClean})
{
idCompany
name
imageServices {
idImageService
name
url
key
}
}
}`;
And the imageServicesClean is sent in this way, but return error:
[{
"idImageService": 1,
"name": "Service1",
"url": "",
"key": "asdasdas"
}, {
"idImageService": 2,
"name": "Service2",
"url": "sdsads",
"key": "sddsfsds_"
}]
Because my GraphQL server (Laravel) just allows the variable without quotes, in this way:
[{
idImageService: 1,
name: "Service1",
url: "",
key: "sdofunc4938urcnnwikk"
}, {
idImageService: 2,
name: "Service2",
url: "sdsads",
key: "sddsfsdssss8347yuirh"
}]
So the function JSON.stringify don't work for build format in GraphQL. How can I convert the object array to GraphQL format in Javascript?
Finally this was my solution:
const imageServicesClean = JSON.stringify(imageServices);
const graphQLImageServices = imageServicesClean.replace(/"([^(")"]+)":/g,"$1:");
But finally I'm working with this library, it does everything for me: https://github.com/atulmy/gql-query-builder
There is a bug in Albert reply. If you have ": somewhere in your string like "field": "\"Hello\": World", then after regexp replace you will end up with something like this: field: "\\Hello\\: World".
I fixed this by adding [^\\"]+ to the regexp, so it looks like
imageServicesClean.replace(/"([^(")"]+[^\\"]+)":/g, "$1:");
I am not quite sure if this is a right fix and do not causes any bugs, but it works for me for now

Resources