Include `meta` member in resource identifiers in `relationships` object - active-model-serializers

From my understanding of the JSON:API spec (specifically https://jsonapi.org/format/#document-resource-object-linkage) I should be able to include meta members for each member of a relationship.
I have been able to add a hash of meta data to the relationships object itself, but not one to each of the individual relationships within.
class PlanSerializer < ApplicationSerializer
attributes :id, :name
has_many :features do
meta value: "x"
end
end
I know I can use a block syntax for has_many, and think that's the way to achieve this. But I haven't got it working. Calling the meta method within the block adds the meta block to the features relationship object, and I need to add one to each entry in that array.
My questions:
Have I understood the spec correctly? Should I be able to add a meta object to each relationship?
How would I go about doing this with the active model serializers?
Background:
My goal is to represent a many-many from Plans to Features where each plan might have some extra information for it's own relationship to a given Feature (and that information is different for every Plan, so it doesn't belong on the Feature object)
If your answer is that I shouldn't be doing this, that's fine, but please present an alternative which you think is preferred.
// My desired output
{
"data": [
{
"id": "small",
"type": "plans",
"attributes": {
/* Some attributes */
},
"relationships": {
"features": {
"data": [
{
"id": "num-users",
"type": "features",
"meta": {
"value": 1
}
},
{
"id": "num-projects",
"type": "features",
"meta": {
"value": 5
}
}
]
}
}
},
{
"id": "large",
"type": "plans",
"attributes": {
/* Some attributes */
},
"relationships": {
"features": {
"data": [
{
"id": "num-users",
"type": "features",
"meta": {
"value": 5
}
},
{
"id": "num-projects",
"type": "features",
"meta": {
"value": 50
}
},
{
"id": "unlimited-downloads",
"type": "features"
}
]
}
}
}
],
"included": [
{
"id": "num-users",
"type": "features",
"attributes": {
"description": "Number of users"
}
},
{
"id": "num-projects",
"type": "features",
"attributes": {
"description": "Number of projects"
}
},
{
"id": "unlimited-downloads",
"type": "features",
"attributes": {
"description": "Unlimited downloads"
}
}
]
}

Related

OpenAPI type schemas are written to the Swagger type field

My question is in regard to API schemas in Azure API Management, resource type Microsoft.ApiManagement/service/apis/schemas.
A schema created through the Azure Portal is created with the content type application/vnd.oai.openapi.components+json and written to document/components/schemas, which is the correct path for schema definitions in an OpenAPI definition.
Sample:
{
"value": [
{
"id": "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.ApiManagement/service/xxx/apis/xxx/schemas/1629566051926",
"type": "Microsoft.ApiManagement/service/apis/schemas",
"name": "1629566051926",
"properties": {
"contentType": "application/vnd.oai.openapi.components+json",
"document": {
"components": {
"schemas": {
"patch-components-id-request-1": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"documentationURL": {
"type": "string"
},
"iacURL": {
"type": "string"
},
"duration": {
"type": "integer"
},
"statusID": {
"type": "integer"
},
"owner": {
"type": "string"
}
}
}
}
}
}
}
}
],
"count": 1
}
A schema created through the REST API or the Go SDK is set using properties.document.definitions, which leads to it being written to document/definitions, no matter the contentType.
Sample:
{
"value": [
{
"id": "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.ApiManagement/service/xxx/apis/marble-dev-fctn/schemas/marbleschemas",
"type": "Microsoft.ApiManagement/service/apis/schemas",
"name": "marbleschemas",
"properties": {
"contentType": "application/vnd.oai.openapi.components+json",
"document": {
"definitions": {
"patch-components-id-request-1": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"documentationURL": {
"type": "string"
},
"iacURL": {
"type": "string"
},
"duration": {
"type": "integer"
},
"statusID": {
"type": "integer"
},
"owner": {
"type": "string"
}
}
}
}
}
}
}
],
"count": 1
}
When setting the contentType to application/vnd.ms-azure-apim.swagger.definitions+json this is fine, but when setting the contentType to application/vnd.oai.openapi.components+json two problems arise: The definition will not be included when exporting the OpenAPI Schema (at least when exporting from the Azure Portal, i have not tried any other way), since definitions is not a valid OpenAPI field, and it will not be shown in the Developer Portal.
As far as i understand it the definitons would need to be written to document/components/schemas for application/vnd.oai.openapi.components+json and document/definitions for application/vnd.ms-azure-apim.swagger.definitions+json.
I can import the definition to the correct path by setting it manually in an ARM Template or REST API call, but i am working on getting the terraform resource to work correctly, which is relying on the Go SDK. There might be a workaround for that as well but i would really like to find out if my understanding is wrong or if there might be a problem here.

