Create object paths from a deeply nested object (Schema) - javascript

I have an avro schema, which is a deeply nested object with the same (sub)-structure. It looks like this:
{
"type": "record",
"namespace": "company.car.v1",
"name": "CarV1",
"fields": [
{
"name": "plateNumber",
"type": "string"
},
{
"name": "ownerId",
"type": "string",
"keepThisField": "true"
},
{
"name" : "details",
"keepThisField": "true"
"type" : {
"type" : "record",
"name" : "DetailsV1",
"fields" : [
{
"name": "engine",
"type": {
"type": "record",
"name": "EngineV1",
"fields": [
{
"name": "size",
"type": "int",
"default": 0,
"keepThisField": "true"
},
{
"name": "valvesCount",
"type": "int",
"default": 0
}
]
}
},
{
"name" : "color",
"type" : "string",
"default" : "NONE"
},
{
"name" : "rimSize",
"type" : "int",
"default" : "NONE"
}
]},
"default" : {}
},
{
"name": "isBrandNew",
"type": "boolean"
}
]
}
I want to be able to generally get all the object paths of this object (Schema) in JavaScript. So having something like a extractPaths(avroSchema) which for the example above would return:
[
"plateNumber",
"ownerId",
"details.engine.size",
"details.engine.valvesCount",
"details.color",
"details.rimSize",
"isBrandNew"
]
The order of the strings does not matter obviously. Anyone has an idea how this can be achieved in JavaScript?

Use a recursive generator function for generate path string.
var schema = { "type": "record", "namespace": "company.car.v1", "name": "CarV1", "fields": [{ "name": "plateNumber", "type": "string" }, { "name": "ownerId", "type": "string", "keepThisField": "true" }, { "name": "details", "keepThisField": "true", "type": { "type": "record", "name": "DetailsV1", "fields": [{ "name": "engine", "type": { "type": "record", "name": "EngineV1", "fields": [{ "name": "size", "type": "int", "default": 0, "keepThisField": "true" }, { "name": "valvesCount", "type": "int", "default": 0 }] } }, { "name": "color", "type": "string", "default": "NONE" }, { "name": "rimSize", "type": "int", "default": "NONE" }] }, "default": {} }, { "name": "isBrandNew", "type": "boolean" }] }
function* extractPaths(schema, value) {
for (const { name, type } of schema.fields) {
let path = value ? `${value}.${name}` : name
if (typeof type == "object")
yield* extractPaths(type, path);
else
yield path
}
}
console.log([...extractPaths(schema)]);

Related

Remove all properties in a deeply nested JavaScript object based on a particular condition

