Fastify schema validation multipart/form-data (body should be object) - node.js

Multipart form data file uplaod time comming error body should be object, and i am using ajv plugin also, still i am using same issue. below is my reference code.
app.js
const fastify = require('fastify')({
logger: true
});
const Ajv = require('ajv');
const ajv = new Ajv({
useDefaults: true,
coerceTypes: true,
$data: true,
extendRefs: true
});
ajv.addKeyword("isFileType", {
compile: (schema, parent, it) => {
parent.type = "file";
delete parent.isFileType;
return () => true;
},
});
fastify.setSchemaCompiler((schema) => ajv.compile(schema));
routes.js
schema: {
tags: [{
name: 'Category'
}],
description: 'Post category data',
consumes: ['multipart/form-data'],
body: {
type: 'object',
isFileType: true,
properties: {
name: {
type: 'string'
},
thumb_url: {
isFileType: true,
type: 'object'
},
img_url: {
isFileType: true,
type: 'object'
},
status: {
type: 'number',
enum: [0, 1],
default: 1
}
},
required: ['name', 'thumb_url', 'img_url']
},
response: {
201: {
type: 'object',
properties: categoryProperties
}
}
}
response
{
"statusCode": 400,
"error": "Bad Request",
"message": "body should be object"
}
I suspect the error is in the way I send the data, but I am having trouble figuring it out. I have read about this error and it seems to be generated when an object is passed to formData, but I am sending a string so I don't understand why it happens. thanks in advance!

I think your configuration to manage multipart is wrong and the schema should be fixed as this working example:
const fastify = require('fastify')({ logger: true })
fastify.register(require('fastify-multipart'), {
addToBody: true
})
const Ajv = require('ajv')
const ajv = new Ajv({
useDefaults: true,
coerceTypes: true,
$data: true,
extendRefs: true
})
ajv.addKeyword('isFileType', {
compile: (schema, parent, it) => {
parent.type = 'file'
delete parent.isFileType
return () => true
}
})
fastify.setSchemaCompiler((schema) => ajv.compile(schema))
fastify.post('/', {
schema: {
tags: [{
name: 'Category'
}],
description: 'Post category data',
consumes: ['multipart/form-data'],
body: {
type: 'object',
properties: {
name: { type: 'string' },
thumb_url: { isFileType: true },
img_url: { isFileType: true },
status: {
type: 'number',
enum: [0, 1],
default: 1
}
},
required: ['name', 'thumb_url', 'img_url']
}
}
}, async (req, reply) => {
let filepath = path.join(__dirname, `${req.body.thumb_url[0].filename}-${Date.now()}`)
await fs.writeFile(filepath, (req.body.thumb_url[0].data))
filepath = path.join(__dirname, `${req.body.img_url[0].filename}-${Date.now()}`)
await fs.writeFile(filepath, (req.body.img_url[0].data))
return req.body
})
fastify.listen(3000)
Call it with this request:
curl -X POST \
http://127.0.0.1:3000/ \
-H 'content-type: multipart/form-data' \
-F 'thumb_url=#/home/wks/example-file' \
-F 'img_url=#/home/wks/example-file' \
-F 'name=fooo'
You will get:
{
"thumb_url":[
{
"data":{
"type":"Buffer",
"data":[
97,
115,
100,
10
]
},
"filename":"example-file",
"encoding":"7bit",
"mimetype":"application/octet-stream",
"limit":false
}
],
"img_url":[
{
"data":{
"type":"Buffer",
"data":[
97,
115,
100,
10
]
},
"filename":"example-file",
"encoding":"7bit",
"mimetype":"application/octet-stream",
"limit":false
}
],
"name":"foo",
"status":1
}

Related

Recaptcha v3 Invalid Input Response

