Non CRUD Route in FeathersJS - javascript

I have a service results that handles all CRUD Operations for the results service in feathersjs. How would I create a route /results/:id/hr_bar_graph which basically fetches the result at that particular id and uses the resulting data to create an bar graph.
My code currently is:
module.exports = function (app) {
const Model = createModel(app);
const paginate = app.get('paginate');
const options = {
name: 'results',
Model,
paginate
};
// Initialize our service with any options it requires
app.use('/results', createService(options));
app.use('/results/:id/hr_bargraph_image', {
find(id, params){
this.app.service('results').get(id)
.then(function(response){
console.log(response);
})
.cathc(function(error){
console.log(error);
})
return Promise.resolve({
imageData: ''
});
}
});
// Get our initialized service so that we can register hooks and filters
const service = app.service('results');
service.hooks(hooks);
};
Been stuck here for a while now. Please help.

For reference, from this issue, the answer is to use params.route and Feathers normal find(params):
module.exports = function (app) {
const Model = createModel(app);
const paginate = app.get('paginate');
const options = {
name: 'results',
Model,
paginate
};
// Initialize our service with any options it requires
app.use('/results', createService(options));
app.use('/results/:id/hr_bargraph_image', {
async find(params){
const { id } = params.route;
const results = await app.service('results').get(id);
return {
imageData: ''
};
}
});
// Get our initialized service so that we can register hooks and filters
const service = app.service('results');
service.hooks(hooks);
};

Related

Unit Testing for dynamodb-onetable

I am new to Unit Testing and wanted to stub dynamodb-onetable library. I was trying to stub getData() from getDetails.ts file but it shows that "OneTableArgError: Missing Name Property". Because this getProjectDetails() contain new Table() class.
How to stub dynamodb-onetable so that I can get data in dataDetails variable. I was doing something like this in getEmp.spec.ts
dataDetailsStub = sinon.stub(DataService , "getData");
------lambda.ts
import { DynamoDBClient } from '#aws-sdk/client-dynamodb';
import Dynamo from 'dynamodb-onetable/Dynamo';
export const client = new Dynamo({
client: new DynamoDBClient({
region: REGION, }),
});
-------DataService.ts
import { client } from '../lambda';
const workspaceTable = new Table({
client,
name: TableName,
schema,
logger: true,
partial: false,
});
const projectDetail = workspaceTable.getModel('empDetail');
export default class **DataService** {
static getData = async (empId: string, type: string) => {
const params = {
projectId,
type
};
const response = await empDetail.find(params);
logger.trace('response', { response });
return response; };
}
------getDetails.ts
const dataDetails= await DataService.getData(
empId,
'EMPLOYEE-SAVEDATA'
);
I was trying to stub the DataService.getData() but getting error saying "OneTableArgError: Missing "name" property". I want to get data in dataDetailsStub whatever i am sending while mocking the getData()
const dataDetailsStub = sinon.stub(DataService , "getData").return({emp object});
Can anyone help me out on this. I'm really got stuck in this. Thanks in advance

GraphQL - Cannot move resolver to a separate file

