How to validate a JSON schema with nested references - javascript

I have a JSON schema that references multiple schemas, using the '$ref' parameter whose schemas, in turn, reference other schemas.
For eg.
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"allOf": [
{
"$ref": "xyz.json"
}
]
}
where schema xyz.json is:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"id": { "type": "string", "title": "Identifier"},
"Xid": { "type": "object", "$ref": "pqr.json", "title": "X Identifier"}
}
}
Just for the sake of
convenience, I've placed them all in a single directory and attempted to validate the schema using a library called Ajv but on compiling the schema I get the error:
can't resolve reference #/event.json from id.
How can I possibly validate such a JSON schema? My goal is to create a single JavaScript object model for all the schemas. Thanks.

I don't know Ajv, but from looking at the docs, this seems to be accomplishable via the .addSchema method. Add all partial schemas like this (fs.readFileSync from node.js just for the sake of argument, in a browser it could be a XMLHttpRequest):
var main = JSON.parse(fs.readFileSync('main.json'));
var xyz = JSON.parse(fs.readFileSync('xyz.json'));
var avj = new Avj(main);
avj.addSchema(xyz, 'xyz');
and reference it from the main schema with the id you set in the second argument:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"allOf": [
{
"$ref": "xyz#"
}
]
}
If you have multiple partial schemas, you can also add them all in an array, if you set the ids as properties:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "xyz",
...
}
From my experience with other libraries (the old jayschema), you might need to write xyz# (with a hash at the end) at least for the reference, maybe also for the id parameter.

Related

No relational fields in response Strapi v4

I have a problem where none of the relational fields are present in the responses after fetching my data. When I look to the schema of one of my schemas with a relation, I see that the relational fields are present in the attributes object. But still I only get the non-relational fields in my response.
This is one of my schemas
{
"kind": "collectionType",
"collectionName": "activities",
"info": {
"singularName": "activity",
"pluralName": "activities",
"displayName": "activity"
},
"options": {
"draftAndPublish": true
},
"pluginOptions": {},
"attributes": {
"name": {
"type": "string"
},
"date": {
"type": "date"
},
"subcategory": {
"type": "relation",
"relation": "oneToOne",
"target": "api::subcategory.subcategory"
},
"members": {
"type": "relation",
"relation": "manyToMany",
"target": "api::member.member",
"inversedBy": "activities"
}
}
}
In Strapi v4 relations are not populated when fetching entries by default.
Explaination:
Queries can accept a populate parameter to explicitly define which fields to populate, with the following syntax:
GET /api/:pluralApiId?populate=field1,field2
Example request: Get books and populate relations with the author's name and address
GET /api/books?populate=author.name,author.address
For convenience, the * wildcard can be used to populate all first-level relations:
Example request: Get all books and populate all their first-level relations
GET /api/books?populate=*
Example request: Get all books and populate with authors and all their relations
GET /api/books?populate[author]=*
Note: Only first-level relations are populated with populate=*. Use the LHS bracket syntax (i.e. [populate]=*) to populate deeper:
Example request: Get all relations nested inside a "navigation" component in the "global" single type
GET /api/global?populate[navigation][populate]=*
Solution:
Change your API url to one of the following and you should be able to see the related fields populated in the response.
GET /api/activities?populate=subcategory,members
OR
GET /api/activities?populate=*
Reference:
Relations Population

Ember unknown relationship

