Can't connect to Atlas cluster from my lambda using mongoose - node.js

I am trying to connect to a cluster using the last example from mongoose site
Here are my files using node14 and typescript
src/index.ts
import { APIGatewayProxyHandler } from "aws-lambda"
export { list as productsList } from "./products"
export const list: APIGatewayProxyHandler = (event, context, callback) => {
callback(null, {
statusCode: 200,
body: `Hello from ${process.env.AWS_SAM_BRANCH}`,
})
}
src/utils.ts
import mongoose from "mongoose"
import { APIGatewayProxyResult, Callback } from "aws-lambda"
let mongoConnection: Promise<typeof mongoose> | null = null
export const connectMongoose = async () => {
if (mongoConnection == null) {
const mongoURI = `mongodb+srv://USER:PASS#cluster0.ohjoj.mongodb.net/myFirstDB?retryWrites=true&w=majority`
mongoConnection = mongoose
.connect(mongoURI, { serverSelectionTimeoutMS: 3000 })
.then((mongooseReply) => {
console.log({ mongooseReply })
return mongoose
})
.catch((mongooseError) => {
console.log({ mongooseError })
return mongoose
})
await mongoConnection
}
return mongoConnection
}
export const errorHandler = (error: unknown, callback: Callback<APIGatewayProxyResult>) => {
console.error("catchedError", error)
if (error instanceof Error) {
callback(null, {
statusCode: 400,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ error: error.message }),
})
} else {
callback(null, {
statusCode: 500,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ error: "Internal server error" }),
})
}
}
src/products/index.ts
import { APIGatewayProxyHandler } from "aws-lambda"
import Model from "./model"
import { connectMongoose, errorHandler } from "../utils"
export const list: APIGatewayProxyHandler = (event, context, callback) => {
try {
connectMongoose()
Model.find({}, (error: unknown, reply: unknown) => {
if (error) throw error
callback(null, {
statusCode: 200,
headers: { "Content-Type": "application/json" },
body: JSON.stringify(reply),
})
})
} catch (error) {
errorHandler(error, callback)
}
}
src/products/model.ts
import mongoose from "mongoose"
const model = mongoose.model(
"Product",
new mongoose.Schema(
{
title: {
type: String,
required: true,
maxLength: 256,
},
description: {
type: String,
required: true,
maxLength: 2048,
},
count: {
type: Number,
required: true,
min: 0,
max: 1000 * 1000,
},
},
{
timestamps: true,
versionKey: false,
}
)
)
export default model
Here is the code in a repo is includes commands used to deploy with AWS SAM
https://github.com/LuisEnMarroquin/aws-sam-mongo
There are 2 routes in my app
https://2us58gl430.execute-api.us-east-2.amazonaws.com/
This works and returns Hello from test with status 200
https://2us58gl430.execute-api.us-east-2.amazonaws.com/products
This doesn't work and returns {"message":"Internal Server Error"} with status 500
Here are the CloudWatch logs exported as CSV
timestamp,message
1647203544609,"START RequestId: 83fd3fc8-1134-4ff4-a5f7-7e83a65159ce Version: $LATEST
"
1647203545742,"2022-03-13T20:32:25.685Z 83fd3fc8-1134-4ff4-a5f7-7e83a65159ce INFO {
mongooseReply: <ref *1> Mongoose {
connections: [ [NativeConnection] ],
models: { Product: Model { Product } },
events: EventEmitter {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
[Symbol(kCapture)]: false
},
options: {
pluralization: true,
autoIndex: true,
autoCreate: true,
[Symbol(mongoose:default)]: true
},
_pluralize: [Function: pluralize],
Schema: [Function: Schema] {
reserved: [Object: null prototype],
Types: [Object],
ObjectId: [Function]
},
model: [Function (anonymous)],
plugins: [ [Array], [Array], [Array], [Array], [Array], [Array] ],
default: [Circular *1],
mongoose: [Circular *1]
}
}
"
1647203549616,"END RequestId: 83fd3fc8-1134-4ff4-a5f7-7e83a65159ce
"
1647203549616,"REPORT RequestId: 83fd3fc8-1134-4ff4-a5f7-7e83a65159ce Duration: 5005.75 ms Billed Duration: 5000 ms Memory Size: 128 MB Max Memory Used: 76 MB Init Duration: 366.30 ms
"
1647203549616,"2022-03-13T20:32:29.616Z 83fd3fc8-1134-4ff4-a5f7-7e83a65159ce Task timed out after 5.01 seconds
"