I'm having a problem when trying to apply recaptcha into my web app.
Basically, it's returning the error message: "invalid-input-response"
What could I be doing wrong?
Stack:
#nuxtjs/recaptcha 1.1.1
express-recaptcha 5.1.0
nuxt 2.15.8
node 16.15.9
Here is my configuration on front, I don't have certain about the recaptcha mode, if I should use base or enterprise.
nuxt.config.js
modules: [
'#nuxtjs/recaptcha',
],
recaptcha: {
hideBadge: true,
mode: 'base',
siteKey: 'MY_SITE_KEY',
version: 3,
size: 'normal',
},
On index I don't know if has any problem here
Based on docs, it's only do that
On my action I'm sending the token alongside my data on that format
{
token: 'asdadsadadasdas',
review: {...}
}
index.vue
async mounted() {
try {
await this.$recaptcha.init()
} catch (e) {
console.log(e);
}
},
methods: {
...mapActions({
getProduct: "getProduct",
postReview: "postReview",
}),
async submit() {
const postReviewToken = await this.$recaptcha.execute('postReview')
try {
await this.postReview({
token: postReviewToken,
productId: this.$route.params.productId,
review: {
title: this.review.title,
content: this.review.content,
},
});
this.getProduct({ productId: this.$route.params.productId });
this.review = {
title: "",
content: "",
};
} catch (error) {}
},
},
Node
And on my api I just added the middleware as docs require
import { RecaptchaV3 } from 'express-recaptcha/dist'
const recaptcha = new RecaptchaV3(
'MY_SITE_KEY',
'MY_SECRET_KEY'
)
routes.post('/product/:id/review', recaptcha.middleware.verify, async (request: Request, response: Response) => {
if (request.recaptcha?.error) {
return response.status(400).json({ message: request.recaptcha.error })
}
const review = await prisma.review.create({
data: {
productId: request.params.id,
title: request.body.review.title,
content: request.body.review.content,
},
select: {
id: true,
title: true,
content: true,
}
})
response.json({review});
});

Serverless Event object failed validation at createError