I have an object that looks something like this (it is an Avro Schema):
{
"type": "record",
"namespace": "company.car.v1",
"name": "CarV1",
"fields": [
{
"name": "plateNumber",
"type": "string"
},
{
"name": "ownerId",
"type": "string"
},
{
"name" : "details",
"type" : {
"type" : "record",
"name" : "DetailsV1",
"fields" : [
{
"name": "engine",
"type": {
"type": "record",
"name": "EngineV1",
"fields": [
{
"name": "size",
"type": "int",
"default": 0
},
{
"name": "valvesCount",
"type": "int",
"default": 0
}
]
}
},
{
"name" : "color",
"type" : "string",
"default" : "NONE"
},
{
"name" : "rimSize",
"type" : "int",
"default" : "NONE"
}
]},
"default" : {}
},
{
"name": "isBrandNew",
"type": "boolean"
}
]
}
My main goal is to have a function that takes such an object as input and extract only some particular fields and produce a subSet of this schema.
So a function that looks something like this reduceSchema(avroSchema, [paths])
For example the:
function reduceSchema(avroSchemaOnTop, ['ownerId', 'details.engine.size']
And then this would produce the desired output:
{
"type":"record",
"namespace":"company.car.v1",
"name":"CarV1",
"fields":[
{
"name":"ownerId",
"type":"string"
},
{
"name":"details",
"type":{
"type":"record",
"name":"DetailsV1",
"fields":[
{
"name":"engine",
"type":{
"type":"record",
"name":"EngineV1",
"fields":[
{
"name":"size",
"type":"int",
"default":0
}
]
}
}
]
},
"default":{}
}
]
}
Currently I am able to attach a property keepThisField to every field (and its parent-tree) that I want to keep, in this case the details.engine.size and ownerId
{
"type": "record",
"namespace": "company.car.v1",
"name": "CarV1",
"fields": [
{
"name": "plateNumber",
"type": "string"
},
{
"name": "ownerId",
"type": "string",
"keepThisField": "true"
},
{
"name" : "details",
"keepThisField": "true"
"type" : {
"type" : "record",
"name" : "DetailsV1",
"fields" : [
{
"name": "engine",
"type": {
"type": "record",
"name": "EngineV1",
"fields": [
{
"name": "size",
"type": "int",
"default": 0,
"keepThisField": "true"
},
{
"name": "valvesCount",
"type": "int",
"default": 0
}
]
}
},
{
"name" : "color",
"type" : "string",
"default" : "NONE"
},
{
"name" : "rimSize",
"type" : "int",
"default" : "NONE"
}
]},
"default" : {}
},
{
"name": "isBrandNew",
"type": "boolean"
}
]
}
What I need now is a mechanism to be able to remove all other fields (in a deeply nested manner) that don't have the property keepThisField in them, and then after the keepThisField property itself. So we are left with the desired output.
Anyone have an idea how one can achieve the removal process in a generic manner in JavaScript?
Update:
This is what I have tried with the flatMap:
function fn(o) {
const hasMore = _.get(o, 'type.fields');
if (o.keepThisField === true) {
if (hasMore) {
const retObj = {
...o,
type: {
...o.type,
fields: _.flatMap(o.type.fields, fn),
}
};
_.unset(retObj, 'keepThisField');
return retObj;
}
const cpO = o;
_.unset(cpO, 'keepThisField');
return ({
...cpO,
});
}
return [];
}
parentSchema.fields = _.flatMap(parentSchema.fields, fn)
[Answering my own question]
A potential solution for the removal part of the process. Credits to #Kinglish for the reference to this Stack Overflow question to use the flatMap.
function fn(o) {
const hasMore = _.get(o, 'type.fields');
if (o.keepThisField === true) {
if (hasMore) {
const retObj = {
...o,
type: {
...o.type,
fields: _.flatMap(o.type.fields, fn),
}
};
_.unset(retObj, 'keepThisField');
return retObj;
}
const cpO = o;
_.unset(cpO, 'keepThisField');
return ({
...cpO,
});
}
return [];
}
parentSchema.fields = _.flatMap(parentSchema.fields, fn)

validate json object with varying properties

I have json object whose property values are unique and can be anything;
{
"cat1": {
"name": "kitty",
"type": "animal",
"color": "ginger"
},
"dog2": {
"name": "ripple",
"type": "animal",
"color": "black"
},
"book10": {
"name": "myBook",
"type": "book",
"color": "NA"
},
"orange6": {
"name": "NA",
"type": "fruit",
"color": "orange"
},
"pig1":{
"name": "spring",
"type": "animal",
"color": "pink"
}
}
Now I'm confused how to write its validation schema. Does anybody know how to do it?
var mySchema = {
"type": "object",
"properties": {
// no idea how to check varying properties like cat1, dog2, etc. which might change next time
}
}
You can try this
var mySchema = {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"name": { "type": "string"},
"type": { "type": "string"},
"color": { "type": "string"},
}
}
}
ref: JSONSchema how to define a schema for a dynamic object

Angular Schema Form - Require Field if Integer Field is Not Null

