I'm developing an API with loopback, everything worked fine until I decided to change the ids of my documents in the database. Now I don't want them to be auto generated.
Now that I'm setting the Id myself. I get an "Unknown id" 404, whenever I hit this endpoint: GET properties/{id}
How can I use custom IDs with loopback and mongodb?
Whenever I hit this endpoint: http://localhost:5000/api/properties/20020705171616489678000000
I get this error:
{
"error": {
"name": "Error",
"status": 404,
"message": "Unknown \"Property\" id \"20020705171616489678000000\".",
"statusCode": 404,
"code": "MODEL_NOT_FOUND"
}
}
This is my model.json, just in case...
{
"name": "Property",
"plural": "properties",
"base": "PersistedModel",
"idInjection": false,
"options": {
"validateUpsert": true
},
"properties": {
"id": {"id": true, "type": "string", "generated": false},
"photos": {
"type": [
"string"
]
},
"propertyType": {
"type": "string",
"required": true
},
"internalId": {
"type": "string",
"required": true
},
"flexCode": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": []
}
Your model setup (with with idInjection: true or false) did work when I tried it with a PostGreSQL DB setup with a text id field for smaller numbers.
Running a Loopback application with DEBUG=loopback:connector:* node . outputs the database queries being run in the terminal - I tried it with the id value you are trying and the parameter value was [2.002070517161649e+25], so the size of the number is the issue.
You could try raising it as a bug in Loopback, but JS is horrible at dealing with large numbers so you may be better off not using such large numbers as identifiers anyway.
It does work if the ID is an alphanumeric string over 16 characters so there might be a work around for you (use ObjectId?), depending on what you are trying to achieve.
Related
I'm trying to replicate an Azure DevOps process from one organization to another via the AZDO REST Api. I'm working on replicating the layout and am stuck because I can't discover the relationship between a custom field and a picklist when querying the source AZDO instance.
In my scenario I have a test work item type which I've called Issue. On the Issue interface I've created a custom field which is a picklist. While I can retrieve a list of lists via the Rest API and examine the field as well, I can't figure out how the two are related.
Here is a partial payload from the field:
{
"count": 39,
"value": [
...
{
"referenceName": "Custom.IssueSource",
"name": "Issue Source",
"type": "string",
"description": "Who is this attributed to",
"required": true,
"url": "https://dev.azure.com/MYORG/_apis/work/processes/f390103e-7097-4f19-b5b5-f9dbcf92bb6f/behaviors",
"customization": "custom"
},
... ]
}
and here is a partial payload from the lists get query which I used trial and error to determine was the picklist I've assigned:
{
"count": 10,
"value": [
...
{
"id": "2998d4e4-2bec-4935-98a1-b67a0b0b6d5d",
"name": "picklist_e854661e-8620-4ad9-be28-b974c5cb3a5d",
"type": "String",
"isSuggested": false,
"url": "https://dev.azure.com/MYORG/_apis/work/processes/lists/2998d4e4-2bec-4935-98a1-b67a0b0b6d5d"
},
...
]
}
Here is a partial layout response for the WIT:
{
"pages": [
{
"id": "d0171d51-ff84-4038-afc1-8800ab613160.System.WorkItemType.Details",
"inherited": true,
"label": "Details",
"pageType": "custom",
"visible": true,
"isContribution": false,
"sections": [
{
"id": "Section1",
"groups": [
...
{
"id": "bf03e049-5062-4d82-b91d-4396541fbed2",
"label": "Custom",
"isContribution": false,
"visible": true,
"controls": [
{
"id": "Custom.IssueSource",
"label": "Issue Source",
"controlType": "FieldControl",
"readOnly": false,
"visible": true,
"isContribution": false
}
]
}
]
},
... ]
}
Using fiddler against the AZDO web interface, the only time I see a reference to the picklist is from another non-AZDO API to https://dev.azure.com/MYORG/_apis/Contribution/dataProviders/query
Is there a way to discover the link via the AZDO Rest API? I saw this question which was similar but was about creating the link
Figured it out. Turns out you need to query from a different scope - work item tracking rather than work item tracking process:
https://dev.azure.com/MYORG/_apis/wit/fields/Custom.IssueSource?api-version=5.0-preview.2
returns
{
"name": "Issue Source",
"referenceName": "Custom.IssueSource",
"description": "Who is this attributed to",
"type": "string",
"usage": "workItem",
"readOnly": false,
"canSortBy": true,
"isQueryable": true,
...
"isIdentity": false,
--> "isPicklist": true,
"isPicklistSuggested": false,
--> "picklistId": "2998d4e4-2bec-4935-98a1-b67a0b0b6d5d",
"url": "https://dev.azure.com/MYORG/_apis/wit/fields/Custom.IssueSource"
}
In Loopback (v3), when defining indexes in my model.json files, how do I specify different types of indexes (such as a BRIN)? Also, how do I specify index conditions (such as if I want to create a partial index)? I'm using postgres for the database, if that's relevant.
You can configure the index type via type field.
{
"name": "MyModel",
"properties": {
// ...
},
"indexes": {
"myindex": {
"columns": "name, email",
"type": "BRIN",
// ...
}
}
}
I am afraid LoopBack does not support index conditions (partial indexes) yet. Feel free to open a new issue in https://github.com/strongloop/loopback-connector-postgresql/issues.
i was trying to add in Lb4. Its pretty straightforward there (should be same for lb3 as well i hope)
#model({
name: 'tablename',
settings: {
indexes: {
idx_tablename: {
columnA : '',
columnB : '',
columnC: ''
}
}
}
})
once the build is done, the index name idx_tablename with 3 columns will get created
In PostgreSQL and Loopback 3 you can specify an index for multi-column like this.
The following loopback JSON code creates index in Postgres with fields message and type are unique together.
{
"name": "notification",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"message": {
"type": "string",
"required": true
},
"type": {
"type": "string",
"required": true
},
"seen": {
"type": "boolean",
"required": true,
"default": false
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": {},
"indexes": {
"message_type_index": {
"keys": "message, type",
"options": {"unique": true}
}
}
}
Every time I query something from database I get objects like this:
{
"title": "faq",
"description": "",
"type": "application/pdf",
"size": 122974,
"filename": "faq.pdf",
"order": 0,
"createdAt": "2017-08-17T08:10:33.101Z",
"updatedAt": "2017-08-17T08:10:33.101Z",
"ACL": {
"role:cxcccccc_public": {
"read": true
},
"role:cxaaaaaaa_admin": {
"read": true,
"write": true
}
},
"objectId": "l6L5J1mRpH",
"__type": "Object",
"className": "Document"
}
A lot of these fields are Parse-specific: createdAt, updatedAt, ACL, className...
I didn't insert them when I added the row in the database but still they are returned when I do a query.
I want to expose this data through a clean REST API so I want to get rid of them.
Is there a way to get only the data I specified at insert time?
For now I am using lowdash, filtering with
data = _.omit(data, ['ACL', '__type', 'className', ...])
I have started using the dropwizard to develop a REST server. The Issue as long resource-method return an EntityType (say Enrolment), the out put is as expected but I have decided to send custom status codes using the below code
Response.status(Response.Status.PRECONDITION_FAILED)
.entity(Entity.json(new enrolment, AdapterResponseStatus.FAILURE)))
.build();
Everything is fine but the out put now contains a few more extra extra attributes as shown below.
{
"entity":
{
"id": 1267,
"courseId": "5798890",
"userName": "user#abc.com",
"tenantId": "tenant1",
"status": "approved",
"link": "/enrollments/null"
},
"variant":
{
"language": null,
"mediaType":
{
"type": "application",
"subtype": "json",
"parameters":
{
},
"wildcardType": false,
"wildcardSubtype": false
},
"encoding": null,
"languageString": null
},
"annotations":
[
],
"mediaType":
{
"type": "application",
"subtype": "json",
"parameters":
{
},
"wildcardType": false,
"wildcardSubtype": false
},
"language": null,
"encoding": null
}
I was expecting "entity" property alone but was getting other attributes. Is there any to get rid of them as no one is going to consuming them?
Those tags appear even if I replace my entity object (enrolment) with an empty string.
If you look at the signature of the ResponseBuilder's entity method, it takes the object directly; unlike Jersey client which requires a special Entity object that coincidentally has annotations and variants fields within.
Change your code to this:
Response.status(Response.Status.PRECONDITION_FAILED)
.entity(new Enrolment())
.build();
I am using the swagger tool for documenting my Jersey based REST API (the swaggerui I am using was downloaded on June 2014 don't know if this issue has been fixed in later versions but as I made a lot of customization to its code so I don't have the option to download the latest without investing lot of time to customize it again).
So far and until now, all my transfer objects have one level deep properties (no embedded pojos). But now that I added some rest paths that are returning more complex objects (two levels of depth) I found that SwaggerUI is not expanding the JSON model schema when having embedded objects.
Here is the important part of the swagger doc:
...
{
"path": "/user/combo",
"operations": [{
"method": "POST",
"summary": "Inserts a combo (user, address)",
"notes": "Will insert a new user and a address definition in a single step",
"type": "UserAndAddressWithIdSwaggerDto",
"nickname": "insertCombo",
"consumes": ["application/json"],
"parameters": [{
"name": "body",
"description": "New user and address combo",
"required": true,
"type": "UserAndAddressWithIdSwaggerDto",
"paramType": "body",
"allowMultiple": false
}],
"responseMessages": [{
"code": 200,
"message": "OK",
"responseModel": "UserAndAddressWithIdSwaggerDto"
}]
}]
}
...
"models": {
"UserAndAddressWithIdSwaggerDto": {
"id": "UserAndAddressWithIdSwaggerDto",
"description": "",
"required": ["user",
"address"],
"properties": {
"user": {
"$ref": "UserDto",
"description": "User"
},
"address": {
"$ref": "AddressDto",
"description": "Address"
}
}
},
"UserDto": {
"id": "UserDto",
"properties": {
"userId": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string"
},...
},
"AddressDto": {
"id": "AddressDto",
"properties": {
"addressId": {
"type": "integer",
"format": "int64"
},
"street": {
"type": "string"
},...
}
}
...
The embedded objects are User and Address, their models are being created correctly as shown in the json response.
But when opening the SwaggerUI I can only see:
{
"user": "UserDto",
"address": "AddressDto"
}
But I should see something like:
{
"user": {
"userId": "integer",
"name": "string",...
},
"address": {
"addressId": "integer",
"street": "string",...
}
}
Something may be wrong in the code that expands the internal properties, the javascript console doesn't show any error so I assume this is a bug.
I found the solution, there is a a line of code that needs to be modified to make it work properly:
In the swagger.js file there is a getSampleValue function with a conditional checking for undefined:
SwaggerModelProperty.prototype.getSampleValue = function(modelsToIgnore) {
var result;
if ((this.refModel != null) && (modelsToIgnore[this.refModel.name] === 'undefined'))
...
I updated the equality check to (removing quotes):
modelsToIgnore[this.refModel.name] === undefined
After that, SwaggerUI is able to show the embedded models.