I am working on a serverless/nodejs12 project. The project deploys fine, but when I try to invoke the endpoint with postman I get the following error. I have browsed through many postings of similar errors but still failed to understand what's going on. Will appreciate any pointers.
Thank you
at createError (/var/task/src/handlers/webpack:/home/serverless-workspace/notification/node_modules/#middy/util/index.js:259:1)
at validatorMiddlewareBefore (/var/task/src/handlers/webpack:/home/serverless-workspace/notification/node_modules/#middy/validator/index.js:55:1)
at runMiddlewares (/var/task/src/handlers/webpack:/home/serverless-workspace/notification/node_modules/#middy/core/index.js:120:1)
at runRequest (/var/task/src/handlers/webpack:/home/serverless-workspace/notification/node_modules/#middy/core/index.js:80:1) {
details: [
{
instancePath: '/body',
schemaPath: '#/properties/body/type',
keyword: 'type',
params: [Object],
message: 'must be object'
}
]
createNotification.js
--------------------------
async function createNotification(event, context) {
const { title } = event.body;
const { destination } = event.body;
const { destinationType } = event.body
const notification = {
id: uuid(),
title,
destination,
destinationType,
status: 'OPEN',
createdAt: new Date().toISOString(),
}
try {
await dynamodb.put({
TableName: process.env.NOTIFY_TABLE_NAME,
Item: notification
}).promise();
} catch (error) {
console.error(error);
throw new createError.InternalServerError(error);
}
return {
statusCode: 200,
body: JSON.stringify(notification),
};
}
export const handler = commonMiddleware(createNotification)
.use(validator({ inputSchema: createNotificationSchema }));
and the schema
----------------------
const schema = {
type: 'object',
properties: {
body: {
type: 'object',
required: ['status'],
default: { status: 'OPEN' },
properties: {
status: {
default: 'OPEN',
enum: ['OPEN', 'CLOSED'],
},
},
},
},
required: ['body'],
};
export default schema;
You're getting the error must be object. I'm guessing your input looks like { event: { body: 'JSON string' } }. You'll need to use another middy middleware to parse the body prior to validating the input. Which middleware will depend on what AWS event it's expecting. Middy >3.0.0 supports all AWS events.

How I can solve $near query in mongoddb adbpter query building issue?

I'm trying to create a nearby searching platform with keystone.js
But the nearby queries not working with the keystone js.
How I can run mongoose $nearSphere query or aggregation queries in the keystone?
Hereby added my best try and errors.
Index.js
require('dotenv').config({
path: './.env'
});
const {
Keystone
} = require('#keystonejs/keystone');
const {
GraphQLApp
} = require('#keystonejs/app-graphql');
const {
AdminUIApp
} = require('#keystonejs/app-admin-ui');
const {
PasswordAuthStrategy
} = require('#keystonejs/auth-password');
const {
MongooseAdapter
} = require('#keystonejs/adapter-mongoose');
const initialiseData = require('./initial-data');
const {
Post,
User,
Category,
SubCategory,
Comment,
ForgottenPasswordToken,
} = require('./schema');
const PROJECT_NAME = 'rentospot';
const keystone = new Keystone({
appVersion: {
version: '1.0.0',
addVersionToHttpHeaders: false,
access: false,
},
cookie: {
secure: process.env.NODE_ENV === 'production', // Default to true in production
maxAge: 1000 * 60 * 60 * 24 * 30, // 30 days
sameSite: false,
},
cookieSecret: process.env.COOKIE_SECRET,
adapter: new MongooseAdapter({
mongoUri: 'mongodb+srv://*******:******.mongodb.net/my-app-key-stone'
}),
onConnect: process.env.CREATE_TABLES !== 'true' && initialiseData,
});
const postList = keystone.createList('Post', Post);
keystone.createList('Category', Category);
keystone.createList('SubCategory', SubCategory);
keystone.createList('Comment', Comment);
keystone.createList('User', User);
keystone.createList('ForgottenPasswordToken', ForgottenPasswordToken);
const authStrategy = keystone.createAuthStrategy({
type: PasswordAuthStrategy,
list: 'User',
});
const extendSchema = {
queries: [{
schema: 'nearBy: [Post]',
resolver: async(_) => {
try {
const {
adapter
} = postList;
const result = await adapter.find({
// "feature": true,
"venue.location": {
"$nearSphere": {
"$geometry": {
"type": "Point",
"coordinates": [12, 75],
},
"$minDistance": 0,
"$maxDistance": 500,
},
}
});
console.log(result)
return result;
} catch (e) {
console.log("errrrrrrrrrrrrrror: ", e)
return null
}
},
}, ]
};
keystone.extendGraphQLSchema(extendSchema)
module.exports = {
keystone,
apps: [new GraphQLApp(),
new AdminUIApp({
name: PROJECT_NAME,
enableDefaultRoute: true,
authStrategy
})
],
};
Schema.js
const fetch = require('node-fetch');
const { v4: uuid } = require('uuid');
const { sendEmail } = require('./emails');
const {
File,
Text,
Slug,
Relationship,
Select,
Password,
Checkbox,
CalendarDay,
DateTime,
Integer
} = require('#keystonejs/fields');
const { S3Adapter } = require('#keystonejs/file-adapters');
const { AuthedRelationship } = require('#keystonejs/fields-authed-relationship');
const { formatISO } = require('date-fns');
const { Wysiwyg } = require('#keystonejs/fields-wysiwyg-tinymce');
const LocationGoogle = require('./LocationGoogle');
const fileAdapter = new S3Adapter({
bucket: process.env.BUCKET_NAME,
folder: process.env.S3_PATH,
publicUrl: ({ filename }) =>
// `https://${process.env.CF_DISTRIBUTION_ID}.cloudfront.net/${process.env.S3_PATH}/${filename}`,
`https://${process.env.CF_DISTRIBUTION_ID}/${process.env.S3_PATH}/${filename}`,
s3Options: {
// Optional paramaters to be supplied directly to AWS.S3 constructor
apiVersion: '2006-03-01',
accessKeyId: process.env.API_KEY_ID,
secretAccessKey: process.env.SECRET_ACCESS_KEY,
region: process.env.REGION
},
uploadParams: ({ id }) => ({
Metadata: {
keystone_id: `${id}`,
},
}),
});
// Access control functions
const userIsAdmin = ({ authentication: { item: user } }) => Boolean(user && user.isAdmin);
const userOwnsItem = ({ authentication: { item: user } }) => {
if (!user) {
return false;
}
return { id: user.id };
};
const userIsCurrentAuth = ({ authentication: { item: user } }) => Boolean(user); // item will be undefined for anonymous user
const userIsAdminOrOwner = auth => {
const isAdmin = access.userIsAdmin(auth);
const isOwner = access.userOwnsItem(auth);
return isAdmin ? isAdmin : isOwner;
};
const access = { userIsAdmin, userIsCurrentAuth, userOwnsItem, userIsAdminOrOwner };
// Read: public / Write: admin
const DEFAULT_LIST_ACCESS = {
create: access.userIsCurrentAuth,
read: true,
update: access.userIsAdminOrOwner,
delete: access.userIsAdmin,
};
exports.User = {
access: {
update: access.userIsCurrentAuth,
delete: access.userIsAdmin,
},
fields: {
name: { type: Text },
dob: {
type: CalendarDay,
format: 'do MMMM yyyy',
dateFrom: '1901-01-01',
dateTo: formatISO(new Date(), { representation: 'date' }),
},
phone: {
type: Text,
isUnique: true,
access: { read: access.userIsCurrentAuth },
// hooks: {
// validateInput:
// }
},
email: { type: Text, isUnique: true, access: { read: access.userIsCurrentAuth } },
password: { type: Password, isRequired: true },
isAdmin: { type: Checkbox, access: { update: access.userIsAdmin } },
twitterHandle: { type: Text },
image: { type: File, adapter: fileAdapter },
},
hooks: {
afterChange: async ({ updatedItem, existingItem }) => {
if (existingItem && updatedItem.password !== existingItem.password) {
const url = process.env.SERVER_URL || 'http://localhost:3000';
const props = {
recipientEmail: updatedItem.email,
signinUrl: `${url}/signin`,
};
const options = {
subject: 'Your password has been updated',
to: updatedItem,
from: process.env.MAILGUN_FROM,
domain: process.env.MAILGUN_DOMAIN,
apiKey: process.env.MAILGUN_API_KEY,
};
await sendEmail('password-updated.jsx', props, options);
}
},
},
};
// exports.Organiser = {
// access: DEFAULT_LIST_ACCESS,
// fields: {
// user: { type: Relationship, ref: 'User' },
// order: { type: Integer },
// role: { type: Text },
// },
// };
// TODO: We can't access the existing item at the list update level yet,
// read access needs to check if event is "active" or if the user is admin
// read: ({ existingItem, authentication }) => access.userIsAdmin({ authentication }) || !!(existingItem && existingItem.status === 'active'),
exports.Post = {
access: DEFAULT_LIST_ACCESS,
fields: {
title: { type: Text },
slug: { type: Slug, from: 'title' },
author: {
type: AuthedRelationship,
ref: 'User',
isRequired: true,
access: {
read: userIsCurrentAuth,
update: userIsAdmin,
},
},
status: {
type: Select,
defaultValue: 'published',
options: [
{ label: 'Draft', value: 'draft' },
{ label: 'Published', value: 'published' },
],
},
feature: { type: Checkbox, access: { update: access.userIsAdmin } },
assured: { type: Checkbox, access: { update: access.userIsAdmin } },
posted: { type: DateTime, format: 'dd/MM/yyyy' },
categories: {
type: Relationship,
ref: 'Category',
many: false,
},
subCategories: {
type: Relationship,
ref: 'SubCategory',
many: true,
},
description: { type: Wysiwyg },
venue: {
type: LocationGoogle,
googleMapsKey: process.env.GOOGLE_MAPS_KEY,
// hooks: {
// resolveInput: async ({ originalInput }) => {
// try {
// const placeId = originalInput.venue;
// if (typeof placeId === 'undefined') {
// // Nothing was passed in, so we can bail early.
// return undefined;
// }
// const r = await fetch(
// `https://maps.googleapis.com/maps/api/geocode/json?place_id=${placeId}&key=${process.env.GOOGLE_MAPS_KEY}`
// )
// const response = await r.json()
// if (response.results && response.results[0]) {
// const { place_id, formatted_address } = response.results[0];
// const { lat, lng } = response.results[0].geometry.location;
// return {
// googlePlaceID: place_id,
// formattedAddress: formatted_address,
// lat,
// lng,
// loc: {
// type: "Point",
// coordinates: [lng, lat]
// }
// };
// }
// return null;
// }
// catch (e) {
// console.log("error:", e)
// }
// },
// validateInput: async (x) => {
// console.log("---------------------------------item 2-------------------------------------- : ",x);
// },
// beforeChange: async (x) => {
// console.log("========", x)
// },
// afterChange: async (x) => {
// console.log("++++", x)
// }
// },
},
locationAddress: { type: Text },
locationDescription: { type: Text },
createdby: {
type: AuthedRelationship,
ref: 'User',
isRequired: true,
access: {
read: userIsCurrentAuth,
update: userIsAdmin,
},
},
image: {
type: File,
adapter: fileAdapter,
hooks: {
beforeChange: async ({ existingItem }) => {
if (existingItem && existingItem.image) {
await fileAdapter.delete(existingItem.image);
}
},
},
},
},
hooks: {
afterDelete: ({ existingItem }) => {
if (existingItem.image) {
fileAdapter.delete(existingItem.image);
}
},
},
adminConfig: {
defaultPageSize: 20,
defaultColumns: 'title, status',
defaultSort: 'title',
},
labelResolver: item => item.title,
};
exports.Category = {
// access: userIsAdmin,
fields: {
name: { type: Text },
slug: { type: Slug, from: 'name' },
image: {
type: File,
adapter: fileAdapter,
hooks: {
beforeChange: async ({ existingItem }) => {
if (existingItem && existingItem.image) {
await fileAdapter.delete(existingItem.image);
}
},
},
},
},
};
exports.SubCategory = {
// access: userIsAdmin,
fields: {
name: { type: Text },
slug: { type: Slug, from: 'name' },
categories: {
type: Relationship,
ref: 'Category',
many: false,
},
image: {
type: File,
adapter: fileAdapter,
hooks: {
beforeChange: async ({ existingItem }) => {
if (existingItem && existingItem.image) {
await fileAdapter.delete(existingItem.image);
}
},
},
},
},
};
exports.Comment = {
access: {
create: userIsCurrentAuth,
update: userIsAdminOrOwner,
},
fields: {
body: { type: Text, isMultiline: true },
originalPost: {
type: Relationship,
ref: 'Post',
},
author: {
type: AuthedRelationship,
ref: 'User',
isRequired: true,
access: {
create: userIsAdmin,
update: userIsAdmin,
},
},
posted: { type: CalendarDay },
},
labelResolver: item => item.body,
};
// exports.Talk = {
// access: DEFAULT_LIST_ACCESS,
// fields: {
// name: { type: Text },
// event: { type: Relationship, ref: 'Event.talks' },
// speakers: { type: Relationship, ref: 'User.talks', many: true },
// isLightningTalk: { type: Checkbox },
// description: { type: Wysiwyg },
// },
// };
// exports.Rsvp = {
// access: {
// create: true,
// read: true,
// update: ({ authentication: { item } }) => {
// if (!item) {
// return false;
// }
// return { user: { id: item.id } };
// },
// delete: access.userIsAdmin,
// },
// fields: {
// event: { type: Relationship, ref: 'Event' },
// user: { type: Relationship, ref: 'User' },
// status: { type: Select, options: 'yes, no' },
// },
// hooks: {
// validateInput: async ({ context, resolvedData, existingItem }) => {
// const { status } = resolvedData;
// const { event: eventId } = existingItem ? existingItem : resolvedData;
// if (status === 'no') {
// return;
// }
// const { data } = await context.executeGraphQL({
// query: `query {
// event: Event(where: { id: "${eventId}" }) {
// id
// startTime
// maxRsvps
// isRsvpAvailable
// }
// allRsvps(where: { event: { id: "${eventId}" }}) {
// id
// }
// }`,
// });
// const { event, allRsvps } = data;
// if (
// !event ||
// !event.isRsvpAvailable ||
// !event.startTime ||
// new Date() > new Date(event.startTime) ||
// allRsvps.length >= event.maxRsvps
// ) {
// throw 'Error rsvping to event';
// }
// },
// },
// };
// exports.Sponsor = {
// access: DEFAULT_LIST_ACCESS,
// fields: {
// name: { type: Text },
// website: { type: Text },
// logo: { type: CloudinaryImage, adapter: cloudinaryAdapter },
// },
// };
exports.ForgottenPasswordToken = {
access: {
create: true,
read: true,
update: access.userIsAdmin,
delete: access.userIsAdmin,
},
fields: {
user: {
type: Relationship,
ref: 'User',
access: {
read: access.userIsAdmin,
},
},
token: {
type: Text,
isRequired: true,
isUnique: true,
access: {
read: access.userIsAdmin,
},
},
requestedAt: { type: DateTime, isRequired: true },
accessedAt: { type: DateTime },
expiresAt: { type: DateTime, isRequired: true },
},
hooks: {
afterChange: async ({ context, updatedItem, existingItem }) => {
if (existingItem) return null;
const now = new Date().toISOString();
const { errors, data } = await context.executeGraphQL({
context: context.createContext({ skipAccessControl: true }),
query: `
query GetUserAndToken($user: ID!, $now: DateTime!) {
User( where: { id: $user }) {
id
email
}
allForgottenPasswordTokens( where: { user: { id: $user }, expiresAt_gte: $now }) {
token
expiresAt
}
}
`,
variables: { user: updatedItem.user.toString(), now },
});
if (errors) {
console.error(errors, `Unable to construct password updated email.`);
return;
}
const { allForgottenPasswordTokens, User } = data;
const forgotPasswordKey = allForgottenPasswordTokens[0].token;
const url = process.env.SERVER_URL || 'http://localhost:3000';
const props = {
forgotPasswordUrl: `${url}/change-password?key=${forgotPasswordKey}`,
recipientEmail: User.email,
};
const options = {
subject: 'Request for password reset',
to: User.email,
from: process.env.MAILGUN_FROM,
domain: process.env.MAILGUN_DOMAIN,
apiKey: process.env.MAILGUN_API_KEY,
};
await sendEmail('forgot-password.jsx', props, options);
},
},
};
exports.customSchema = {
mutations: [
{
schema: 'startPasswordRecovery(email: String!): ForgottenPasswordToken',
resolver: async (obj, { email }, context) => {
const token = uuid();
const tokenExpiration =
parseInt(process.env.RESET_PASSWORD_TOKEN_EXPIRY) || 1000 * 60 * 60 * 24;
const now = Date.now();
const requestedAt = new Date(now).toISOString();
const expiresAt = new Date(now + tokenExpiration).toISOString();
const { errors: userErrors, data: userData } = await context.executeGraphQL({
context: context.createContext({ skipAccessControl: true }),
query: `
query findUserByEmail($email: String!) {
allUsers(where: { email: $email }) {
id
email
}
}
`,
variables: { email: email },
});
if (userErrors || !userData.allUsers || !userData.allUsers.length) {
console.error(
userErrors,
`Unable to find user when trying to create forgotten password token.`
);
return;
}
const userId = userData.allUsers[0].id;
const result = {
userId,
token,
requestedAt,
expiresAt,
};
const { errors } = await context.executeGraphQL({
context: context.createContext({ skipAccessControl: true }),
query: `
mutation createForgottenPasswordToken(
$userId: ID!,
$token: String,
$requestedAt: DateTime,
$expiresAt: DateTime,
) {
createForgottenPasswordToken(data: {
user: { connect: { id: $userId }},
token: $token,
requestedAt: $requestedAt,
expiresAt: $expiresAt,
}) {
id
token
user {
id
}
requestedAt
expiresAt
}
}
`,
variables: result,
});
if (errors) {
console.error(errors, `Unable to create forgotten password token.`);
return;
}
return true;
},
},
{
schema: 'changePasswordWithToken(token: String!, password: String!): User',
resolver: async (obj, { token, password }, context) => {
const now = Date.now();
const { errors, data } = await context.executeGraphQL({
context: context.createContext({ skipAccessControl: true }),
query: `
query findUserFromToken($token: String!, $now: DateTime!) {
passwordTokens: allForgottenPasswordTokens(where: { token: $token, expiresAt_gte: $now }) {
id
token
user {
id
}
}
}`,
variables: { token, now },
});
if (errors || !data.passwordTokens || !data.passwordTokens.length) {
console.error(errors, `Unable to find token`);
throw errors.message;
}
const user = data.passwordTokens[0].user.id;
const tokenId = data.passwordTokens[0].id;
const { errors: passwordError } = await context.executeGraphQL({
context: context.createContext({ skipAccessControl: true }),
query: `mutation UpdateUserPassword($user: ID!, $password: String!) {
updateUser(id: $user, data: { password: $password }) {
id
}
}`,
variables: { user, password },
});
if (passwordError) {
console.error(passwordError, `Unable to change password`);
throw passwordError.message;
}
await context.executeGraphQL({
context: context.createContext({ skipAccessControl: true }),
query: `mutation DeletePasswordToken($tokenId: ID!) {
deleteForgottenPasswordToken(id: $tokenId) {
id
}
}
`,
variables: { tokenId },
});
return true;
},
},
],
};
LocationGoogle is a directory of the files exactly #keystonejs/fields-location-google and small changes in its Implementation.js
Implementation.js
const { Implementation } = require('#keystonejs/fields');
const { MongooseFieldAdapter } = require('#keystonejs/adapter-mongoose');
const fetch = require('node-fetch');
// Disabling the getter of mongoose >= 5.1.0
// https://github.com/Automattic/mongoose/blob/master/migrating_to_5.md#checking-if-a-path-is-populated
class LocationGoogleImplementation extends Implementation {
constructor(_, { googleMapsKey }) {
super(...arguments);
this.graphQLOutputType = 'LocationGoogle';
if (!googleMapsKey) {
throw new Error(
'You must provide a `googleMapsKey` to LocationGoogle Field. To generate a Google Maps API please visit: https://developers.google.com/maps/documentation/javascript/get-api-key'
);
}
this._googleMapsKey = googleMapsKey;
}
get _supportsUnique() {
return false;
}
extendAdminMeta(meta) {
return {
...meta,
googleMapsKey: this._googleMapsKey,
};
}
gqlOutputFields() {
return [`${this.path}: ${this.graphQLOutputType}`];
}
gqlQueryInputFields() {
return [...this.equalityInputFields('String'), ...this.inInputFields('String')];
}
getGqlAuxTypes() {
return [
`
type Location{
coordinates: [Float]
},
type ${this.graphQLOutputType} {
location: Location
googlePlaceID: String
formattedAddress: String
lat: Float
lng: Float
}
`,
];
}
// Called on `User.avatar` for example
gqlOutputFieldResolvers() {
return {
[this.path]: item => {
const itemValues = item[this.path];
if (!itemValues) {
return null;
}
return itemValues;
},
};
}
async resolveInput({ resolvedData }) {
const placeId = resolvedData[this.path];
// NOTE: The following two conditions could easily be combined into a
// single `if (!inputId) return inputId`, but that would lose the nuance of
// returning `undefined` vs `null`.
// Premature Optimisers; be ware!
if (typeof placeId === 'undefined') {
// Nothing was passed in, so we can bail early.
return undefined;
}
if (placeId === null) {
// `null` was specifically set, and we should set the field value to null
// To do that we... return `null`
return null;
}
const response = await fetch(
`https://maps.googleapis.com/maps/api/geocode/json?place_id=${placeId}&key=${this._googleMapsKey}`
).then(r => r.json());
if (response.results && response.results[0]) {
const { place_id, formatted_address } = response.results[0];
const { lat, lng } = response.results[0].geometry.location;
return {
location: {
type: "Point",
coordinates: [lng, lat]
},
googlePlaceID: place_id,
formattedAddress: formatted_address,
lat: lat,
lng: lng,
};
}
return null;
}
gqlUpdateInputFields() {
return [`${this.path}: String`];
}
gqlCreateInputFields() {
return [`${this.path}: String`];
}
getBackingTypes() {
const type = `null | {
location: {
type: "Point",
coordinates: [lng, lat]
};
googlePlaceID: string;
formattedAddress: string;
lat: number;
lng: number;
}
`;
return { [this.path]: { optional: true, type } };
}
}
const CommonLocationInterface = superclass =>
class extends superclass {
getQueryConditions(dbPath) {
return {
...this.equalityConditions(dbPath),
...this.inConditions(dbPath),
};
}
};
class MongoLocationGoogleInterface extends CommonLocationInterface(MongooseFieldAdapter) {
addToMongooseSchema(schema) {
const schemaOptions = {
type: {
location: {
type: { type: String, default: "Point" },
coordinates: [Number]
},
googlePlaceID: String,
formattedAddress: String,
lat: Number,
lng: Number,
},
};
schema.add({
[this.path]: this.mergeSchemaOptions(schemaOptions, this.config)
});
}
}
module.exports = {
LocationGoogleImplementation,
MongoLocationGoogleInterface
}
package.json
{
"name": "#keystonejs/example-projects-blank",
"description": "A blank KeystoneJS starter project.",
"private": true,
"version": "5.0.15",
"author": "The KeystoneJS Development Team",
"repository": "https://github.com/keystonejs/keystone/tree/master/packages/create-keystone-app/example-projects/blank",
"homepage": "https://github.com/keystonejs/keystone",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"scripts": {
"dev": "cross-env NODE_ENV=development ENABLE_DEV_FEATURES=false DISABLE_LOGGING=false nodemon --exec keystone dev --harmony",
"build": "cross-env NODE_ENV=production ENABLE_DEV_FEATURES=false keystone build",
"start": "cross-env NODE_ENV=production keystone start",
"create-tables": "cross-env keystone create-tables"
},
"dependencies": {
"#arch-ui/layout": "^0.2.14",
"#arch-ui/typography": "^0.0.18",
"#keystonejs/adapter-mongoose": "^10.1.2",
"#keystonejs/app-admin-ui": "^7.3.13",
"#keystonejs/app-graphql": "^6.2.1",
"#keystonejs/auth-password": "^6.0.0",
"#keystonejs/email": "^5.2.0",
"#keystonejs/fields-authed-relationship": "^1.0.16",
"#keystonejs/fields-location-google": "^3.2.1",
"#keystonejs/fields-wysiwyg-tinymce": "^5.3.15",
"#keystonejs/file-adapters": "^7.0.8",
"#keystonejs/keystone": "^18.1.0",
"cross-env": "^7.0.3",
"date-fns": "^2.16.1",
"dotenv": "^8.2.0",
"google-maps-react": "^2.0.6",
"node-fetch": "^2.6.1",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"uuid": "^8.3.2"
},
"devDependencies": {
"nodemon": "^2.0.7"
}
}
The Error screenshot attached bellow

Create Custom mapping type in Elasticsearch 7.3.2 using node js

When I am doing custom mapping using kibana its working properly but when I am doing the same thing in my node program its showing mapper parsing exception.
Reason:Root mapping definition has unsupported parameters:tags(custom mapping name)
Because in kibana i am able to use include_type_name =true but in my node program it is not available.
var name = req.body.templatename;
var index_patterns = req.body.index_patterns;
console.log(index_patterns);
const opts: IndicesPutTemplateParams = {
name: name,
body: {
index_patterns: [index_patterns],
settings: {
analysis: {
filter: {
autocomplete_filter: {
type: "edge_ngram",
min_gram: 1,
max_gram: 20
}
},
analyzer: {
autocomplete: {
type: "custom",
tokenizer: "standard",
filter: [
"lowercase",
"autocomplete_filter"
]
}
}
}
},
mappings: {
tags: {
properties: {
name: {
type: "text",
analyzer: "autocomplete",
search_analyzer: "standard"
},
normalized: {
type: "text"
},
status: {
type: "text"
},
createdat: {
type: "date"
},
updatedat: {
type: "date"
}
}
}
}
}
}
try {
esClient.indices.putTemplate(opts).then((data: any) => {
return res.json({
data
});
console.log(data);
}).catch((err: any) => {
console.log(err);
res.status(500).json({
err
})
});
} catch (error) {
res.status(500).json({
error
})
}
}```
As Per documentation you need to give include_type_name as
client.indices.putTemplate({
name: string,
include_type_name: boolean, --->
order: number,
create: boolean,
timeout: string,
master_timeout: string,
flat_settings: boolean,
body: object -> mapping object
})
Or you can drop mapping name tags from mapping
mappings: {
tags: { ---> remove

Logic error when I try to create a new record in sails.js

When I want to create a new record in sails.js, I receive a logical error.
When I delete the following lines in my Controller, it's working:
amaliat_owner: req.param('amaliat_id') || 0,
place_die_owner: req.param('place_die_id') || 0,
media_place_die_owner: req.param('media_place_die_id') || 0,
media_owner: req.param('media_id') || null,
media_amaliat_owner: req.param('media_amaliat_id') || 0,
My route is:
'POST /delneveshte/create': 'DelneveshteController.create'
My model is:
module.exports = {
attributes: {
body: {
type: 'string',
columnType: 'text'
},
date_time: {
type: 'string',
},
is_confirmed: {
type: 'boolean',
defaultsTo: false
},
likee: {
type: 'number',
columnType: 'integer',
defaultsTo: 0
},
favorite: {
type: 'number',
columnType: 'integer',
defaultsTo: 0
},
user_owner: {
model: 'user',
},
shahid_owner: {
model: 'shahid',
},
amaliat_owner: {
model: 'amaliat',
},
media_place_die_owner: {
model: 'mediaplacedie',
},
place_die_owner: {
model: 'placedie',
},
media_owner: {
model: 'media',
},
media_amaliat_owner: {
model: 'mediaamaliat',
},
user_delneveshte_favorites: {
collection: 'userdelneveshtefavorite',
via: 'delneveshte_owner'
},
user_delneveshte_likes: {
collection: 'userdelneveshtelike',
via: 'delneveshte_owner'
},
},
};
My controller is:
create: async function (req, res, next) {
console.log(req.param('place_die_id'));
Delneveshte.create({
body: req.param('body'),
date_time: req.param('date_time'),
likee: req.param('like') || 0,
favorite: req.param('favorite') || 0,
is_confirmed: req.param('is_confirmed') || 0,
user_owner: req.param('user_id'),
shahid_owner: req.param('shahid_id') || 0,
amaliat_owner: req.param('amaliat_id') || 0,
place_die_owner: req.param('place_die_id') || 0,
media_place_die_owner: req.param('media_place_die_id') || 0,
media_owner: req.param('media_id') || null,
media_amaliat_owner: req.param('media_amaliat_id') || 0,
}, function (err, new_del) {
console.log("1");
if(err){
console.log("2");
return res.json({'status':false});
}
console.log("2");
return res.json({'status':true, 'result':new_del});
});
},
I use postman to send the request as follows:
But I receive the JSON {"status": false} and no record is created in the database. How can I fix my problem?
Some things to clarify:
Can you show the error? You can do that adding it to the json response.
If you put a defaultsTo property in the model, you shouldnt put it in the controller.
You have a lot of associations, so you can't send 0 as value of that associations because 0 is not a valid identifier. You should put a valid id or undefined.

Resources