I'm currently building software with Rails + Ember 3.12, but hitting a strange issue.
My models are the following:
// test-case-run
import DS from 'ember-data';
const { Model } = DS;
export default Model.extend({
testCase: DS.belongsTo('test-case'),
testCaseRunLogs: DS.hasMany('test-case-run-logs')
});
// test-case-run-log
import DS from 'ember-data';
const { Model } = DS;
export default Model.extend({
testCaseRun: DS.belongsTo('test-case-run'),
payload: DS.attr('')
});
And, my backend is returning the following payload:
{
"data": {
"id": "83",
"type": "test_case_run",
"relationships": {
"test_case": {
"data": {
"id": "90",
"type": "test_case"
}
},
"test_case_run_logs": {
"data": []
}
}
}
}
{
"data": {
"id": "83",
"type": "test_case_run",
"relationships": {
"test_case": {
"data": {
"id": "90",
"type": "test_case"
}
},
"test_case_run_logs": {
"data": [
{
"id": "426",
"type": "test_case_run_log"
}
]
}
}
},
"included": [
{
"id": "426",
"type": "test_case_run_log",
"attributes": {
"payload": "SCREENSHOT"
},
"relationships": {
"test_case_run": {
"data": {
"id": "83",
"type": "test_case_run"
}
}
}
}
]
}
I've got a custom adapter defining:
pathForType(type) {
return underscore(pluralize(type));
}
So, I think that everything should go well.
However, when I get into the ember inspector, I've got the following:
It seems that my relationship is not loaded properly.
And, I cannot access any data, such as:
log.get('testCaseRun') // that is null
run.get('testCaseRunLogs.length') // it returns 0
This is quite strange, as my records are loaded in the store, but not their relationships.
I have no idea on how to troubleshoot this, since the amount of information I can get from ember is quite limited (there is no error, the format looks good, ...).
Could someone help me to understand what's wrong with my calls? I've tried many things, such as renaming my models, but this does not improve the situation.
Moreover, this model is the only one, which I have problem with. All my other models don't have this problem. So, that's a bit weird.
Thanks a lot
The unknown in <(unknown):ember264> refers to the name of the class. That doesn't mean that your relationship is not loaded correctly. It's just Ember Data using anonymous classes.
To see the data of the relationship you could click on that string and afterwards on content. Another option is passing the full record to the console using the $E link in top right corner. Afterwards you could interact with the record on console, e.g. do a $E.get('testCaseRun.id').
By the way: You don't need to explicitly declare the model name on relationship definition if it's matches the dasherized property name. So testCaseRun: DS.belongsTo('test-case-run') is the same as testCaseRun: DS.belongsTo().
Try to declare hasMany relationship with the model name without 's'
testCaseRunLogs: DS.hasMany('test-case-run-log')
Finally, I've found the answer of my question.
The problem was that I was using the "underscore" form of the relationships:
"included": [
{
"id": "426",
"type": "test_case_run_log", <= HERE
"attributes": {
"payload": "SCREENSHOT"
},
"relationships": {
"test_case_run": {
"data": {
"id": "83",
"type": "test_case_run" <= HERE
}
}
}
}
]
And, changing pathForType was not sufficient.
So, I made my backend use dashes. And, it worked.

Strongloop Loopback: Filter by id of related Model