JSON schema validation Draft 7 two type of data for one field

I need help creating a JSON schema for a value that could be an object, or an array of objects.
lib: jsonschema==3.2.0
py: 3.8
I have 2 responses from the server:
first:
{
"result": [
{
"brand": "Test"
}
]}
second:
{
"result":
{
"brand": "Test"
}
}
As you can see the difference between both in the first case its an array of obj the second just object.
my schema:
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "http://example.com/example.json",
"type": "object",
"required": [
"result"
],
"properties": {
"result": {
"$id": "#/properties/result",
"type": ["array", "object"],
"additionalItems": true,
"items": {
"$id": "#/properties/result/items",
"anyOf": [
{
"$id": "#/properties/result/items/anyOf/0",
"type": "object",
"required": [
"brand"
],
"properties": {
"brand": {
"$id": "#/properties/result/items/anyOf/0/properties/brand",
"type": "string"
}
},
"additionalProperties": true
}
]
}
}
},
"additionalProperties": true}
In the first case when return array, it checks the "brand" type on the second when return object, no.
How I can set up 2 types for one field "result" that it could check the brand type?
Your schema can be fixed as follows:
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "http://example.com/example.json",
"type": "object",
"required": [
"result"
],
"properties": {
"result": {
"$id": "#/properties/result",
"anyOf": [
{
"$id": "#/properties/result/items/brand",
"type": "object",
"properties": {
"brand": {
"$id": "#/properties/result/items/anyOf/0/properties/brand",
"type": "string"
}
},
"required": [
"brand"
],
"additionalProperties": true
},
{
"$id": "#/properties/result/items/array",
"type": "array",
"items": {
"$ref": "#/properties/result/items/brand"
}
}
]
}
},
"additionalProperties": true
}
Demos here, here and here.
However, it is customary to extract reusable portions of a schema into a separate "definitions" section, like so:
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "http://example.com/example.json",
"definitions": {
"brand": {
"type": "object",
"properties": {
"brand": {
"$id": "#/properties/result/items/anyOf/0/properties/brand",
"type": "string"
}
},
"required": [
"brand"
],
"additionalProperties": true
}
},
"type": "object",
"required": [
"result"
],
"properties": {
"result": {
"$id": "#/properties/result",
"anyOf": [
{
"$ref": "#/definitions/brand"
},
{
"$id": "#/properties/result/items/array",
"type": "array",
"items": {
"$ref": "#/definitions/brand"
}
}
]
}
},
"additionalProperties": true
}
Demos here, here and here.
Notes:
To express that the property "result" may be of two different types, use the "anyof" keyword for the property's schema. The value of the "anyOf" should be an array with the schemas for each possible type (here the "brand" object or an array of "brand" objects) as the array items.
See: Multiple Types.
To avoid duplicating the definitions for the "brand" object, you can use the "$ref" when defining a schema for the array's items to refer back to the previously given schema for "brand". As noted above it s customary to place reused subschemas into a "definitions" section, but it is not necessary, "$ref" can refer to any schema item via the JSON Pointer syntax.
See: Reuse.
When the items of a list have a single schema, "additionalItems" should not be used.
See: List validation.

Do Azure Logic Apps support oneOf, anyOf in JSON schema validation

I was trying to add JSON schema validation with in a Logic App using ParseJSON action.
I want to validate the existence of either of the object in the message (equivalent to XSD choice).
For instance, messages may have either of lastname or familyname.
{
"name": "Alan",
"familyname": "Turing"
}
Or
{
"name": "Alan",
"lastname": "Turing"
}
I modified the generated schema as,
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"oneOf": [
{
"lastname": {
"type": "string"
}
},
{
"familyname": {
"type": "string"
}
}
]
}
}
Logic App execution throws below error
Just to test if any other schema combination keywords works, tried to test with anyOf in place of oneOf and it fails in execution as well.
Does Logic Apps support these extended validation ? Am I missing some specific syntax here ?
If you are validating that either familyname or lastname be present then you are missing the "required" attribute.
{
"type": "object",
"properties": {
"name": {
"type": "string"
}
},
"oneOf": [
{
"familyname": {
"type": "string"
},
"required": [ "familyname" ]
},
{
"lastname": {
"type": "string"
},
"required": [ "lastname" ]
}
]
}
This will validate the JSON. If you want to get the values out in a later step you could use the coalesce function.
#coalesce(actionBody('Parse_JSON')?['familyname'], actionBody('Parse_JSON')?['lastname'])

