Unable to do a mutation with a property of type "array of objects" in apollo - graphql

I'm new to all graphql world, so this might be a very easy question, sorry
I'm using graphql-compose-mongoose to generate my graphql schema, here's my mongoose schema:
const ComplainSchema = new Schema({
entityId: {type: String, required: true},
user: {type: UserInfoSchema, required: true},
title: String, // standard types
desc: String,
state: {required: true, type: String, enum: ["DRAFT", "MODERATION", "PUBLIC", "SOLVED"]},
attachments: [{
url: {type: String, required: true},
name: String,
mimeType: String,
attachmentId: Schema.Types.ObjectId
}],
createdAt: {type: Date, index: true},
updatedAt: {type: Date, index: true},
}, {timestamps: {}})
export default mongoose.model('Complaint', ComplainSchema)
If I attempt the following mutation in graphiql it works fine
mutation {
complaintUpdateById(record:{_id:"5bdd9350fe144227042e6a20", title:"ok", desc:"updated", attachments:[{name:"zied", url:"http://zied.com"}]}){
recordId,
record{
_id,
entityId,
user {
userId,
userName,
roleInShop
},
title,
desc,
createdAt,
updatedAt,
attachments{
name,
url
}
}
}
}
and returns this (in case there could be helpful to see the response)
{
"data": {
"complaintUpdateById": {
"recordId": "5bdd9350fe144227042e6a20",
"record": {
"_id": "5bdd9350fe144227042e6a20",
"entityId": "5bd9b1858788f51f44ab678a",
"user": {
"userId": "5bd9ac078788f51f44ab6785",
"userName": "Zied Hamdi",
"roleInShop": "ASA"
},
"title": "ok",
"desc": "updated",
"createdAt": "2018-11-03T12:23:44.565Z",
"updatedAt": "2018-11-05T09:02:51.494Z",
"attachments": [
{
"name": "zied",
"url": "http://zied.com"
}
]
}
}
}
}
Now if I try to pass the attachments to apollo, I don't know how to do that, I don't know which type to provide (Attachment is not the right type obvisouly):
const UPDATE_COMPLAINT = gql `mutation complaintUpdateById($_id:MongoID!, $title: String!, $desc: String!, $attachments: [Attachment]
)
{
complaintUpdateById(record:{_id:$_id, title:$title, desc:$desc, attachments:$attachments}){
recordId,
record{
_id,
entityId,
user {
userId,
userName,
roleInShop
},
title,
desc,
createdAt,
updatedAt
}
}
}`
So searching for the right type, I did a introspection of my object, the issue is that I get the type of attachment as null for this query:
{
__type(name: "Complaint") {
kind
name
fields {
name
description
type {
name
}
}
}
}
this is the response:
{
"data": {
"__type": {
"kind": "OBJECT",
"name": "Complaint",
"fields": [
{
"name": "entityId",
"description": null,
"type": {
"name": "String"
}
},
{
"name": "user",
"description": null,
"type": {
"name": "ComplaintUser"
}
},
{
"name": "title",
"description": null,
"type": {
"name": "String"
}
},
{
"name": "desc",
"description": null,
"type": {
"name": "String"
}
},
{
"name": "state",
"description": null,
"type": {
"name": "EnumComplaintState"
}
},
{
"name": "attachments",
"description": null,
"type": {
"name": null
}
},
{
"name": "createdAt",
"description": null,
"type": {
"name": "Date"
}
},
{
"name": "updatedAt",
"description": null,
"type": {
"name": "Date"
}
},
{
"name": "_id",
"description": null,
"type": {
"name": null
}
}
]
}
}
}
googling didn't help since I don't know how is this operation called, I don't think it's a nested mutation from what I found...

Ok fixed,
I did these steps:
I first introspected the type of attachment in a regular query using the __typename keyword: as follows
mutation {
complaintUpdateById(record:{_id:"5bdd9350fe144227042e6a20", title:"ok", desc:"updated", attachments:[{name:"zied", url:"http://zied.com"}]}){
recordId,
record{
_id,
entityId,
user {
userId,
userName,
roleInShop
},
title,
desc,
createdAt,
updatedAt,
attachments{
__typename,
name,
url
}
}
}
}
it showed up a type named ComplaintAttachments
when replacing the Attachment type with this new value, ComplaintAttachments, an error occured and that error message helped me out:
Variable "$attachments" of type "[ComplaintAttachments]" used in
position expecting type "[ComplaintComplaintAttachmentsInput]"
so the array is of type ComplaintComplaintAttachmentsInput, I still don't know how to introspect it directly, but I'm already happy with the result :)

Related

Graphql Validation