I have searched the forum for anything related to customizing GraphQL in Strapi v4 but found nothing.
Note: my GraphQL skills is a novice.
I extended my GraphQL resolver in Strapi v4, and it worked fine as long as the resolver was in the same file as "index.ts." I want to modularize my GraphQL code by moving the resolver into a separate file. When I did that, I kept getting the following error:
"resolvers" is defined in the resolver but not in the schema.
Below is my resolver embedded in the file index.ts, which works fine without any issue.
index.ts
/**
* Extend register for GraphQL
*/
register({ strapi }): void {
// customized programmatically using GraphQL's extension
const extensionService = strapi.plugin("graphql").service("extension");
const UID = "api::truth-lending-disclosure.truth-lending-disclosure";
extensionService.use(({ strapi }) => ({
typeDefs: ``,
resolvers: {
Query: {
truthLendingDisclosures: async (parent, args, context) => {
// toEntityResponse method to allow us to convert our response
// to the appropriate format before returning the data.
const { toEntityResponseCollection } = strapi
.plugin("graphql")
.service("format").returnTypes;
// define level to populate
let _populate = {
body: {
populate: {
section: true,
},
},
};
// using shadow CRUD from entity service to fetch data
let entities = await strapi.entityService.findMany(UID, {
populate: _populate,
});
// find and replace placeholder with key-value risCustomerTermDataMap
// return the result as JSON string
let stringResult = dataSubstitution(
JSON.stringify(entities), // convert an object to JSON string
risCustomerTermDataMap
);
// conver JSON string back object
let objectResult = JSON.parse(stringResult);
debugger;
return toEntityResponseCollection(objectResult);
},
},
},
}));
},
Moved the resolvers logic into a separate file mycustom.resolvers.ts
mycustom.resolvers.ts
import { risCustomerTermDataMap } from "../../../../libs/common/risaCustomerTermDataMap";
import { dataSubstitution } from "../../../../libs/helpers/dataSubstitution";
// the logic in this file does not work with index.ts yet
// keep getting "resolvers" define in resolvers, but not in schema.
export const resolvers = {
Query: {
truthLendingDisclosures: async (parent, args, context) => {
console.log("***** GraphQL Resolvers*****");
const UID = "api::truth-lending-disclosure.truth-lending-disclosure";
// toEntityResponse method to allow us to convert our response
// to the appropriate format before returning the data.
const { toEntityResponseCollection } = strapi
.plugin("graphql")
.service("format").returnTypes;
// define level to populate
let _populate = {
body: {
populate: {
section: true,
},
},
};
// using shadow CRUD from entity service to fetch data
let entities = await strapi.entityService.findMany(UID, {
populate: _populate,
});
// find and replace placeholder with key-value risCustomerTermDataMap
// return the result as JSON string
let stringResult = dataSubstitution(
JSON.stringify(entities), // convert an object to JSON string
risCustomerTermDataMap
);
// conver JSON string back object
let objectResult = JSON.parse(stringResult);
debugger;
return toEntityResponseCollection(objectResult);
},
},
};
What am I missing?
Finally,I got it to work.
I modified my resolver in the external file "mycustom.resolvers.ts" to this:
filename: mycustom.resolvers.ts
export const truthLendingDisclosureResolvers = {
Query: {
async truthLendingDisclosures(): Promise<any> {
console.log("***** external resolver *****");
const UID = "api::truth-lending-disclosure.truth-lending-disclosure";
// toEntityResponse method to allow us to convert our response
// to the appropriate format before returning the data.
const { toEntityResponseCollection } = strapi
.plugin("graphql")
.service("format").returnTypes;
// define level to populate
let _populate = {
body: {
populate: {
section: true,
},
},
};
// using shadow CRUD from entity service to fetch data
let entities = await strapi.entityService.findMany(UID, {
populate: _populate,
});
// find and replace placeholder with key-value risCustomerTermDataMap
// return the result as JSON string
let stringResult = dataSubstitution(
JSON.stringify(entities), // convert an object to JSON string
risCustomerTermDataMap
);
// conver JSON string back object
let objectResult = JSON.parse(stringResult);
return toEntityResponseCollection(objectResult, {
args: {},
resourceUID: UID,
});
},
},
};
and modified the "index.ts" to this:
filename: index.ts
register({ strapi }): void {
// customized programmatically using GraphQL's extension
const extensionService = strapi.plugin("graphql").service("extension");
const UID = "api::truth-lending-disclosure.truth-lending-disclosure";
// disable an action on a query
// extensionService.shadowCRUD(UID).disableAction("find");
extensionService.use(({ strapi }) => ({
typeDefs: ``,
resolvers: truthLendingDisclosureResolvers,
}));
},

How to fetch Strapi by slug, and how to populate the categories

