How to set global behavior of GraphQL with default values for object and array ?
Some examples
const AddressTC = schemaComposer.createObjectTC({
name: 'Address',
fields: {
street: {type: 'String',defaultValue: null},
number: {type: 'String', defaultValue: null},
postcode: {type: 'String', defaultValue: null},
city: {type: 'String', defaultValue: null},
comment: {type: 'String', defaultValue: null},
country: {type: 'String', defaultValue: null},
quality: {type: 'QualityEnum', defaultValue: 'BAD'},
}
});
const customerTC = schemaComposer.createObjectTC({
name: 'Customer',
fields: {
address: AddressTC
needs: {
type : ['objectID'],
defaultValue: []
},
documents: {
type: schemaComposer.getOTC('Document').List,
defaultValue: []
},
}
});
In my case I use noSQL database and when a field doesn't exist or is empty, the request GraphQL always returns null then crashed my app with wrongs data type.
INFO : I use graphql-compose
I found this post, whith the same problem, but this solution not useful for me with a large API and dynamics resolvers. Graphql, how to return empty array instead of null
Below is my api.yml:
openapi: 3.0.0
info:
title: 'AutoDelievery API'
description: 'AutoDelievery API Documentation'
termsOfService: 'https://www.yopmail.com'
contact:
name: yopmail
url: 'http://www.yopmail.com'
email: contactus#yopmail.com
license:
name: yopmail
url: 'http://www.yopmail.com/licenses/yopmail-AutoDelievery-Licence.html'
version: 1.0.0-oas3
servers:
-
url: 'http://localhost:5055/'
tags:
-
name: AutoDelievery
description: 'AutoDelievery product related APIs'
externalDocs:
description: 'Documentation:'
url: 'http://localhost:5055/external/doc'
paths:
/autoDelievery:
post:
tags: [autoDelievery]
summary: 'Creates a new autoDelievery record'
operationId: insertSubscription
requestBody: {description: 'autoDelievery request body', content: {application/json: {schema: {$ref: '#/components/schemas/AutoDelievery'}}}}
responses: {'200': {description: 'successful operation', content: {application/json: {schema: {$ref: '#/components/schemas/AutoDelievery'}}}}, '400': {description: 'Bad Request', content: {application/json: {schema: {$ref: '#/components/schemas/AutoDelieveryError'}}}}, '500': {description: 'Server Error', content: {application/json: {schema: {$ref: '#/components/schemas/AutoDelieveryError'}}}}}
'/autoDelievery/{autoDelieveryID}':
get:
tags: [AutoDelievery]
summary: 'AutoDelievery as per ID'
description: 'Fetches the AutoDelievery for the provided Id'
operationId: getAutoDelieveryById
parameters: [{in: path, name: autoDelieveryID, required: true, schema: {type: string}}]
responses: {'200': {description: 'successful operation', content: {application/json: {schema: {$ref: '#/components/schemas/AutoDelievery'}}}}, '400': {description: 'Server Error', content: {application/json: {schema: {$ref: '#/components/schemas/AutoDelieveryError'}}}}}
delete:
tags: [autoDelievery]
summary: 'Delete AutoDelievery as per ID'
description: 'Finds the autoDelievery for the provided Id and deletes the same'
operationId: deleteAutoDelieveryById
parameters: [{in: path, name: autoDelieveryID, required: true, schema: {type: string}}]
responses: {'200': {description: 'successful operation', content: {application/json: {schema: {$ref: '#/components/schemas/AutoDelievery'}}}}, '400': {description: 'Server Error', content: {application/json: {schema: {$ref: '#/components/schemas/AutoDelieveryError'}}}}}
'/autoDelievery/{autoDelieveryID}/payment':
put:
tags: [AutoDelievery]
summary: 'Updates autoDelievery payment details'
parameters: [{in: path, name: autoDelieveryID, description: 'AutoDelievery Id of which payment details are to be updated', required: true, schema: {type: string}}]
operationId: updatePayment
requestBody: {content: {application/json: {schema: {$ref: '#/components/schemas/PaymentMethod'}}}}
responses: {'200': {description: 'successful operation', content: {application/json: {schema: {$ref: '#/components/schemas/AutoDelievery'}}}}, '400': {description: 'Bad Request', content: {application/json: {schema: {$ref: '#/components/schemas/AutoDelieveryError'}}}}, '500': {description: 'Server Error', content: {application/json: {schema: {$ref: '#/components/schemas/AutoDelieveryError'}}}}}
'/autoDelievery/{autoDelieveryId}/frequency':
put:
tags: [AutoDelievery]
summary: 'Updates AutoDelievery frequency, quantity and end data.'
parameters: [{in: path, name: autoDelieveryId, description: 'AutoDelievery Id of which frequency is to be updated', required: true, schema: {type: string}}]
operationId: updateFrequency
requestBody: {content: {application/json: {schema: {$ref: '#/components/schemas/Frequency'}}}}
responses: {'200': {description: 'successful operation', content: {application/json: {schema: {$ref: '#/components/schemas/AutoDelievery'}}}}, '400': {description: 'Bad Request', content: {application/json: {schema: {$ref: '#/components/schemas/AutoDelieveryError'}}}}, '500': {description: 'Server Error', content: {application/json: {schema: {$ref: '#/components/schemas/AutoDelieveryError'}}}}}
components:
schemas:
SubscriptionList:
type: object
properties: {count: {type: integer}, result: {type: array, items: {$ref: '#/components/schemas/AutoDelievery'}}, page: {type: integer}}
DataEntity:
type: object
properties: {id: {type: string}, lastModifiedDate: {type: string}, createdDate: {type: string}}
AutoDelievery:
allOf: [{$ref: '#/components/schemas/DataEntity'}, {type: object, required: [userId, clientId, quantity, frequency, payment, product, shippingAddress], properties: {userId: {type: string}, clientId: {type: string}, parentSubscriptionId: {type: string}, product: {$ref: '#/components/schemas/Product'}, quantity: {type: integer, minimum: 1}, frequency: {type: number, minimum: 1}, payment: {$ref: '#/components/schemas/PaymentMethod'}, paymentMethodId: {type: string}, shippingMethodId: {type: string}, nextOrderDate: {type: string}, subscriptionStartDate: {type: string}, subscriptionEndDate: {type: string}, shippingMethod: {type: string}, shippingAddress: {$ref: '#/components/schemas/Address'}}}]
Product:
type: object
required: [productId]
properties: {productId: {type: string}, productDescription: {type: string}}
PaymentMethod:
type: object
required: [billingAddress]
properties: {createdDate: {type: string}, modifiedDate: {type: string}, paymentType: {type: string}, billingAddress: {$ref: '#/components/schemas/Address'}}
discriminator: {propertyName: paymentType}
Frequency:
type: object
required: [frequency, quantity]
properties: {frequency: {type: number, minimum: 1}, subscriptionEndDate: {type: string}, quantity: {type: integer, minimum: 1}}
Card:
allOf: [{$ref: '#/components/schemas/PaymentMethod'}, {type: object, required: [nameOnCard, lastFourDigits, expiryMonth, expiryYear], properties: {nameOnCard: {type: string}, lastFourDigits: {type: string}, token: {type: string}, expiryMonth: {type: string, minLength: 2, maxLength: 2}, expiryYear: {type: string, format: year}}, discriminator: {propertyName: paymentType}}]
PayPal:
allOf: [{$ref: '#/components/schemas/PaymentMethod'}, {type: object, required: [accountId], properties: {accountId: {type: string}}}]
Payment:
type: object
properties: {payment: {allOf: [{$ref: '#/components/schemas/PaymentMethod'}], discriminator: {propertyName: paymentType}}}
Address:
type: object
required: [firstName, lastName, addressLine1, city, state, postalCode, country]
properties: {prefix: {type: string}, firstName: {type: string}, middleName: {type: string}, lastName: {type: string}, suffix: {type: string}, title: {type: string}, companyName: {type: string}, addressLine1: {type: string}, addressLine2: {type: string}, city: {type: string}, state: {type: string}, postalCode: {type: string}, country: {type: string}, phoneNumber: {type: string}, mobilePhoneNumber: {type: string}}
Status:
type: object
required: [status]
properties: {status: {type: string}, details: {type: string}}
Shipping:
type: object
properties: {shippingMethodId: {type: string}, shippingMethod: {type: string}}
PaymentAuthorization:
type: object
properties: {authId: {type: string}}
CommerceItem:
type: object
required: [subscriptionId, quantity, product]
properties: {subscriptionId: {type: string}, product: {$ref: '#/components/schemas/Product'}, quantity: {type: number, minimum: 1}, itemUnitPrice: {type: number}, itemTotalPrice: {type: number}, itemDiscount: {type: number}}
ErrorResponse:
type: object
required: [errorMessage]
properties: {field: {type: string}, errorMessage: {type: string}}
ErrorResponseList:
type: object
required: [errors]
properties: {errors: {type: array, items: {$ref: '#/components/schemas/ErrorResponse'}}}
NoSubscriptionResponse:
allOf: [{$ref: '#/components/schemas/ErrorResponseList'}]
AutoDelieveryError:
allOf: [{$ref: '#/components/schemas/ErrorResponseList'}]
NoOrderResponse:
allOf: [{$ref: '#/components/schemas/ErrorResponseList'}]
my sample api is:
"id": "string",
"lastModifiedDate": "string",
"createdDate": "string",
"userId": null,
"clientId": "string",
"parentSubscriptionId": "string",
"product": {
"productId": "string",
"productDescription": "string"
},
"quantity": 0,
"frequency": 0,
"payment": {
"createdDate": "string",
"modifiedDate": "string",
"paymentType": "string",
"billingAddress": {
"prefix": "string",
"firstName": "string",
"middleName": "string",
"lastName": "string",
"suffix": "string",
"title": "string",
"companyName": "string",
"addressLine1": "string",
"addressLine2": "string",
"city": "string",
"state": "string",
"postalCode": "string",
"country": "string",
"phoneNumber": "string",
"mobilePhoneNumber": "string"
}
},
"paymentMethodId": "string",
"shippingMethodId": "string",
"nextOrderDate": "string",
"subscriptionStartDate": "string",
"subscriptionEndDate": "string",
`enter code here`"shippingMethod": "string",
"shippingAddress": {
"prefix": "string",
"firstName": "string",
"middleName": "string",
"lastName": "string",
"suffix": "string",
"title": "string",
"companyName": "string",
"addressLine1": "string",
"addressLine2": "string",
"city": "string",
"state": "string",
"postalCode": "string",
"country": "string",
"phoneNumber": "string",
"mobilePhoneNumber": "string"
}
}
I get an error response in the following format:
{
"errors": [
{
"field": "userId",
"errorMessage": "must not be null"
}
]
}
What should I do to have a customized error message?
This can be easily solved by adding a ControllerAdvice class. Figure out the exception being thrown from the stacktrace and write a handler for the same. In the handler you will be able to throw custom error messages, when ever this kind of exception occurs.
Here is some reference for ExceptionHandler
Swagger puts #NotNull annotation on required fields while creating POJO.
Message interpolation can be done by creating ValidationMessages.properties file in the classpath as follows:
javax.validation.constraints.NotNull.message=CUSTOMIZED ERROR MESSAGE
It will override the default error message which comes from
/org/hibernate/validator/ValidationMessages.properties
You can implement spring's abstract class OncePerRequestFilter and override
doFilterInternal method .
Inside this method add validation using httpservletRerquest and return the custom message using ResponseStatusException .
ref- https://www.baeldung.com/spring-onceperrequestfilter
I am trying to do geo point filter in mongodb in meanjs. i have used mongoosastic modules but i am not able to perform geo point filter.
here below are the mongoose schema and controller code for filter.
Mongoose schema
'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
mongoosastic = require('mongoosastic');
var BusinessSchema = new Schema({
name: {type: String, unique: 'Name already exists', trim: true, required: 'Name is required.', es_indexed: true},
searchTags: {type: [String], es_indexed: true},
alias: {type: Array, es_indexed: true},
// geoLocation: { type: [Number], /*/ [<longitude>, <latitude>]*/ index: '2d', /*/ create the geospatial index,*/ required: 'GeoLocation is required.', es_indexed:true,es_type:'geo_point'},
geo_cords: {
type: Array
},
address: {
address1: {type: String, required: 'Address is required', trim: true},
address2: String,
city: {type: String, required: 'City is required', trim: true},
// state: {type: String, required: 'State is required', trim: true},
country: {type: String, required: 'Country is required', trim: true},
postalCode: {type: String, required: 'Postal code is required', trim: true},
neighbourhood: String
},
isActive: {
type: Boolean,
default: true,
es_indexed: true
},
dateUpdated: {
type: Date
, es_indexed: true
},
dateCreated: {
type: Date,
default: Date.now
, es_indexed: true
}
});
controller code for filter and query
var mongoose = require('mongoose'),
Business = mongoose.model('Businesses');
var query = {
"query_string": {
"multi_match": {
"query": categoryIds.join(' OR '),
"fields": ["categoryIds", "relatedCategoryIds"]
}
},
"filter": {
"bool": {
"should": [
{"term": {"address.postalCode": "110016"}},
{"geo_distance": {
"distance": "50km",
"geo_cords": [-122.3050, 37.9174]
}
}
],
}
}
}
Business.search(query, function (err, results) {
// sendResponse(req, res, err, results)
if (!err) {
res.json(results);
} else {
res.status(400).send({message: 'Business Not Found'})
}
});
while doing this i am getting a long error saying
QueryParsingException[[businessess] failed to find geo_point field [geo_cords]
According to the documentation of mongoosastic
Geo mapping
Prior to index any geo mapped data (or calling the synchronize), the mapping must be manualy created with the createMapping (see above).
First, in your schema, define 'geo_cords' this way:
geo_cords: : {
geo_point: {
type: String,
es_type: 'geo_point',
es_lat_lon: true
},
lat: { type: Number },
lon: { type: Number }
}
Add an es_type: 'object' to each Array or embbeded type
alias: {type: Array, es_indexed: true, es_type: 'object'}
Then call .createMapping() on the model just after you've created it.
The load of JsonDaten not work. It just shows the loading Notice
Model.js:
ps.models.Event = Ext.regModel("ps.models.Event", {
fields: [
{name: "id", type: "int"},
{name: "date", type: "string"},
{name: "kat", type: "string"},
{name: "name", type: "string"},
{name: "location", type: "string"},
]
});
ps.stores.event = new Ext.data.Store({
model: "ps.models.Event",
proxy: {
type: 'scripttag',
url: 'http://www.asfdasdf.com/eventkalender/eventlist/format/json',
reader: {
type: 'json'
}
},
autoLoad: true
});
View.js:
items: [{
xtype: 'list',
emptyText: 'Keine Events verfügbar',
itemTpl: '{name}',
//grouped: true,
scroll: 'vertical',
fullscreen: true,
store: ps.stores.event
}
Whats wrong?
Thank you for your support!!
Probably this is because of incorrect json format (you could check for errors by pressing CTRL+Shift+J in Chrome)
Try to follow this tutorial http://www.sencha.com/learn/legacy/Tutorial:Creating_JSON_Data_in_PHP
UPDATE
You need to add root:'events' to you proxy's reader:
ps.stores.event = new Ext.data.Store({
model: "ps.models.Event",
proxy: {
type: 'scripttag',
url: 'http://www.asfdasdf.com/eventkalender/eventlist/format/json',
reader: {
type: 'json',
root: 'events'
}
},
autoLoad: true
});
You may also want to account for any errors and timeouts with the below:
ps.stores.event = new Ext.data.Store({
model: "ps.models.Event",
proxy: {
type: 'scripttag',
url: 'http://www.asfdasdf.com/eventkalender/eventlist/format/json',
reader: {
type: 'json',
root: 'events'
},
timeout: 3000, //milliseconds
listeners: {
exception:function(proxy, response){
console.error(response.responseText);
}
}
},
autoLoad: true
});