feathersjs/sequelize -> results of a 'find' are not equal to defined 'select' - feathersjs

Situation
In a project I have this code to select data from a table. Please note, it is working, I only don't get the result I expect.
serviceSurveyQuestions.find({
query: {
survey_id: this.survey_id,
user_id: this.$store.state.auth.user.id, //TODO move this to the hook!!
//todo make satus also not equal new
$or: [
{ status_id: process.env.mfp.statusSurveyQuestionStarted },
{ status_id: process.env.mfp.statusSurveyQuestionPlanned }
],
$sort: {
survey_question_question: 1
},
$limit: 150,
$select: [
'survey_question_question',
'survey_question_at',
'survey_question_answer',
'survey_question_details',
'survey_question_source_id',
'survey_question_source_answer_id',
'survey_question_source_user_id',
'survey_question_step',
'survey_question_dep_step',
'id'
]
}
}).then(page => {
this.listSurveyQuestions = page;
});
When I see what would be in one item of listSurveyQuestion I will see this:
{
"survey_question_question": "PEN 10 Scope vaststellen",
"survey_question_at": "2017-06-23T06:46:10.038Z",
"survey_question_answer": "",
"survey_question_details": "tester done",
"survey_question_source_id": 83499707,
"survey_question_source_answer_id": 74864,
"survey_question_source_user_id": 83488216,
"survey_question_step": 10,
"survey_question_dep_step": null,
"id": 4651,
"source_user": {
"user_id": 1005
},
"status": {
"status": "Planned"
},
"language": {
"language": "Dutch"
,
"source": {
"source": "MexonInControl - Pob - Dev (local)"
},
"survey_question": [{
"answer_type_id": 1014,
"answer_en": null,
"answer_nl": null,
"answer_explanation_en": null,
"answer_explanation_nl": null,
"survey_question_next_id": 4652
} ]
}
I know the result is comming from the configuration in my get and find hook of the service being called.
Expected Result
What I expect to happen is that the data returned is only the columns defined in the $SELECT. If I leave this as is, it will work but I'm getting to much data from the database which can be seen later as a security breach. Not with this example, but with other tables it will.
** Question **
So what do I need to change to have this functioning as expected. You could adapt the return of the service, but then I can't use the same service in other situations for the columns aren't available. Or can you pass an option to the service which will result in if (parameter = view 1) then return view 1 and so on.
** Solving **
Remark 1:
So I just see the 'cause' is a bit different. The configured hooks returns more columns from the question table which are not shown. So my guess here is that if you don't configure the includes in the find query, it will pass all includes. I need to check that and if this is the case, see if there is a option to not select the 'includes' as well.

Assuming that the hook you are referring to is setting hook.params.sequelize similar to this answer you will have to check if you included properties are also set in the $select query with something like this:
// GET /my-service?include=1
function (hook) {
const include = [];
const select = hook.params.query.$select;
// Go through all properties that are added via includes
['includeProp1', 'includeProp2'].forEach(propertyName => {
// If no $select or the include property is part of the $select
if(!select || select.indexOf(propertyName) !== -1) {
include.push({ model: ModelForIncludeProp1 });
}
});
hook.params.sequelize = { include };
return Promise.resolve(hook);
}

Related

How to retrive children from a single object intead of array in json-server?

I am using json-server for mock-backend to retrive children form a single object.
The parent table sentinel and the child table sensor
As you can see the sensors is an array and sentinel is an object.
I have used http://localhost:3000/sentinel?_embed=sensors but the response is not what i am expecting, because I want sensors: [{id: 1}, {id: 2}, ecc]
The official documentation shows that are two ways to retrive two tables:
_embed (include children) and _expand (include parent).
How could I achive this result?
Given that sentinel is a single object in your db.json and you can't have more than one sentinel it is not clear to me how your query is different from retrieving all sensors with sentinelId=10:
/sensors?sentinelId=10
In fact if you try this API:
/sentinel/10/sensors
it will work, because json-server rewrite the url exactly to the previous query.
If for some reason you don't want to use the sentinel id directly in the query, the other option is to use json-server as a module and define a custom route with the logic you need. Here's a basic example that exposes a /sentinel/sensors API and retrieve sentinel data along with the sensors whose sentinelId equals to the current sentinel id:
const jsonServer = require('json-server');
const server = jsonServer.create();
const router = jsonServer.router('./db.json');
const db = router.db;
server.use(jsonServer.bodyParser);
server.get('/sentinel/sensors', (req, res) => {
const sentinel = db.get('sentinel').value();
const sensors = db
.get('sensors')
.filter({ sentinelId: sentinel.id })
.value();
res.send({ ...sentinel, sensors: sensors });
});
server.use(router);
server.listen(3001, () => {
console.log('Mock server is running on port ' + 3001);
});
That would give you a response like this:
{
"id": 10,
"name": "Sentinel",
"sensors": [
{
"id": 1,
"sentinelId": 10
},
{
"id": 2,
"sentinelId": 10
}
]
}
Here's a stackblitz

Angular with json-server post entities without ID

I'm developing an angular project with the mock server json-server. GET is working correctly, but now I'm having some problems with POST.
I've defined this file interventi.json with these data:
{
"interventi":[
{
"codice": "123",
"codiceUso": 897,
"dataIntervento": "21-06-2019",
"dataInserimento": "24-06-2019",
"cooperativa": "Example",
"puntoVendita": "Example",
"tipiAttivita": [
"Prelievo",
"Sanzione"
],
"organoCompetente": "Ex"
},
{
"codice": "456",
"codiceUso": 823764,
"dataIntervento": "24-06-2019",
"dataInserimento": "29-06-2019",
"cooperativa": "Example 2",
"puntoVendita": "Example 2",
"tipiAttivita": [
"Prelievo"
],
"organoCompetente": "Ex"
}
]
}
I'm trying to post this json:
{
"codice":"a7spx",
"codiceUso":"123",
"dataIntervento":"2019-06-21T12:58:09.345Z",
"cooperativa":"2",
"puntoVendita":2,
"tipiAttivita":[
"Attivita 1",
"Attivita 2"
],
"organoCompetente":3,
"dataInserimento":"2019-06-21T12:58:09.345Z"
}
but it's giving me 500 internal server error:
"TypeError: Cannot read property 'id' of undefined
at Function.createId (C:\Users\Marina\coop-workspace\wqrng\web\src\main\angular\wqrng\node_modules\json-server\lib\server\mixins.js:47:39)
at Function.insert (C:\Users\Marina\coop-workspace\wqrng\web\src\main\angular\wqrng\node_modules\lodash-id\src\index.js:47:49)
at C:\Users\Marina\coop-workspace\wqrng\web\src\main\angular\wqrng\node_modules\lodash\lodash.js:4374:28
at arrayReduce (C:\Users\Marina\coop-workspace\wqrng\web\src\main\angular\wqrng\node_modules\lodash\lodash.js:683:21)
at baseWrapperValue (C:\Users\Marina\coop-workspace\wqrng\web\src\main\angular\wqrng\node_modules\lodash\lodash.js:4373:14)
at LodashWrapper.wrapperValue (C:\Users\Marina\coop-workspace\wqrng\web\src\main\angular\wqrng\node_modules\lodash\lodash.js:9052:14)
at create (C:\Users\Marina\coop-workspace\wqrng\web\src\main\angular\wqrng\node_modules\json-server\lib\server\router\plural.js:225:50)
at Layer.handle [as handle_request] (C:\Users\Marina\coop-workspace\wqrng\web\src\main\angular\wqrng\node_modules\express\lib\router\layer.js:95:5)
at next (C:\Users\Marina\coop-workspace\wqrng\web\src\main\angular\wqrng\node_modules\express\lib\router\route.js:137:13)
at next (C:\Users\Marina\coop-workspace\wqrng\web\src\main\angular\wqrng\node_modules\express\lib\router\route.js:131:14)"
I've followed this tutorial to do post, and saw that field id is not mandatory, but server is complaining. If I add id field in my json, it works correctly. But I don't want id in my entities, is there a way to disable this behaviour?
EDIT
// Return incremented id or uuid
// Used to override lodash-id's createId with utils.createId
function createId(coll) {
var _ = this;
var idProperty = _.__id();
if (_.isEmpty(coll)) {
return 1;
} else {
var id = _(coll).maxBy(idProperty)[idProperty];
// Increment integer id or generate string id
return _.isFinite(id) ? ++id : nanoid(7);
}
}
If you are using any id other then default "id" you have explicitly configure it using json-server CLI.
--id, -i Set database id property (e.g. _id) [default: "id"]
Check following link
https://github.com/typicode/json-server#cli-usage

Jira API | Error: "Operation value must be a string" - trying to set value nested two levels deep

Trying to create a new jira ticket of specific requestType, but it is nested two levels deep. Tried few possible alterations, but no luck. Here's the code I have,
require 'jira-ruby' # https://github.com/sumoheavy/jira-ruby
options = {
:username => jira_username,
:password => jira_password,
:site => 'https://jiraurl/rest/api/2/',
:context_path => '',
:auth_type => :basic,
:read_timeout => 120
}
client = JIRA::Client.new(options)
issue = client.Issue.build
fields_options = {
"fields" =>
{
"summary" => "Test ticket creation",
"description" => "Ticket created from Ruby",
"project" => {"key" => "AwesomeProject"},
"issuetype" => {"name" => "Task"},
"priority" => {"name" => "P1"},
"customfield_23070" =>
{
"requestType" => {
"name" => "Awesome Request Type"
}
}
}
}
issue.save(fields_options)
"errors"=>{"customfield_23070"=>"Operation value must be a string"}
Also tried passing a JSON object to customfield_23070,
"customfield_23070": { "requestType": { "name": "Awesome Request Type" } }
still no luck, get the same error message.
If it helps, this is how customfield_23070 looks like in our Jira,
Does anyone know how to set requestType in this case, please? Any help is greatly appreciated!!
It seems that for custom fields with specific data types (string/number), you must pass the value as:
"customfield_1111": 1
or:
"customfield_1111": "string"
instead of:
"customfield_1111":{ "value": 1 }
or:
"customfield_1111":{ "value": "string" }
I'm not sure but you can try this possible examples:
eg.1:
"customfield_23070"=>{"name"=>"requestType","value"=>"Awesome Request Type"}
eg.2:
"customfield_23070"=>{"requestType"=>"Awesome Request Type"}
eg.3:
"customfield_23070"=>{"value"=>"Awesome Request Type"}
eg.4
"customfield_23070"=>{"name"=>"Awesome Request Type"}
for ref there are 2 methods depending upon the fields you are interacting with
have a look here '
updating-an-issue-via-the-jira-rest-apis-6848604
' for the applicable fields for update via verb operations, the other fields you can use examples as per above,
you can use both methods within the same call
{
"update": {"description": [{"set": "Description by API Update - lets do this thing"}]},
"fields": {"customfield_23310": "TESTING0909"}
}
Ok, I think I found how to do it.
You need to provide a string, and that string is the GUID of the RequestType.
In order to get that GUID. You need to run the following in a scriptrunner console:
import com.atlassian.jira.component.ComponentAccessor
def issue = ComponentAccessor.issueManager.getIssueByCurrentKey("ISSUE-400546") //Issue with the desired Request Type
def cf = ComponentAccessor.customFieldManager.getCustomFieldObjectByName("Tipo de solicitud del cliente") //Change it to the name of your request type field
issue.getCustomFieldValue(cf)
Source: https://community.atlassian.com/t5/Jira-Software-questions/how-to-set-request-type-value-in-while-create-jira-issue/qaq-p/1106696

Querying couchbase by passing parameter

I am having couchbase report documents stored in below format:
{
"agree_allowed":true,
"assigned_by":"",
"assigned_to":"",
"closed":[
],
"comments_allowed":true,
"details":"Test",
"email":"",
"status":"In Progress",
"subscribed":{
"user_cfd29b81f0263a380507":true,
"user_cfd29b81f0263a380508":true,
"user_cfd29b81f0263a380509":true,
"user_cfd29b81f0263a3805010":true
},
"summary":"Test",
"time_open":0,
"timestamp":"2015-07-17T15:34:30.864Z",
"type":"report",
"user_id":"user_cfd29b81f0263a380507",
"username":"test17"
}
json contain subscribed filed, it is list of user_id who follow reports.
Problem is i have to emit report document if subscribed field contain user_id, if i pass user_id ='user_cfd29b81f0263a380507' pass as key parameter. i am wondering how can use user_id to compare in view
here is the code i write:-
function map(doc, meta) {
if (doc.type == 'report' && doc.subscribed) {
for (var user_id in doc.subscribed) {
emit(doc.user_id, doc);
}
}
}
but it didn't return expected result.
Can anybody help.
If I understand your question I think you want the ability to query the users who have subscribed.
If that is the case the view code is wrong it is submitting doc.user_id and not user_id, which is the variable you assign values to in the loop but never use. In any case I think it would be better to use a different names to avoid confusion.
function map(doc, meta) {
if (doc.type == 'report' && doc.subscribed) {
for (var subscriber in doc.subscribed) {
emit(subscriber);
}
}
}
To query the users who have subscribed you would use key=user_cfd29b81f0263a380507. The result would be:
{
"total_rows": 4,
"rows": [
{
"id": "docs",
"key": "user_cfd29b81f0263a380507",
"value": null
}
]
}

Using REST design properly - Categories and Subcategories

I have two question on basic REST concepts.
QUESTION 1: Categories
So I have a list of Categories I want to show from a database.
SELECT * from categories
Currently, I use this REST desgin: /api/v1/categories/
Is that proper?
I have also seen /api/v1/categories/list/ -- or is this preferred? (If so, then what would a simple /categories call display then? (Or would the proper way then be /api/v1/category/list where category is singular and adding list will show you all categories -- this way a call to /category would allow veiwing info on just one?)
QUESTION 2: Subcategories. (Think "Seinfeld" as a subcategory of "Television".)
SELECT * FROM subcategories" WHERE category_id = {id}
The id above might be the Television Category where I want to get specific shows listed.
Would I do /api/v1/categories/{id}/ for the Subcategory with the subcat_id? Would I have to use parameters instead like /Categories?id={id}/
How would this relationship work?
My answers are based on "pragmatic REST".
QUESTION 1: Categories
If you go with plural or singular form then I would suggest sticking with it and not jump between singular and plural... this is subjective.
If you go with singular form, then the list path action sounds applicable. If you go with plural then I think it is more subjective... IMHO list removes ambiguity and I would prefer it.
QUESTION 2: Subcategories. (Think "Seinfeld" as a subcategory of "Television".)
IMHO sub-category sounds like a separate resource. I think it should have its own path element.
Would I do /api/v1/categories/{id}/ for the Subcategory with the subcat_id? Would I have to use parameters instead like /Categories?id={id}/
I think that /api/v1/subcategories/{id}/ is more popular. But one thing that is becoming more popular is searching criteria. ID might just be one of many search criteria. If you see yourself adding search criteria then /api/v1/subcategories/?id={id} or /api/v1/subcategories/?filter={some_search_string} where you decide how that search string is parsed.
The most important thing to consider is that you are able to grow (extend) your API without changing these initial decisions you are making now. Its easy to add to an API but harder to alter existing API design once it is being used.
The URI structure is an implementation detail, it does not matter by REST as long as your service fulfills the uniform interface constraint, which is about applying the relevant standards. In your case the URI structure must fulfill the URI standard and you have to use a hypermedia format, which contains hyperlinks. So in your case /api/v1/sdfh34gsv/123regf3 would be completely okay as long as it is in a hyperlink and there is sufficient metadata available to understand what that hyperlink does. E.g. with HAL+JSON:
{
"_links": {
"/api/v1/docs/rels/category": {
"href": "/api/v1/sdfh34gsv/123regf3",
"title": "Television"
}
}
}
By processing such a response the client will recognize the "/api/v1/docs/rels/category" link relation, so it will know that it is a hyperlink to a category, which title is "Television" and the details of the category can be retrieved by following the link. If the client does not know the /api/v1/docs/rels/category link relation, then it can dereference the URI and and probably it will get some description in RDF, which it can use to display the hyperlink in a more basic form. Ofc, if developers dereference the same URI, they can get a HTML description of the link relation.
By most of the REST services this does not happen, because they use vendor specific MIME types and probably plain JSON, which violates the HATEOAS constraint, but I guess it is more practical in some cases.
async getAllCategorySubCategoryAndGroupCategoryDetail(): Promise < CategoryDetails[] > {
try {
let categoryGroupQuery = `SELECT shrt_category_groups.id as categoryGroupId,
shrt_category_groups.category_group_name as categoryGroupName,
concat('${ENV.IMG_SERVER}',shrt_category_groups.category_group_image) AS categoryGroupImage,
shrt_category_groups.is_active as isActive,
shrt_category_groups.sort_order as sortOrder
FROM shrt_category_groups where shrt_category_groups.is_active = '1' `;
let categoryGroupList = await pool.query(categoryGroupQuery);
if (categoryGroupList.length > 0) {
let categoryListQuery = `SELECT shrt_categories.id as categoryId,
shrt_categories.category_name as categoryName,
shrt_categories.category_image as categoryImage,
shrt_categories.is_active as isActive,
shrt_categories.category_group_id as categoryGroupId
FROM shrt_categories where shrt_categories.is_active = '1'`;
let categoryList = await pool.query(categoryListQuery);
let subcategoryQuery = `SELECT shrt_sub_categories.id as subCategoryId,
shrt_sub_categories.is_active as isActive,
shrt_sub_categories.subcategory_commission as commission,
shrt_sub_categories.commission_type as commissionType,
shrt_sub_categories.sub_category_name as subCategoryName,
shrt_sub_categories.category_id as categoryId
FROM shrt_sub_categories where shrt_sub_categories.is_active = '1'`;
let subCategoryList = await pool.query(subcategoryQuery);
const getCategory = (categoryGroupId: any) => {
return categoryList.filter((cat: any) => cat.categoryGroupId == categoryGroupId)
}
const getSubCategory = (categoryId: any) => {
return subCategoryList.filter((subCategory: any) => subCategory.categoryId == categoryId)
}
let mergeData = categoryGroupList.map((catG: any) => {
return { ...catG,
category: getCategory(catG.categoryGroupId).map((cat) => {
return { ...cat,
subCategory: getSubCategory(cat.categoryId)
}
})
}
})
return mergeData;
} else {
return categoryGroupList;
}
} catch (error) {
throw error;
}
}
{
"data": [
{
"categoryGroupId": 1,
"categoryGroupName": "Cloud",
"categoryGroupImage": null,
"isActive": "1",
"sortOrder": null,
"category": [
{
"categoryId": 1,
"categoryName": "Kinder & Baby",
"categoryImage": null,
"isActive": "1",
"categoryGroupId": 1,
"subCategory": [
{
"subCategoryId": 17,
"isActive": "1",
"commission": null,
"commissionType": null,
"subCategoryName": "Shirts",
"categoryId": 1
},
{
"subCategoryId": 20,
"isActive": "1",
"commission": null,
"commissionType": null,
"subCategoryName": "Jacken",
"categoryId": 1
},
{
"subCategoryId": 21,
"isActive": "1",
"commission": null,
"commissionType": null,
"subCategoryName": "Organic Collection",
"categoryId": 1
},
{
"subCategoryId": 22,
"isActive": "1",
"commission": null,
"commissionType": null,
"subCategoryName": "Baby",
"categoryId": 1
},
{
"subCategoryId": 23,
"isActive": "1",
"commission": null,
"commissionType": null,
"subCategoryName": "Hoodies & Sweatshirts",
"categoryId": 1
}
]
}
]
}
]
}