My graphql schema looks like this:
type Query {
}
type Mutation {
addEmployee(
employeePayload: EmployeeRequest!
): EmployeeResponse
}
type EmployeeResponse {
transactionId: String!
status: String!
message: String!
}
type EmploymentHistory {
historyId: Int
}
type Address {
title: String
firstLine: String!
secondLine: String!
line3: String
county: String
country: String
postcode: String
}
type Employee {
employeeId: Int!
nationalityStatus: String!
issuingOfficeName: String!
surname: String
forenames: String
dateOfBirth: String
townOfBirth: String
countryOfBirth: String
gender: String
address: Address
}
type Details {
employmentHistory: [EmploymentHistory!]
employee: Employee!
}
type Meta {
messageId: String
action: String
}
input EmployeeRequest {
details: Details
meta: Meta
}
I have a json data , which looks like this:
{
"query": "mutation addEmployee($employeePayload:EmployeeRequest!){addEmployee(employeePayload: $employeePayload) { status transactionId message} }",
"variables": {
"employeePayload": {
"meta": {
"messageId": "4fc8ec8f-67ad-46d2-9fab-1234567",
"action": "Create"
},
"details": {
"employee": {
"employeeId": 123,
"nationalityStatus": "GBR",
"issuingOfficeName": "London",
"surname": "MARRIED",
"forenames": "LEON",
"address": {
"title": "MR",
"firstLine": "20 Maze street",
"secondLine": "Darlington",
"line3": "Darlington",
"county": "UNITED KINGDOM",
"country": "UNITED KINGDOM",
"postcode": "DW1H 9EX"
},
"dateOfBirth": "1980-05-18",
"townOfBirth": "SOME PLACE",
"countryOfBirth": "UNITED KINGDOM",
"gender": "M"
},
"employmentHistory": [
{
"historyId": 123
},
{
"historyId": 456
}
]
}
}
}
}
My code to validate the json against the schema is this:
try {
String query = "mutation addEmployee($employeePayload:EmployeeRequest!)
{addEmployee(employeePayload: $employeePayload) { status
transactionId message}}";
document = parser.parseDocument(query);
} catch (ParseCancellationException e) {
log.error("There seems to be an issue parsing the document" + e.getMessage());
}
Validator validator = new Validator();
List<ValidationError> validationErrors = validator.validateDocument(schema, document);
return validationErrors.isEmpty();
The query is not the full payload (that needs to be validated). The above validation always pass. Is there a way I can find the validation constraints like not null or wrong data type?

How to create joi schema to validate object of objects

I tried to make schema to validate json such this:
{
"integration": { "module": [ "m" ] },
"tile": {
"title": "TTT",
"text": "ttt",
"icon": "./resources/main-icon.png",
"tags": [ "bbb", "vvvv"],
"orderNumber": 20
},
"steps": {
"order": [
"1",
"2",
"3"
],
"data": {
"1": {
"title": "tt1",
"description": "",
"screens": { "default": "true" }
},
"2": {
"title": "tt2",
"description": "",
"screens": { "default": "true" }
},
"3": {
"title": "tt3",
"description": "",
"screens": { "default": "true" }
}
}
}
};
Schema:
Joi.object({
integration: Joi.object({
module: Joi.array().items(Joi.string().valid('m').required())
}).required(),
tile: Joi.object({
title: Joi.string().required(),
text: Joi.string().required(),
icon: Joi.string().required(),
tags: Joi.array().items(Joi.string()).required(),
orderNumber: Joi.number().integer().min(1).max(255).required()
}).required(),
steps: Joi.object({
order: Joi.array().items(Joi.string()).required(),
data: Joi.object().keys({
title: Joi.string().required(),
description: Joi.string().required(),
screens: Joi.object({
default: Joi.string().valid('true', 'false').required()
}).required()
}).unknown(),
}).required()
});
But it generate error:
Validation Error: "steps.data.title" is required. "steps.data.description" is required. "steps.data.screens" is required
Please help. How can I make this schema?
Your data key is an object with keys 1, 2, and 3, each one is also an object with keys title, description, and screens.
But in your validation, your data key is an object with keys title, description, and screens, which is not correct.
You should change your steps.data validation to this:
data: Joi.object().pattern(
Joi.string().valid("1", "2", "3"),
Joi.object().keys({
title: Joi.string().required(),
description: Joi.string().required().allow(''),
screens: Joi.object({ default: Joi.string().valid('true', 'false').required() }),
})).unknown(),
}).required()
I used Joi.object().pattern to avoid duplicating the code since your object value is the same for each key.
I also changed your data.description, since you were not allowing empty strings, I just added .allow('').

Apollo Federation schema, representations incomplete