I am trying to create a form using the Angular JSON Schema Form. I want one field (dropdown1) to be required when another field (number1) is populated. I am able to get the following form + schema working in the SchemaForm.io Sandbox, but when I put it into my site, the dropdown field is missing.
Here is my schema:
{
"type": "object",
"properties": {
"isChecked": {
"title": "checked?",
"type": "boolean"
},
"text1": {
"title": "text1",
"type": "number",
"minimum": 0,
"maximum": 100,
"condition": "model.isChecked"
},
"number1": {
"title": "number1",
"type": "number",
"minimum": 0,
"condition": "model.isChecked"
},
"dropdown1": {
"title": "",
"type": "string",
"enum": ["Days", "Months", "Years"],
"condition": "model.isChecked"
},
"comment": {"type": "string", "Title": "Comment"}
}
}
Here is my form:
[
"isChecked",
{
"key": "text1",
"type": "decimal",
"validationMessages": {
"maximum": "text1 must be 100 or lower.",
"minimum": "text1 must be 0 or higher.",
"required": "This field is required"
},
"required": false
},
{
"key": "number1",
"type": "number",
"validationMessages": {
"minimum": "number1 must be 0 or higher."
},
"required": false
},
{
"key": "dropdown1",
"required": false,
"condition": "model.number1 === null"
},
{
"key": "dropdown1",
"required": true,
"condition": "model.number1 > 0",
"validationMessages": {
"required": "dropdown1 is required."
}
},
{
"key": "comment",
"type": "textarea"
}
]
You can use the "dependencies" property (https://json-schema.org/understanding-json-schema/reference/object.html#property-dependencies). The example requires billing_address to be present when a credit_card is provided:
{
"type": "object",
"properties": {
"credit_card": {
"type": "number"
},
"billing_address": {
"type": "string"
}
},
"dependencies": {
"credit_card": [
"billing_address"
]
}
}
This implementation supports "dependencies":
https://github.com/dashjoin/json-schema-form
You can check out the online example here:
https://dashjoin.github.io/#/example/dependencies
Here is the solution I found:
"condition": {
"functionBody": "return model.number1 === undefined && model.isChecked"
}

Loopback query with where inside include

I am trying to query my data the following way:
const filter = {where: {activity_id: activity_id, include: {relation: 'planGoal', scope: {where: {activity_goal_id: goal_id}}}}};
However this doesn't seem to work only the activity_id filter is applied and the wrong data is returned.
So my question is how do I query data within an include? and is it even possible?
For reference, here are the models in question:
{
"name": "plan_goal_has_action_option",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"id": {
"type": "number"
},
"activity_id": {
"type": "number"
},
"plan_has_goal_id": {
"type": "number"
},
"action_option": {
"type": "string"
}
},
"validations": [],
"relations": {
"planGoal": {
"type": "belongsTo",
"model": "plan_has_goal",
"foreignKey": "plan_has_goal_id"
}
},
"acls": [],
"methods": {}
}
{
"name": "plan_has_goal",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"id": {
"type": "number"
},
"activity_id": {
"type": "number"
},
"activity_goal_id": {
"type": "number"
}
},
"validations": [],
"relations": {
"goal": {
"type": "belongsTo",
"model": "activity_goal",
"foreignKey": "activity_goal_id"
},
"actions": {
"type": "hasMany",
"model": "plan_goal_has_action_option",
"foreignKey": "plan_has_goal_id"
}
},
"acls": [],
"methods": {}
}
include and where are two different filters:
{
where: {
activity_id: activity_id
},
include: {
relation: 'planGoal',
scope: {
where: {
activity_goal_id: goal_id
}
}
}
}

Unable to Implement angular-schema-form-signature

I'm unsure as to why an input field shows on the form when implementing this code.(ref: https://github.com/JamesGuthrie/angular-schema-form-signature)
This is my Schema:
{ "type": "object", "properties": { "signature": { "type": "string" } } }
This is my Form:
[
"name",
{
"type": "tabs",
"tabs": [
{
"title": "Tab1",
"tabType": "top",
"items": [
{
"title": "Signature",
"key": "signature",
"type": "signature",
"width": 500,
"height": 100,
"resetButtonText": "Clear signature"
}
]
}
]
}
]

Categories

Resources