As explained in this GitHub issue
A few suggestions:
You should either choose between a full callback approach and a full promise approach
Don't mix async / await with .then syntax when you can avoid it
import mongoose from "mongoose"
import { APIGatewayProxyHandler } from "aws-lambda"
let mongoConnection: Promise<typeof mongoose> | null = null
const connectMongoose = async () => {
if (mongoConnection == null) {
const mongoURI = `mongodb+srv://YOUR_CLUSTER_URL`
mongoConnection = mongoose
.connect(mongoURI, { serverSelectionTimeoutMS: 3000 })
.then((mongooseReply) => {
console.log({ mongooseReply })
return mongoose
})
.catch((mongooseError) => {
console.log({ mongooseError })
return mongoose
})
await mongoConnection
}
return mongoConnection
}
const Model = mongoose.model(
"Product",
new mongoose.Schema(
{
title: String,
description: String,
},
{
timestamps: true,
versionKey: false,
}
)
)
export const myRoute: APIGatewayProxyHandler = async (event, context) => {
try {
await connectMongoose();
const reply = await Model.find({}).exec();
return {
statusCode: 200,
headers: { "Content-Type": "application/json" },
body: JSON.stringify(reply),
};
} catch (error) {
return {
statusCode: 400,
headers: { "Content-Type": "application/json" },
body: "Server error",
};
}
}

Related

JS Sequelize Express Postgres API gives notNull Violation

Trying to learn sequelize, so I created a small test table and API. I am able to connect to the database and GET data from the API, but I can not insert anything. Sequelize is giving a warning that the value can't be null, even though I'm passing it in.
Call:
{payload=INSERT INTO api.testertab(submission_id, notes) VALUES (6452453223, 'testmessage2');, method=POST}
Basic Table Model:
const getTestertabModel = (sequelize, { DataTypes }) => {
const TT_ret = sequelize.define('testertab', {
submission_id: {
type: DataTypes.TEXT,
allowNull: false,
primaryKey: true
},
notes: {
type: DataTypes.TEXT,
allowNull: true
}
}, {
sequelize,
tableName: 'testertab',
schema: 'api',
timestamps: false,
indexes: [
{
name: "testertab_pkey",
unique: false,
fields: [
{ name: "submission_id" },
]
},
]
});
return TT_ret;
};
export default getTestertabModel;
and routes:
import { Router } from 'express';
const router = Router();
router.get('/', async (req, res) => {
console.log("get all")
const resp = await req.context.models.TT_ret.findAll()
return res.status(200).send(resp);
});
router.post('/upload', async (req, res) => {
console.log("post sub id")
//console.log(req.body)
const resp = await req.context.models.TT_ret.create();
return res.send(resp);
});
export default router;
index.js
import cors from 'cors';
import express from 'express';
import bodyParser from 'body-parser';
import models, { sequelize } from './src/models/index.js';
import routes from './src/routes/index.js';
const app = express();
import dotenv from 'dotenv/config';
app.use(bodyParser.json());
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use('/tab', routes.tester);
app.listen(process.env.port, () =>
console.log(`Example app listening on port ${process.env.port}!`),
);
Error message:
/root/node_modules/sequelize/lib/instance-validator.js:50
throw new sequelizeError.ValidationError(null, this.errors);
^
ValidationError [SequelizeValidationError]: notNull Violation: testertab.submission_id cannot be null
at InstanceValidator._validate (/root/node_modules/sequelize/lib/instance-validator.js:50:13)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async InstanceValidator._validateAndRunHooks (/root/node_modules/sequelize/lib/instance-validator.js:60:7)
at async InstanceValidator.validate (/root/node_modules/sequelize/lib/instance-validator.js:54:12)
at async model.save (/root/node_modules/sequelize/lib/model.js:2368:7)
at async Function.create (/root/node_modules/sequelize/lib/model.js:1344:12)
at async file:///root/easton_seqdb_api/src/routes/tester.js:14:16 {
errors: [
ValidationErrorItem {
message: 'testertab.submission_id cannot be null',
type: 'notNull Violation',
path: 'submission_id',
value: null,
origin: 'CORE',
instance: testertab {
dataValues: { submission_id: null },
_previousDataValues: {},
uniqno: 1,
_changed: Set(0) {},
_options: {
isNewRecord: true,
_schema: 'api',
_schemaDelimiter: '',
attributes: undefined,
include: undefined,
raw: undefined,
silent: undefined
},
isNewRecord: true
},
validatorKey: 'is_null',
validatorName: null,
validatorArgs: []
}
]
}