I have a Strongloop Loopback Node.js project with some models and relations.
The problem at hand
My problem relates how to query only those Media instances that have a relation to a certain Tag id, using the Angular SDK - while not querying Tags.media (which return Tag instances), but instead making a query somehow that returns plain Media instances.
Please read below for specific information..
Spec
Basically, I have a Model Media which has many 'tags' (model Tag). Think of a image file (Media) having various EXIF tags (Tag). Here is the relation spec (this all works as expected):
Media (media.json):
{
"name": "media",
"base": "PersistedModel",
"properties": {
"id": {
"type": "string",
"id": true
}
},
"relations": {
"tags": {
"type": "hasAndBelongsToMany",
"model": "tag"
}
}
Tag (tag.json):
{
"name": "tag",
"base": "PersistedModel",
"idInjection": true,
"properties": {
"name": {
"type": "string",
"required": true
}
},
"relations": {
"medias": {
"type": "hasAndBelongsToMany",
"model": "media"
}
},
"acls": [],
"methods": []
}
Solutions
Now, I know I could do a query like this (using Angular SDK in my example, but the syntax is the same):
injector.get('Tag').find({
'filter': {
'include': 'medias',
'where': {'id': <mytagid>}
}
});
My problem with this approach is, that I receive 1 (one) Tag instance with attached Media instances. This disrupts why whole workflow as I deal only with Media instances.. i just want to filter by Tag id, not bother about Tag at all.
Bottom line
If I see the API explorer (/explorer/), the return value of GET /api/tags/<myTagID>/medias is exactly what I need - an array of Media objects - but how to query them exactly like this using the Angular SDK (lb_services)?
I had a similar problem. One recommendation is to open the lb-services.js and try to find: /tags/:id/medias or something similar. Then you will find a comment like this: // INTERNAL. Use Tags.medias() instead. Or something similar. So that is the method that you should call. Do not call the "prototype$__get....." methods.
Then just call what it says there I suppose: Tag.medias({id:})
Other suggestions:
As you said in your description Media has many Tags. So why not use just
{
"name": "media",
"base": "PersistedModel",
"properties": {
"id": {
"type": "string",
"id": true
}
},
"relations": {
"tags": {
"type": "hasMany", <---------- hasMany
"model": "tag",
"foreignKey": "tagId" <---FK name
}
}
and
for the tags just belongsTo as type.
{
"name": "tag",
"base": "PersistedModel",
"idInjection": true,
"properties": {
"name": {
"type": "string",
"required": true
}
},
"relations": {
"medias": {
"type": "belongsTo",
"model": "media",
"foreignKey": "mediaId" <---FK name
}
},
"acls": [],
"methods": []
}
But really I don't think this is the problem because you said when you request GET /api/tags/<myTagID>/medias it returns what you want.
Then, in AngularJS you can use:
Media.tags({id:<mediaId>})
for media/:id/tags
and for the other side try:
Tag.medias({id:<tagId>})
Tag.find({
filter:{
where:{mediaId: <mediaId>} <----mediaId comes from FK name
}
})
In this case both are persistent models there is no problems, I had permission problems when doing a similar thing with data that extends User type. But that is another story...
Hope this is helpful, I changed some stuff from a similar app that I am doing and hope not making so many errors when adapting to your code...

Set the custom properties in joining model using LoopBacks HasManyThrough

I am trying to set up a many to many relationship in LoopBack 2.1.2
http://docs.strongloop.com/display/LB/HasManyThrough+relations
I tried to POST /api/patients/:patientId/physicians to create a new physician which links the patientId to the new physician, but does not set the appointmentDate in the appointment model.
Is there one API call to create this in one transaction?
What is the best way to add a new physician to a patient and setting the appointmentDate?
Do I have to create my own RESTFUL API call?
These are my json models
filename: appointment.json
{
"name": "appointment",
"base": "PersistedModel",
"relations": {
"patient": {
"type": "belongsTo",
"model": "patient"
},
"physician": {
"type": "belongsTo",
"model": "physician"
}
},
"properties": {
"appointmentDate": {
"type": "string"
}
},
"validations": [],
"acls": [],
"methods": []
}
filename: patient.json
{
"name": "patient",
"base": "PersistedModel",
"relations": {
"physicians": {
"type": "hasMany",
"model": "physician",
"through": "appointment"
}
},
"properties": {
"name": {
"type": "string"
}
},
"validations": [],
"acls": [],
"methods": []
}
filename: physician.json
{
"name": "physician",
"base": "PersistedModel",
"relations": {
"patients": {
"type": "hasMany",
"model": "patient",
"through": "appointment"
}
},
"properties": {
"name": {
"type": "string"
}
},
"validations": [],
"acls": [],
"methods": []
}
Disclaimer: I am a LoopBack developer working for StrongLoop.
Is there one API call to create this in one transaction?
No, there is no such API at the moment.
What is the best way to add a new physician to a patient and setting the appointmentDate?
You have to send two requests: The first one to create a physician (POST /physicians), the second one to create the appointment (POST /appointments).
Alternatively, you can use "Patient hasMany appointments" instead of "Patient hasMany physicians through Appointment", in which case the appointment can be added via
POST /patients/:patientId/appointments`
You will still have to create the physician first.
Do I have to create my own RESTFUL API call?
You can certainly do that, although I personally don't understand why two requests are a problem in this case. The operation "create a new physician with an appointment for the given patient" looks weird to me. Two steps ("create a new physician", and some time later "make an appointment") make more sense to me.
However, if you have a good example where it makes sense to create both records in one request, then please open a github issue in strongloop/loopback to discuss this further.
More info
At the moment, the "hasMany through" relation is tuned for the purpose of "hasAndBelongsToMany" relation, where the "through" model is just a container for the two id properties (foreign keys). That's the reason why the relation methods like POST /api/patients/:patientId/physicians do not support "through" properties like "appointmentDate".
I have created a github issue loopback#466 to discuss how to improve this part of LoopBack, feel free to comment there.
There is also a bug in loopback-explorer (#39), where the UI suggest that POST /patients/{id}/physicians is expecting an Appointment, even though the implementation expects a Physician instead.

Using ng-repeat with keys in an array

I have an object, simulating a Cassandra database, where I retrieve its data to display it on a AngularJs app. Unfortunately, I can't make a premade header for my array because the keys are subject to change, depending on the data I'll retrieve (Here's an example of what I'm talking about):
var columnFamilyData = {
"DocInfo": {
"4c58abf5": {
"name": "coucou",
"extension": "pdf",
"size": 8751,
"type": "facture",
"repository": "archive"
},
"8cd524d7a45de": {
"name": "gerard",
"extension": "xml",
"size": 48734,
"type": "compta",
},
"5486d684fe54a": {
"name": "splendide",
"extension": "tiff",
"type": "photos",
"METADATA_A": "jambon"
}
},
"Base": {
"BASE_A": {
"name": "BASE_A",
"description": "That base is truly outrageous, they are truly, truly outrageous",
"baseMetadata_1": "METADATA_A",
"baseMetadata_2": "METADATA_B",
"baseMetadata_3": "METADATA_C"
},
},
}
As you can see, the arrays in DocInfo and Base are different, data and keys.
What I want to do is being able to use my ng-repeat to create a <th> line with the key name (for instance, Extension, of METADATA_A), but only once, since ng-repeat may duplicate this information.
I'm also providing a JSFiddle, if it can help any of you to understand my goal.
Thanks for reading and/or answering, have a great day.
Question is not totally clear, but it seems like you want "(key, val) in object" syntax.
ng-repeat="(name, age) in {'adam':10, 'amalie':12}"

Categories