Freeform subobject in json-schema - openapi

I am drafting an API documentation with swagger.io and is trying to make it fit to our use case. The system is going to receive and process data from all sources, and they would each have different sets of fields.
While the product of the processing share the same schema, we want to include the input in the schema too for reference purpose. For instance, given
{
"foo": "bar"
"bar": "baz"
}
The product of the processing is
{
"original": {
"foo": "bar",
"bar": "baz"
}
"processed": {
"stdFieldA": "bar",
"stdFieldB": "baz"
}
}
Assuming for each input from different sources, we end up having stdFieldA and stdFieldB. So the response schema object we have
type: object
properties:
processed:
type: object
properties:
stdFieldA:
type: string
stdFieldB:
type: string
now that we have the processed subobject defined, can we define a freeform object for the original input, so that this object coming from another source is valid
{
"alpha": "lorem",
"beta": "ipsum"
}
If I don't get any answer to this, my workaround to the problem would be storing the original input as string (convert the original input into JSON string).

type: object without properties describes a free-form object. So the response schema can be:
type: object
properties:
original:
type: object # <----------
processed:
type: object
properties:
stdFieldA:
type: string
stdFieldB:
type: string

Related

Why are the OpenAPI 3 oneOf example objects not always valid against both schemas

The specification for the oneOf keyword in https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/#oneof has an example schema as well as three example objects. The request body is oneOf the Dog or Cat schema:
Dog:
type: object
properties:
bark:
type: boolean
breed:
type: string
enum: [Dingo, Husky, Retriever, Shepherd]
Cat:
type: object
properties:
hunts:
type: boolean
age:
type: integer
And the three JSON objects:
Object 1
{
"bark": true,
"breed": "Dingo"
}
Object 2
{
"bark": true,
"hunts": true
}
Object 3
{
"bark": true,
"hunts": true,
"breed": "Husky",
"age": 3
}
They say, the first object is valid against one of the schemas (I assume they mean the Dog schema), the second one is not valid against both schemas and the third is valid against both schemas. This means the second and third object are not correct request bodies.
But why are not all three objects valid against both schemas? None of the keywords for objects mentioned here http://json-schema.org/draft/2020-12/json-schema-validation.html#name-validation-keywords-for-obj are used. And the types of the properties match for all the objects.
I played with this validator https://www.jsonschemavalidator.net/ and wrote also a simple schema with an object with some properties and even an empty json validates against the schema.
It seems that my assumptions are correct and all objects are valid against the oneOf schema. I found an issue reporting this: https://github.com/swagger-api/swagger.io/issues/253.

How to specify an unknown number of a parameter in the response Open API

I'm trying to model my API using swagger and the Open API 3.0 specification. I have made some schemas and now I'm modeling the response of my endpoints. The problem is that they return something like this:
[
{
"name": "this attribute is always here"
"type1": { "description": "this maybe appear or not" },
"type2": { "description": "this maybe appear or not" },
...
"typeN": { "description": "N is not a fixed number, it may range from 0 to another positive integer" },
}
]
I know how to model the array and the object (with the name property). The problem comes when I have to model the typeX properties, I do not know how to specify that they are optional and the number of ocurrences is variable. Any idea?
This object is basically a string-to-object dictionary/hashmap with an extra name property. Fixed properties are defined in properties, and the dictionary part can be defined using either patternProperties (in OpenAPI 3.1) or additionalProperties (in OpenAPI 3.0 and 2.0).
OpenAPI 3.1
In OAS 3.1 your object can be defined as follows. Since the optional property names all follow the typeX format, the schema uses patternProperties to define the regex for the property names.
MyObject:
type: object
required: [name]
properties:
name:
type: string
patternProperties: # <-- This part defines the "typeX" properties
^type\d+$: # <-- Property name regex
type: object # <-- Property value
properties:
description:
type: string
additionalProperties: false # No other properties other than "name" and "typeX"
OpenAPI 3.0 and 2.0
In earlier OAS versions, you use additionalProperties to define "may have extra properties with <such> values", but there's no way to define the format of those property names. You can however mention the property name format in the schema description and also add a schema example for documentation purposes.
MyObject:
type: object
description: >-
In addition to the `name` property, this object may have an arbitrary
number of properties named `typeX` where X is a positive integer.
required: [name]
properties:
name:
type: string
additionalProperties:
# This part defines the *value* of the typeX properties
type: object
properties:
description:
type: string
# Optional schema example
name: something
type1:
description: ....
type2:
description: ....

Does using dbref do anything more than just storing an `id`