joi validation is not working as i expected

I'm newbie to the nestjs here i wanna to do a joi validation ,if i hit the request
in my terminal it's showing the joi validation errors but not returning any joi error in the response
i have found that controller invoked even before joi middleware
here is my controller,validation and joi schema,
controller
#Post()
#UsePipes(new ValidationPipe(subReportSchema))
cardsteps(#Body() payload:createCardStepsDto):Promise<any>{
return this.cardStepservice.createCardSteps(payload);
}
joi schema
import * as Joi from 'joi';
export const subReportSchema = Joi.object({
stepId: Joi.string().optional(),
stepTitle: Joi.string().when('stepId', { is: 'null', then: Joi.required() }),
order: Joi.number().when('stepId', { is: 'null', then: Joi.required() }),
draft: Joi.boolean().when('stepId', { is: 'null', then: Joi.required() }),
time: Joi.string().when('stepId', { is: 'null', then: Joi.required() }),
date: Joi.string().when('stepId', { is: 'null', then: Joi.required() }),
cardId: Joi.string().when('stepId', { is: 'null', then: Joi.required() }),
counters: Joi.object({
attendees: Joi.number().optional(),
children: Joi.number().optional(),
adults: Joi.number().optional(),
boys: Joi.number().optional(),
girls: Joi.number().optional(),
over18: Joi.number().optional(),
under18: Joi.number().optional(),
over60: Joi.number().optional()
}).when('stepId', { is: 'null', then: Joi.required() }),
}).options({
abortEarly: false, allowUnknown: true
});
validation schema
import {
PipeTransform,
BadRequestException,
ArgumentMetadata,
} from '#nestjs/common';
import { ObjectSchema } from 'joi';
export class ValidationPipe implements PipeTransform {
constructor(private readonly schema: ObjectSchema) { }
transform(value: Record<string, any>) {
const { error } = this.schema.validate(value)
if (error) {
throw new BadRequestException({
error: "validation failed",
message: error.message.replace(/(\"|[\d])/g, ''),
});
}
return value;
}
}
output reflects in my terminal
ValidationError [SequelizeValidationError]: notNull Violation: cardStep.date cannot be null
at InstanceValidator._validate (/home/user-89/projects/report-back2/node_modules/sequelize/src/instance-validator.js:78:13)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at InstanceValidator._validateAndRunHooks (/home/user-89/projects/report-back2/node_modules/sequelize/src/instance-validator.js:111:7)
at InstanceValidator.validate (/home/user-89/projects/report-back2/node_modules/sequelize/src/instance-validator.js:93:12)
at cardStep.save (/home/user-89/projects/report-back2/node_modules/sequelize/src/model.js:3996:7)
at Function.create (/home/user-89/projects/report-back2/node_modules/sequelize/src/model.js:2280:12)
at CardStepService.createCardSteps (/home/user-89/projects/report-back2/src/card-step/card-step.service.ts:30:41)
at /home/user-89/projects/report-back2/node_modules/#nestjs/core/router/router-execution-context.js:46:28
at /home/user-89/projects/report-back2/node_modules/#nestjs/core/router/router-proxy.js:9:17 {
errors: [
ValidationErrorItem {
message: 'cardStep.date cannot be null',
type: 'notNull Violation',
path: 'date',
value: null,
origin: 'CORE',
instance: [cardStep],
validatorKey: 'is_null',
validatorName: null,
validatorArgs: []
}
]
}

Why am I getting this error when I run my Discord Bot? - code: 50035

When I run my Discord bot, which is made through the Discord.js and Node.js framework, I receive the following error.
node_modules\#discordjs\rest\dist\lib\handlers\SequentialHandler.cjs:293
throw new DiscordAPIError.DiscordAPIError(data, "code" in data ? data.code : data.error, status, method, url, requestData);
^
DiscordAPIError[50035]: Invalid Form Body
options[0].type[NUMBER_TYPE_COERCE]: Value "string" is not int.
options[1].type[NUMBER_TYPE_COERCE]: Value "string" is not int.
at SequentialHandler.runRequest (D:\Programming\GitHub\void-bot\node_modules\#discordjs\rest\dist\lib\handlers\SequentialHandler.cjs:293:15)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async SequentialHandler.queueRequest (D:\Programming\GitHub\void-bot\node_modules\#discordjs\rest\dist\lib\handlers\SequentialHandler.cjs:99:14)
at async REST.request (D:\Programming\GitHub\void-bot\node_modules\#discordjs\rest\dist\lib\REST.cjs:52:22)
at async GuildApplicationCommandManager.create (D:\Programming\GitHub\void-bot\node_modules\discord.js\src\managers\ApplicationCommandManager.js:144:18) {
rawError: {
code: 50035,
errors: {
options: {
'0': { type: { _errors: [Array] } },
'1': { type: { _errors: [Array] } }
}
},
message: 'Invalid Form Body'
},
code: 50035,
status: 400,
method: 'POST',
url: 'https://discord.com/api/v10/applications/1016623217399570512/guilds/915541815225118740/commands',
requestBody: {
files: undefined,
json: {
name: 'poll',
name_localizations: undefined,
description: 'Sets up a poll.',
description_localizations: undefined,
type: undefined,
options: [
{
type: 'string',
name: 'title',
name_localizations: undefined,
name_localized: undefined,
description: 'The title of the poll.',
description_localizations: undefined,
description_localized: undefined,
required: true,
autocomplete: undefined,
choices: undefined,
options: undefined,
channel_types: undefined,
min_value: undefined,
max_value: undefined,
min_length: undefined,
max_length: undefined
},
{
type: 'string',
name: 'content',
name_localizations: undefined,
name_localized: undefined,
description: 'The content of the poll.',
description_localizations: undefined,
description_localized: undefined,
required: true,
autocomplete: undefined,
choices: undefined,
options: undefined,
channel_types: undefined,
min_value: undefined,
max_value: undefined,
min_length: undefined,
max_length: undefined
}
],
default_member_permissions: undefined,
dm_permission: undefined
}
}
}
I am unsure why I am receiving this for the code below:
require("dotenv").config();
const { GatewayIntentBits } = require("discord.js");
const Discord = require("discord.js");
const PREFIX = "!";
const client = new Discord.Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMembers,
GatewayIntentBits.GuildBans,
GatewayIntentBits.GuildEmojisAndStickers,
GatewayIntentBits.GuildIntegrations,
GatewayIntentBits.GuildWebhooks,
GatewayIntentBits.GuildInvites,
GatewayIntentBits.GuildVoiceStates,
GatewayIntentBits.GuildPresences,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.GuildMessageReactions,
GatewayIntentBits.GuildMessageTyping,
GatewayIntentBits.DirectMessages,
GatewayIntentBits.DirectMessageReactions,
GatewayIntentBits.DirectMessageTyping,
GatewayIntentBits.MessageContent,
GatewayIntentBits.GuildScheduledEvents
]
});
client.on("ready", () => {
console.log(`Logged in as ${client.user.tag}!`);
const guildID = "915541815225118740";
const guild = client.guilds.cache.get(guildID);
let commands;
if (guild) {
commands = guild.commands;
} else {
commands = client.application?.commands;
}
commands?.create({
name: "poll",
description: "Sets up a poll.",
options: [
{
name: "title",
description: "The title of the poll.",
required: true,
type: "string"
},
{
name: "content",
description: "The content of the poll.",
required: true,
type: "string"
}
]
});
});
client.on("interactionCreate", async (interaction) => {
if (!interaction.isCommand()) {
return;
}
const { commandName, options } = interaction;
if (commandName === "poll") {
const embed = new Discord.EmbedBuilder()
.setColor(0x0099FF)
.setTitle(options.get("title"))
.addFields(
{
value: options.get("content"),
inline: false
},
{
value: ":thumbsup: - Yes",
inline: true
},
{
value: ":thumbsdown: - No",
inline: true
}
)
}
interaction.reply(embed);
});
client.login(process.env.TOKEN);
I hope someone can help me out with this. It would be greatly appreciated as I have been stuck on this issue for a couple of days now.
Thanks,
nozzy
You've specified the option type using a string literal. It should be a value of the ApplicationCommandOptionType enum. So instead you should be using:
{
name: "...",
description: "...",
required: ...
type: ApplicationCommandOptionType.STRING
}