Missing Subscription Key field in Swagger API connector (trough Azure API Management) in Logic App

I have created a REST API with a Swagger/OPEN API specification which I will like to consume trough a Azure API Management tenant in a Logic App.
When I download the specification it looks like this:
{
"swagger": "2.0",
"info": {
"title": "Leasing",
"version": "1.0"
},
"host": "ENDPOINT.azure-api.net",
"basePath": "/leasing",
"schemes": [
"http",
"https"
],
"securityDefinitions": {
"apiKeyHeader": {
"type": "apiKey",
"name": "Ocp-Apim-Subscription-Key",
"in": "header"
},
"apiKeyQuery": {
"type": "apiKey",
"name": "subscription-key",
"in": "query"
}
},
"security": [
{
"apiKeyHeader": []
},
{
"apiKeyQuery": []
}
],
"paths": {
"/{Brand}/groups": {
"get": {
"description": "Get a list of leasing groups on a brand",
"operationId": "GetGroups",
"parameters": [
{
"name": "Brand",
"in": "path",
"description": "Selection of possible brands",
"required": true,
"type": "string",
"enum": [
"Volkswagen",
"Audi",
"Seat",
"Skoda",
"VolkswagenErhverv",
"Porsche",
"Ducati"
]
}
],
"responses": {
"200": {
"description": "Returns a list of leasing groups",
"schema": {
"$ref": "#/definitions/GroupArray"
}
},
"400": {
"description": "If the brand is not valid",
"schema": {
"$ref": "#/definitions/Error"
}
}
},
"produces": [
"application/json"
]
}
}
},
"definitions": {
"Group": {
"type": "object",
"properties": {
"id": {
"format": "int32",
"type": "integer"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"leasingModelCount": {
"format": "int32",
"type": "integer"
},
"lowestMonthlyFee": {
"format": "int32",
"type": "integer"
}
}
},
"Error": {
"type": "object",
"properties": {
"code": {
"enum": [
"NotValidBrand",
"NotValidGroupId"
],
"type": "string",
"x-ms-enum": {
"name": "ErrorCode",
"modelAsString": true
}
},
"message": {
"type": "string"
}
}
},
"GroupArray": {
"type": "array",
"items": {
"$ref": "#/definitions/Group"
}
}
}
}
When I add this in a Logic App with the connector HTTP + Swagger I only get to define the {Brand} query input but not the various ways of using the Subscriptions key (header or query) as defined in SecurityDefiniations.
The whole securityDefinitions and security section are automatically generated in the Azure API Management service, but not recognized in Logic App.
See image of missing subscription key field:
What am I doing wrong?
Update
I have tried the following:
Usage of the 'Authentication' field (but this field is limited to certain types of auths flows https://learn.microsoft.com/en-us/azure/connectors/connectors-native-http#authentication)
Change the Logic App 'Http + Swagger'-action in code to add the header parameter, but this action converts the action to a simple 'Http' action and therfore loosing the automatic schema generation from Swagger.
I think you need to specify this in the Authentication-field in a JSON format. Something like:
{
"apiKeyHeader" : "your Ocp-Apim-Subscription-Key",
"apiKeyQuery" : "your subscription key"
}

Is it possible to define a parameter set and reference it?

I have multiple parameters that I want to reference, but I do not want to specify them one by one.
This snippet does not make the parameters show up:
{
...
"paths": {
"/stuff": {
"get": {
"description": "Gets stuff",
"operationId": "getStuff",
"parameters": {
"$ref": "#/definitions/set1"
}
}
}
},
"parameters": {
"a": {
"name": "a",
"in": "query",
"description": "Param A",
"required": false,
"type": "string"
},
"b": {
"name": "b",
"in": "query",
"description": "Param B",
"required": false,
"type": "string"
}
},
"definitions": {
"set1": [
{
"$ref": "#/parameters/a"
},
{
"$ref": "#/parameters/b"
}
],
"set2": ...
}
}
Is this possible or do I have to specify each parameter like set1, for each request?
Indeed that's not a valid definition and as you suggested, you'd have to specify each parameter separately by referencing the global one. If your parameters are shared for all operations under a specific path, you can define those at the path level and they would be applied to all operations.
For an individual operation, you'd define it as:
"paths": {
"/stuff": {
"get": {
"description": "Gets stuff",
"operationId": "getStuff",
"parameters": [
{
"$ref": "#/parameters/a"
},
{
"$ref": "#/parameters/b"
}
]
}
}
}

Resources