I'm trying to fetch post for a react blog with strapi backend using the slug.
I created the custom route and custom controller, but the value returned is missing a few attributes like images and category.
When I fetch using post Id, I use query string to populate the object returned, but I don't know how to had qs to the slug API route.
Below is the custom controller, and the custom route
///custom controller
async findOne(ctx) {
const { slug } = ctx.params;
const { query } = ctx;
const entity = await strapi.service('api::article.article').findOne(slug, query);
const sanitizedEntity = await this.sanitizeOutput(entity, query);
return this.transformResponse(sanitizedEntity);
}
///Custom Route
{
method: 'GET',
path: '/articles/slug/:slug',
handler: 'custom-controller.findOne',
config: {
policies: []
},
This is how I fetch from client in useEffect
useEffect(()=>{
const fetchData = async()=>{
// const query = qs.stringify({
// populate: '*',
// }, {
// encodeValuesOnly: true,
// });
const res = await axios.get(`http://localhost:1337/api/articles?filters[slug][$eq]=${slug}`)
console.log(res.data)
updateState(res.data)
}
fetchData()
setLoading(false)
}, [slug])
I've also tried to use the Entity API Service, but I just couldn't get it to work.
How do I populate the object to include these missing attributes?
With Strapi v4 you can do it this way
1. Create a file in src/api/article/_custom.js
Please note I put an underscore because:
Routes files are loaded in alphabetical order. To load custom routes before core routes, make sure to name custom routes appropriately (e.g. 01-custom-routes.js and 02-core-routes.js).
Source: https://docs.strapi.io/developer-docs/latest/development/backend-customization/routes.html#creating-custom-routers
module.exports = {
routes: [
{
method: 'GET',
path: '/articles/:slug',
handler: 'article.findOne',
config: {
auth: false
},
}
]
}
2. Edit the src/api/article/controllers/article.js
'use strict';
/**
* article controller
*/
const { createCoreController } = require('#strapi/strapi').factories;
module.exports = createCoreController('api::article.article', ({ strapi }) => ({
// Query by slug
async findOne(ctx) {
// thanks to the custom route we have now a slug variable
// instead of the default id
const { slug } = ctx.params;
const entity = await strapi.db.query('api::article.article').findOne({
where: { slug }
});
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
return this.transformResponse(sanitizedEntity);
},
}));
Now you can call your api this way:
http://localhost:1337/api/articles/my-beautiful-article-about-orange
Reference: https://www.youtube.com/watch?v=OVV0CfgX6Qk
Note: In the video, custom.js is loaded before post.js ^^

FindAll inside model hooks 'afterFind' not working using sequilize in nodejs

I have a project in nodejs using sequelize.
I am tring to do a FindAll inside a model but I am getting this error:
Cannot read property 'findAll' of undefined
I import the models like this:
const db = require('./index');
And in the model hooks I am trying to do this:
hooks: {
afterFind: async function(User){
let ranks_db = await db.rank.findAll({ });
// let rank = await checkRank(User.exp);
User['exp'] = {
value: User.exp,
rank
};
return User;
}
}
The findAll it is working in the controller but not in the model
. I want to know if it is posible to do a FindAll inside a model
It is likely that you have a circular dependency between your models and that db.rank has not been populated yet when the Model you are showing here is constructed. Instead of importing the models via require you can reference them from the sequelize variable that is passed into the Model.
module.exports = function Model(sequelize, DataTypes) {
return sequelize.define('user', { /* columns */ }, {
hooks: {
afterFind: async function(user) {
const ranks = await sequelize.models.rank.findAll();
// user.ranks = ranks;
},
}
});
}

Objection.js - Query builder not a function using insert method

I'm new to nodejs Development and I currently practicing CRUD operations on my postgresql. I used Objection.js for the ORM and Model making. I follow some codes from the docs and edit necessary lines but I don't actually get it to success instead it returns this error:
builder.knex(...).queryBuilder is not a function
I am following MVC pattern so I seperate the files according to it.
My controller:
'use strict';
const Todo = require('./../models/Todo');
class TodoController {
createTodo() {
Todo
.query()
.insert({
'title': 'asdasdasda',
'description': 'sdasdasdasdasdsad',
'date': '2017-12-12',
'isActive': true,
})
.then(name => {
console.log(name.description);
})
.catch(err => {
console.log(err);
});
}
}
module.exports = TodoController;
Knex Schema:
knex.schema.createTableIfNotExists('todo', (table) => {
table.increments();
table.string('title', 255).notNullable();
table.text('description').notNullable();
table.boolean('isActive').defaultTo('false');
table.datetime('date').notNullable();
table.timestamp('createdAt').defaultTo(knex.fn.now());
})
Model:
'use strict';
const { Model } = require('objection');
class Todo extends Model {
static get tableName() {
return 'Todo';
}
}
module.exports = Todo;
server.js:
...
const KnexConfig = require('./knexfile');
const { Model } = require('objection');
...
...
Model.knex(KnexConfig.development);
Hopefully someone could guide me, I'm still newbie on nodejs
It looks like you're trying to pass the knex configuration object to Model.knex() whereas you need to pass the actual knex instance.
On server.js:
const { Model } = require('objection');
const knex = require('knex');
const KnexConfig = require('./knexfile');
Model.knex(knex(KnexConfig.development));
This error message seems to arise whenever the knex instance passed to Objection.js is not what is should be.

Categories