Apollo GraphQL Server - Access query params from cache plugin

I have an Apollo GraphQL server using the apollo-server-plugin-response-cache plugin and I need to determine whether or not I'm going to write to the cache based on incoming parameters. I have the plugin set up and I'm using the shouldWriteToCache hook. I can print out the GraphQLRequestContext object that gets passed into the hook, and I can see the full request source, but request.variables is empty. Other than parsing the query itself, how can I access the actual params for the resolver in this hook? (In the example below, I need the value of param2.)
Apollo Server:
new ApolloServer({
introspection: true,
playground: true,
subscriptions: false,
typeDefs,
resolvers,
cacheControl: {
defaultMaxAge: 60
},
plugins: [
apolloServerPluginResponseCache({
cache, // This is a "apollo-server-cache-redis" instance
shouldWriteToCache: (requestContext) => {
// I get a lot of info here, including the source query, but not the
// parsed out query variables
console.log(requestContext.request);
// What I want to do here is:
return !context.request.variables.param2
// but `variables` is empty, and I can't see that value parsed anywhere else
}
})
]
})
Here is my resolver:
export async function exapi(variables, context) {
// in here I use context.param1 and context.param2
// ...
}
I have also tried:
export async function exapi(variables, { param1, param2 }) {
// ...
}
Here is what I get logged out from the code above:
{
query: '{\n' +
' exapi(param1: "value1", param2: true) {\n' +
' records\n' +
' }\n' +
'}\n',
operationName: null,
variables: {}, // <-- this is empty?! How can I get param2's value??
extensions: undefined,
http: Request {
size: 0,
timeout: 0,
follow: 20,
compress: true,
counter: 0,
agent: undefined,
[Symbol(Body internals)]: { body: null, disturbed: false, error: null },
[Symbol(Request internals)]: {
method: 'POST',
redirect: 'follow',
headers: [Headers],
parsedURL: [Url],
signal: null
}
}
}
If you didn't provide variables for GraphQL query, you could get the arguments from the GraphQL query string via ArgumentNode of AST
If you provide variables for GraphQL query, you will get them from requestContext.request.variables.
E.g.
server.js:
import apolloServerPluginResponseCache from 'apollo-server-plugin-response-cache';
import { ApolloServer, gql } from 'apollo-server';
import { RedisCache } from 'apollo-server-cache-redis';
const typeDefs = gql`
type Query {
exapi(param1: String, param2: Boolean): String
}
`;
const resolvers = {
Query: {
exapi: (_, { param1, param2 }) => 'teresa teng',
},
};
const cache = new RedisCache({ host: 'localhost', port: 6379 });
const server = new ApolloServer({
introspection: true,
playground: true,
subscriptions: false,
typeDefs,
resolvers,
cacheControl: {
defaultMaxAge: 60,
},
plugins: [
apolloServerPluginResponseCache({
cache,
shouldWriteToCache: (requestContext) => {
console.log(requestContext.document.definitions[0].selectionSet.selections[0].arguments);
return true;
},
}),
],
});
server.listen().then(({ url }) => console.log(`🚀 Server ready at ${url}`));
GraphQL query:
query{
exapi(param1: "value1", param2: true)
}
Server logs print param1 and param2 arguments:
🚀 Server ready at http://localhost:4000/
[]
[ { kind: 'Argument',
name: { kind: 'Name', value: 'param1', loc: [Object] },
value:
{ kind: 'StringValue',
value: 'value1',
block: false,
loc: [Object] },
loc: { start: 15, end: 31 } },
{ kind: 'Argument',
name: { kind: 'Name', value: 'param2', loc: [Object] },
value: { kind: 'BooleanValue', value: true, loc: [Object] },
loc: { start: 33, end: 45 } } ]

