Adding Custom Middlewares in Strapi - javascript

I am creating a custom middleware for strapi that will change the response body to the hash.
I need to use this middleware for all routes in strapi, means on the application level.
'use strict';
/**
* `ctx-decrypt` middleware.
*/
var crypto = require('crypto');
const algorithm = 'aes-256-cbc';
function decrypt(text,key)
{
let iv = Buffer.from(text.iv, 'hex');
let encryptedText = Buffer.from(text.encryptedData, 'hex');
let decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(key), iv);
let decrypted = decipher.update(encryptedText);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted.toString();
}
function hash(data)
{
var hash = crypto.createHash('sha256', data);
return hash;
}
module.exports = (config,{env},{strapi}) =>
{
return
{
initialize()
{
strapi.app.use(async (ctx, next) =>
{
strapi.log.info('In ctx-decrypt middleware.');
var ENC_KEY = process.env.ENC_KEY;
var requestBody = ctx.request.body;
//var responseBody = ctx.body;
var dc = decrypt(requestBody,ENC_KEY);
if(!dc)
{
throw new Error('Invalid request body!');
ctx.response.set(hash("5u5234803riqifn"));
console.log(ctx.body)
await next();
}
else
{
ctx.response.set(hash("5u5234803riqifn"));
console.log(ctx.body)
await next();
}
});
}
};
};
Now I followed their latest docs but strapi always give some errors on using that middleware.
I ran
npx strapi generate
to generate middleware at the root of project.
and then I ran
npx strapi middlewares:list
to get all the middleware names, then added my 'global::ctx-decrypt' middleware name in the ./config/middlewares.js like this
module.exports = [
'strapi::errors',
'strapi::security',
'strapi::cors',
'strapi::poweredBy',
'strapi::cors',
'strapi::logger',
'strapi::query',
'strapi::body',
'strapi::session',
'strapi::favicon',
'strapi::public',
'global::ctx-decrypt',
];
it is giving me error like this
0|super-fu | [2022-12-23 02:48:51.181] debug: ⛔️ Server wasn't able to start properly.
0|super-fu | [2022-12-23 02:48:51.183] error: Middleware "global::ctx-decrypt": Cannot destructure property 'strapi' of 'undefined' as it is undefined.
0|super-fu | Error: Middleware "global::ctx-decrypt": Cannot destructure property 'strapi' of 'undefined' as it is undefined.
0|super-fu | at instantiateMiddleware (/home/blackhole/nodejs/super-funnels/super-funnels-backend/node_modules/#strapi/strapi/lib/services/server/middleware.js:12:11)
0|super-fu | at resolveMiddlewares (/home/blackhole/nodejs/super-funnels/super-funnels-backend/node_modules/#strapi/strapi/lib/services/server/middleware.js:56:18)
0|super-fu | at registerApplicationMiddlewares (/home/blackhole/nodejs/super-funnels/super-funnels-backend/node_modules/#strapi/strapi/lib/services/server/register-middlewares.js:66:29)
0|super-fu | at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
0|super-fu | at async Object.initMiddlewares (/home/blackhole/nodejs/super-funnels/super-funnels-backend/node_modules/#strapi/strapi/lib/services/server/index.js:99:7)
0|super-fu | at async Strapi.bootstrap (/home/blackhole/nodejs/super-funnels/super-funnels-backend/node_modules/#strapi/strapi/lib/Strapi.js:414:5)
0|super-fu | at async Strapi.load (/home/blackhole/nodejs/super-funnels/super-funnels-backend/node_modules/#strapi/strapi/lib/Strapi.js:426:5)
0|super-fu | at async Strapi.start (/home/blackhole/nodejs/super-funnels/super-funnels-backend/node_modules/#strapi/strapi/lib/Strapi.js:169:9)
0|super-fu |
0|super-fu | > super-funnels-backend#0.1.0 start
0|super-fu | > strapi start
How can I resolve this and use this middleware?

According to the docs, the correct function definition for middleware is as follows:
module.exports = (config, { strapi })=> {
return (context, next) => {};
};
There is no third parameter so perhaps try {env, strapi}?

Related

Apollo/Express server is not working in nx.dev environment

I'm trying to create a simple graphql backend using apollo-server-express.
Doing this in a expressJS application (Github repo) is working as expected:
Executing the query
query {
search {
result
}
}
at localhost:4000/graphql returns result as expected.
resolver
module.exports = {
Query: {
search: async (obj, { name, value }) => {
return 'result'
}
}
}
Now I would like to use this expressJS backend in a nx.dev environment (Github repo). I set up the same thing here, but running the server and executing the query in the graphQL playground returns null.
I don't see what is going wrong.
This is my structure:
main.ts
const express = require('express');
const { ApolloServer } = require('apollo-server-express');
const typeDefs = require('./graphql/types')
const resolvers = require('./graphql/resolvers')
const app = express();
const server = new ApolloServer({ typeDefs, resolvers });
server.applyMiddleware({ app });
app.listen({ port: 4000 }, () =>
console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`)
)
structure
+-- graphql
| +-- author
| | +-- author.resolvers.js
| | +-- author.graphql
| +-- search
| | +-- search.resolvers.js
| | +-- search.graphql
| +-- resolvers.js <<< Merges all `*.resolvers.*` files
| +-- types.js <<< Merges all `*.graphql` files
resolvers.js
const path = require('path');
const { mergeResolvers } = require('#graphql-tools/merge');
const { loadFilesSync } = require('#graphql-tools/load-files');
const resolversArray = loadFilesSync(path.join(__dirname, "./**/*.resolvers.*"));
module.exports = mergeResolvers(resolversArray);
types.js
const path = require('path');
const { loadFilesSync } = require('#graphql-tools/load-files');
const { mergeTypeDefs } = require('#graphql-tools/merge');
const typesArray = loadFilesSync(path.join(__dirname, './**/*.graphql'))
module.exports = mergeTypeDefs(typesArray, { all: true })
graphql/search/search.graphql
type Query {
search(name: String, value: String): [String]
}
graphql/search/search.resolvers.js
module.exports = {
Query: {
search: async (obj, { name, value }) => {
return 'result'
}
}
}

Supertest: How to write tests for API endpoint which posts to another API endpoint?

I am using supertest to test my API endpoints. One my endpoint to which I do a post, does another post to a different endpoint and I get a 404 error even though the endpoint is implemented. This is because I am doing not a listen() in my server which is used by supertest and since supertest is not aware of this endpoint in the test, it seems logical that I get a 404. Doing a listen isn't a wise choice since I have multiple tests in multiple files and I don't want to encounter address already in use error
Once way to solve this problem was to start another server as a pretest before running the test so that the endpoint is available during the test but there should be a better approach.
This is my server
// server.js
const express = require('express')
const app = express()
// Middlewares...
// Routes...
post(/abc) // abc posts to def during the test
post(/def)
module.exports = app
This is start.js which has nothing to do with the test and just does a listen and I use this for local manual testing
// start.js
const app = require('./server.js')
app.listen(3000)
Here is the solution:
server.js:
const express = require('express');
const request = require('request-promise');
const app = express();
app.post('/abc', async (req, res) => {
const url = req.protocol + '://' + req.get('host');
const rval = await request.post(`${url}/def`);
res.send(rval);
});
app.post('/def', (req, res) => {
res.send('def');
});
module.exports = app;
start.js:
const app = require('./server.js');
const s = app.listen(3000, () => {
console.log(`HTTP server is listening on http://localhost:${s.address().port}`);
});
server.test.js:
const supertest = require('supertest');
const app = require('./server');
describe('server', () => {
it('should pass', (done) => {
supertest(app)
.post('/abc')
.expect(200)
.end((err, res) => {
if (err) throw err;
expect(res.text).toBe('def');
done();
});
});
});
Integration test result with coverage report:
PASS src/stackoverflow/59090082/server.test.js
server
✓ should pass (54ms)
-----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
-----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
server.js | 100 | 100 | 100 | 100 | |
-----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 5.208s, estimated 13s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59090082

How can I test rest api using express and only JEST?

Can I use only JEST to test my rest API endpoints in express? I've searched several articles and also saw some of the questions of stackoverflow to see how can I do this. But the problem is for testing in express most of the people are using mocha/chai or supertest. But I want to use only JEST in my test. Is this possible?
this is my code so far where I want to implement this test:
index.js
const express = require('express');
const app = express();
app.post('/insert', function (req, res, next) {
const values = req.body; //name, roll
pool.query(`INSERT INTO student SET ?`, [values], (err, result) => {
if (err){
let err = new Error('Not Connected');
next(err);
} else {
res.status(201).json({ msg: `added ${result.insertId}`});
console.log(result);
}
});
});
what i've tried so far is :
index.test.js:
const express = require('express');
const app = express();
app.use('../routes');
test('Test POST, Success Scenario', async () => {
const response = await app.post('/insert')({
const values //dummy values will be insert here
});
expect(response.statusCode).toBe(200);
});
I know my test code is not correct, it's just a pseudocode I'm actually confused how I will hit the end point here
Here is the unit test solution for testing Nodejs web framework express REST API ONLY USING JEST:
index.js:
const express = require('express');
const { Pool } = require('pg');
const app = express();
const pool = new Pool();
app.post('/insert', (req, res, next) => {
const values = req.body;
pool.query(`INSERT INTO student SET ?`, [values], (err, result) => {
if (err) {
err = new Error('Not Connected');
next(err);
} else {
res.status(201).json({ msg: `added ${result.insertId}` });
console.log(result);
}
});
});
index.spec.js:
const routes = {};
jest.mock('express', () => {
const mExpress = {
post: jest.fn((path, controller) => {
routes[path] = controller;
})
};
return jest.fn(() => mExpress);
});
let queryCallback;
jest.mock('pg', () => {
const mpool = {
query: jest.fn((query, values, callback) => {
queryCallback = callback;
})
};
const mPool = jest.fn(() => mpool);
return { Pool: mPool };
});
require('./index');
const express = require('express');
const { Pool } = require('pg');
const app = express();
const pool = new Pool();
describe('insert', () => {
afterEach(() => {
jest.restoreAllMocks();
});
test('should insert data correctly', done => {
const logSpy = jest.spyOn(console, 'log');
expect(app.post).toBeCalledWith('/insert', expect.any(Function));
const mReq = { body: 1 };
const mRes = { status: jest.fn().mockReturnThis(), json: jest.fn().mockReturnThis() };
routes['/insert'](mReq, mRes);
expect(pool.query).toBeCalledWith('INSERT INTO student SET ?', [1], expect.any(Function));
const mResult = { insertId: 1 };
queryCallback(null, mResult);
expect(mRes.status).toBeCalledWith(201);
expect(mRes.status().json).toBeCalledWith({ msg: 'added 1' });
expect(logSpy).toBeCalledWith(mResult);
done();
});
test('should call error handler middleware', () => {
expect(app.post).toBeCalledWith('/insert', expect.any(Function));
const mReq = { body: 1 };
const mRes = { status: jest.fn().mockReturnThis(), json: jest.fn().mockReturnThis() };
const mNext = jest.fn();
routes['/insert'](mReq, mRes, mNext);
expect(pool.query).toBeCalledWith('INSERT INTO student SET ?', [1], expect.any(Function));
const mError = new Error('network error');
queryCallback(mError, null);
expect(mNext).toBeCalledWith(new Error('Not Connected'));
});
});
Unit test result with 100% coverage:
PASS src/stackoverflow/56635460/index.spec.js (7.391s)
insert
✓ should insert data correctly (15ms)
✓ should call error handler middleware (1ms)
console.log node_modules/jest-mock/build/index.js:860
{ insertId: 1 }
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.js | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 8.571s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/56635460

How to write test with Jest on call nested in a try { } catch { }

I'm setting up some tests with Jest on a Node.js express server, but I can't figure how to test calls nested in try/catch blocks.
Here is a part of my server.js :
const start = async () => {
try {
if (process.env.NODE_ENV) {
await db.sync({ force: false });
}
...
app.get("/", (request, response) => {
response.send("Please feel free to use our api with /api");
});
...
app.listen(port, () => {
console.log(`Server running on port ${port}`);
return app;
});
} catch (err) {
console.log(err.message);
}
};
export default new Promise ( async () => {
return await start();
});
Here I would like to test what is the app.listen() status code, but I'm still not that familiar with testing.
Any suggestion ?
Here is the test I wrote :
const request = require('supertest');
const app = require('../server');
describe('Test the root path', ()=>{
test("GET method returns status code 200", ()=>{
request(app).get('/').then( response =>{
expect(response.statusCode).toBe(200);
});
});
})
I assume that app is not what I expected because Jest tells me that app.address is not a function, so my export default new Promise is not the right solution.
Sorry if this seems messy, hope you can help !
Here is the solution:
server.js:
const express = require('express');
const app = express();
const port = 3000;
const db = {
async sync(options) {},
};
const start = async () => {
try {
if (process.env.NODE_ENV) {
await db.sync({ force: false });
}
app.get('/', (request, response) => {
response.send('Please feel free to use our api with /api');
});
return app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
} catch (err) {
console.log(err.message);
}
};
export default start;
server.test.js:
import request from 'supertest';
import start from './server';
describe('Test the root path', () => {
let server;
beforeAll(async () => {
server = await start();
});
afterAll((done) => {
server.close(done);
});
test('GET method returns status code 200', () => {
expect.assertions(1);
return request(server)
.get('/')
.then((response) => {
expect(response.statusCode).toBe(200);
});
});
});
Integration test result with coverage report:
PASS src/stackoverflow/55986832/server.test.js
Test the root path
✓ GET method returns status code 200 (45ms)
console.log src/stackoverflow/55986832/server.js:3342
Server running on port 3000
-----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
-----------|----------|----------|----------|----------|-------------------|
All files | 92.31 | 50 | 100 | 92.31 | |
server.js | 92.31 | 50 | 100 | 92.31 | 22 |
-----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 5.075s, estimated 10s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/55986832

How to set the API key in Stampery API.JS file

I am working on setting up Stampery. I am unable to figure out where to set the string API key in this API.JS file. The documentation says to set the STAMPERY_TOKEN as the API key not sure how to do this. Any help would be appreciated.
The link for Stampery is https://github.com/stampery/office.
'use strict';
const express = require('express');
const router = express.Router();
const bodyParser = require('body-parser')
const Stampery = require('stampery');
const development = process.env.NODE_ENV !== 'production';
const stamperyToken = process.env.STAMPERY_TOKEN;
var proofsDict = {}
if (!stamperyToken) {
console.error('Environment variable STAMPERY_TOKEN must be set before running!');
process.exit(-1);
}
//var stampery = new Stampery(process.env.STAMPERY_TOKEN, development ? 'beta' : false);
// For now, always use production Stampery API due to not making it work against beta.
var stampery = new Stampery(process.env.STAMPERY_TOKEN);
router.use(bodyParser.json());
router.post('/stamp', function (req, res) {
var hash = req.body.hash;
// Throw error 400 if no hash
if (!hash)
return res.status(400).send({error: 'No Hash Specified'});
// Transform hash to upper case (Stampery backend preferes them this way)
hash = hash.toUpperCase()
// Throw error 422 if hash is malformed
var re = /^[A-F0-9]{64}$/;
if (!(re.test(hash)))
return res.status(422).send({error: 'Malformed Hash'});
stampery.stamp(hash, function(err, receipt) {
if (err)
res.status(503).send({error: err});
else
res.send({result: receipt.id, error: null});
});
});
router.get('/proofs/:hash', function (req, res) {
var hash = req.params.hash;
stampery.getByHash(hash, function(err, receipts) {
if (err)
res.status(503).send({error: err});
else
if (receipts.length > 0)
res.send({result: receipts[0], error: null});
else
res.status(200).send({error: 'Oops! This email has not yet been attested by any blockchain.'});
});
});
module.exports = router;
I have added the following in Azure website. Should this suffice :
You need to set up STAMPERY_TOKEN environment veriable before starting your server.
You can do this like this for example (in Windows) set STAMPERY_TOKEN=your-token&& node app.js
There are 2 ways to add this to environment (For Ubuntu).
Add to bashrc File. Like:
export STAMPERY_TOKEN="YOUR-TOKEN"
Pass these params before running server. Like:
STAMPERY_TOKEN=YOUR-TOKEN node server.js
To access this variable you can get by:
console.log(process.env["STAMPERY_TOKEN"]);

Categories

Resources