I have this query:
getMyTransactions {
id
createdAt
}
And I got this error:
"message": "Cannot return null for non-nullable field Transaction.createdAt.",
"path": [
"_entities",
0,
"createdAt"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"serviceName": "XXX",
"query": "query($representations:[_Any!]!){_entities(representations:$representations){...on Transaction{createdAt}}}",
"variables": {
"representations": [
{
"__typename": "Transaction",
"id": "29bf75e5-b79e-4a7d-a021-84a8b5662aa8"
},
{
"__typename": "Transaction",
"id": "616f3f8a-3c81-4d2e-bce0-03d031a15062"
}
]
},
//etc
Why isvariables.representations missing createdAt values? When I do a query directly to DynamoDB I can see createdAt values for all those 2 items.
My schemas are like this:
extend type Transaction #key(fields: "id") {
id: ID! #external
}
type Query {
getMyTransactions: [Transaction!]!
}
And the other schema has Transaction type:
type Transaction #key(fields: "id") {
id: ID!
createdAt: String!
}

AppSync graphql query not returning expected data

I have an AppSync graphql schema with Users and Events, that should have a bi-directional connection. My schema looks like this:
type User #model {
id: ID!
email: String
events: [EventUser] #connection(name: "EventUser", keyName: "byUser", fields: ["id"])
}
type Event #model {
id: ID!
name: String
description: String
registeredUsers: [EventUser] #connection(name: "EventUser", keyName: "byEvent", fields: ["id"])
invitedUsers: [User]
}
type EventUser
#model(queries: null)
#key(name: "byEvent", fields: ["eventId", "userId"])
#key(name: "byUser", fields: ["userId", "eventId"]) {
id: ID!
eventId: ID!
userId: ID!
event: Event! #connection(fields: ["eventId"])
user: User! #connection(fields: ["userId"])
}
The query that is generated by the CLI looks like this:
export const listEvents = /* GraphQL */ `
query ListEvents(
$filter: ModelEventFilterInput
$limit: Int
$nextToken: String
) {
listEvents(filter: $filter, limit: $limit, nextToken: $nextToken) {
items {
id
name
description
registeredUsers {
nextToken
}
createdAt
updatedAt
}
nextToken
}
}
`;
When I run this query with:
const result = await API.graphql(graphqlOperation(queries.listEvents, { limit: 10}))
I get these results:
[
{
"createdAt": "2020-08-17T21:36:04.502Z",
"description": "Event description",
"id": "some-unique-id",
"name": "Event name",
"registeredUsers": Object {
"nextToken": null,
},
"updatedAt": "2020-08-17T21:36:04.502Z",
},
{
"createdAt": "2020-08-17T21:36:04.502Z",
"id": "some-other-unique-id",
"name": "event 2 name",
"registeredUsers": Object {
"nextToken": null, // <- This seems wrong.
},
"updatedAt": "2020-08-17T21:36:04.502Z"
}
]
The registeredUsers field should be an array of items. I can run a query in the AppSync console like this:
query ListEvents {
listEvents( limit: 10) {
items {
id
name
registeredUsers {
items {
userId
}
}
}
}
}
and it returns correct results:
{
"data": {
"listEvents": {
"items": [
{
"id": "some-unique-id",
"name": "event name",
"registeredUsers": {
"items": [
{
"userId": "user"
},
{
"userId": "user1"
}
]
}
},
{
"id": "some-other-unique-id",
"name": "event 2 name",
"registeredUsers": {
"items": []
}
},
]
}
}
}
Obviously, the console query is slightly different but it produces correct results, although it does not handle nextToken pagination.
My question is this: Is the CLI-generated query invalid? If so, is it because my Schema is not set up correctly?
If the generated query is valid, how would I call it to get the EventUser data?

How to populate only with content of getter

I have some problem in mongoose project.
I try to populate and use getter but not all data
But now all virtuals appear in document.
I'm using mongoose.Schema and mongoose.Model
Here is example of my test code
const GroupsSchema = schema({
title: String,
users: [{
type: schema.Types.ObjectId,
ref: 'Users'
}]
});
const UsersSchema = schema({
name: String,
avatar: String
}, {
toJSON: {
virtuals: true
}
});
class Users extends Model {
get name() {
return {
name: this.name
};
}
get avatar() {
return {
avatar: this.avatar
};
}
}
Populating document
const groups = await Groups.find({}).populate('users').exec();
My current result:
[
{
"_id": "5c9bb51626924f0a08aa8c3d",
"title": "GroupName"
"users": [
{
"_id": "5c8e37169fc1f9000f8c333b",
"name": "Jack",
"avatar": "avatar.jpg",
"name": {
"name": "Jack",
},
"avatar": {
"avatar": "avatar.jpg"
}
}
]
}
]
How can I populate document with content of only name getter?
Desired result:
[
{
"_id": "5c9bb51626924f0a08aa8c3d",
"title": "GroupName"
"users": [
{
"name": "Jack"
}
]
}
]

Resources