Nestjs with Fastify e2e test with jest mocking is not working

I have Nodejs API that use Fastify. The e2e test is written with jest.
Please find my e2e test. It works and test passes.
Here I find the response is same as the request parameter instead of mocking object.
The actual API respond the request parameter but here I am mocking different object but it is not using it for response.
I am not sure whether passing request body is correct for POST.
Why Jest mocking is not working. The similar mocking works for unit test.
import {
FastifyAdapter,
NestFastifyApplication,
} from '#nestjs/platform-fastify';
import { Test, TestingModule } from '#nestjs/testing';
import request from 'supertest';
import { AppModule } from '../src/app/app.module';
import Axios, { AxiosResponse } from 'axios';
import { HttpService } from '#nestjs/common';
import { Observable, of, observable } from 'rxjs';
import { OracleDBService } from 'synapse.bff.core';
describe('AppController (e2e)', () => {
let app: NestFastifyApplication;
let httpService: HttpService;
let oracleDBService: OracleDBService;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication<NestFastifyApplication>(
new FastifyAdapter(),
);
httpService = moduleFixture.get<HttpService>(HttpService);
oracleDBService = moduleFixture.get<OracleDBService>(OracleDBService);
await app.init();
jest.setTimeout(30000);
});
it('/ (GET)', () => {
const data = [
{ name: 'item1', code: '100', category: 'cat1', price: 1250 },
{ name: 'item2', code: '101', category: 'cat1', price: 1250 },
{ name: 'item3', code: '102', category: 'cat1', price: 1250 },
{ name: 'item4', code: '103', category: 'cat1', price: 1250 },
];
const response: AxiosResponse<any> = {
data,
headers: {},
config: { url: 'http://localhost:3001/item/getitem' },
status: 200,
statusText: 'OK',
};
jest.spyOn(httpService, 'get').mockReturnValue(of(response));
return app
.inject({
method: 'GET',
url: '/item/100',
})
.then(onresponse =>
expect(JSON.parse(onresponse.payload)).toEqual(response.data),
);
});
it('/ (POST)', () => {
const data = {
Name: 'item1Mock',
Code: '500',
Category: 'cat1Mock',
Price: 9250,
};
const response: AxiosResponse<any> = {
data,
headers: {},
config: { url: 'http://localhost:3001/item/getitem' },
status: 200,
statusText: 'OK',
};
jest.spyOn(httpService, 'post').mockReturnValue(of(response));
return app
.inject({
method: 'POST',
url: '/item/create',
payload: {
body: {
Name: 'item1',
Code: '200',
Category: 'cat1',
Price: 1250,
},
},
})
.then(onResponse =>
expect(
JSON.parse(onResponse.payload).message.Detail.message[0].target.body,
).toEqual(response.data),
);
});
I am getting the following error
expect(received).toEqual(expected) // deep equality
- Expected
+ Received
Object {
- "Category": "cat1Mock",
- "Code": "500",
- "Name": "item1Mock",
- "Price": 9250,
+ "Category": "cat1",
+ "Code": "200",
+ "Name": "item1",
+ "Price": 1250,
}
90 | expect(
91 | JSON.parse(onResponse.payload).message.Detail.message[0].target.body,
> 92 | ).toEqual(response.data),
| ^
93 | );
94 | });
95 |
at item.controller.e2e-spec.ts:92:11

Resources