My Mongoose schema:
// set up the schema
var CategorySubSchema = new Schema({
name: { type: String },
_category_main : { type: String, ref: 'CategoryMain' }
},
And my controller code:
CategorySub.create({
name : req.body.name,
_category_main : req.body.category_main
}, function(err, data){
An entry in my db:
{
"_id": "54dd163434d78ae58f6b1a69",
"name": "Snacks",
"_category_main": "54dcf4a71dfecb4d86ddcb87",
"__v": 0
},
So I used an underscore, because I was following an example. Does this mean anything to the database or is it just convention for references?
Also, instead of passing the entire JSON object in the request - req.body.category_main, why not just pass and id and change my schema to this?:
var CategorySubSchema = new Schema({
name: { type: String },
category_main_id : { type: String }
},
In short, Yes.
The below schema definition is an example of Manual references.
var CategorySubSchema = new Schema({
name: { type: String },
category_main_id : { type: String }
}
where,
you save the _id field of one document in another document as a
reference. Then your application can run a second query to return the
related data. These references are simple and sufficient for most use
cases.
In this case, we need to write explicit application code to fetch the referred document and resolve the reference. Since the driver that we use wouldn't know about the collection in which the referred document is present nor the database in which the referred document is present.
When you define the schema as below, this is an example of storing the details of the referred document .(Database references)
var CategorySubSchema = new Schema({
name: { type: String },
_category_main : { type: String, ref: 'CategoryMain' }
}
They include the name of the collection, and in some cases the
database name, in addition to the value from the _id field.
These details allow various drivers to resolve the references by themselves, since the name of the collection and the database(optional) of the referred document would be contained in the document itself, rather than we writing explicit application code to resolve the references.
So I used an underscore, because I was following an example. Does this mean anything to the database or is it just convention for
references?
Using underscore in the _id field is a valid naming convention, but mongoDb doesn't explicitly mention about the naming convention of other fields which are used to resolve references. You could just use any other field name as long as it conforms to this.

MongoDB Object data type won't save with defined schema

Using Mongo and Meteor with CoffeeScript, I'm trying to save a document with one Object:
Test = new SimpleSchema(
tag:
type: Object
)
And the insert:
test1 = new Meteor.Collection("test", { schema: Test})
test1.insert({ tag: {"name": "campus"} })
Result: a document gets saved in the database but the "tag" field is never set.
Couple of different troubleshooting steps I've taken:
Changing the data type to String works and the "tag" field gets set. However, I want to reference a tag property without having to parse the string every time.
Adding a collection without the schema saves the Object exactly how I want:
test2 = new Meteor.Collection("test2")
test2.insert({ tag: {"name": "campus"} })
EDIT: Fixed using the blackbox: true flag. See below answer for clarification.
Test = new SimpleSchema(
tag:
type: Object
blackbox: true
)
According to SimpleSchema docs, all defined properties must pass validation. So any Object data type without properties is treated as an empty Object unless you add the blackbox: true flag.
Source: http://atmospherejs.com/aldeed/simple-schema#blackbox
If you have a key with type Object, the properties of the object will be validated as well, so you must define all allowed properties in the schema. If this is not possible or you don't care to validate the object's properties, use the blackbox: true option to skip validation for everything within the object.
I use simple schema and create my models of the following way, and I don't have any problem.
Test = new Meteor.Collection("test", {
schema: new SimpleSchema({
ownerId: {
type: String,
},
dateAdd: {
type: Date,
}
})
})
Test.insert({ownerId:"123",dateAdd:"..."})
In Coffee Script
Test = new Meteor.Collection("test",
schema: new SimpleSchema(
ownerId:
type: String
dateAdd:
type: Date
)
)

Swagger response class Map

I have a REST API that returns, essentially a Map of (String, Object) where Object is either
A custom bean (let's say class Bean) or
A List of elements, all of type Bean
In JSON, this translates very well to:
{
"key1":{
"val1":"some string",
"val2":"some other string",
"val3":"another string"
},
"key2":[
{
"val1":"some string",
"val2":"some other string",
"val3":"another string"
},
{
"val1":"some string",
"val2":"some other string",
"val3":"another string"
}
]
}
Via swagger annotations, is there a way to specify this kind of a dynamic Map as the response class?
Thanks
I read the 'Open API Specification' - 'Add support for Map data types #38' page. As far as I understand, it recommends to use additionalProperties, but I haven't managed to make it work with Swagger UI 2.1.4 (see my related question: Swagger: map of string, Object).
I have found the following work-around: define an object, with one property that is the key, and with an inner object as the value for the "key" property.
The display in Swagger UI is correct, but one does not see it is a map, so it is then needed to explain in the comment that this is actually a map.
In your case, I find it a bit weird to have once a Bean, and once a list of Beans: I would find it more logical to have an array of one Bean in the first case.
Still, you could do, for example:
your_property: {
description: "This is a map that can contain several objects indexed by different keys. The value can be either a Bean or a list of Beans.",
type: object,
properties: {
key_for_single_bean: {
description: "The value associated to 'key_for_single_bean' is a single Bean",
$ref: "#/definitions/Bean"
},
key_for_list_of_beans: {
description: "The value associated to 'key_for_list_of_beans' is an array of Beans",
type: array,
items: {$ref: "#/definitions/Bean"}
}
}
}