[DECLINED] Squidex's JSON Schema

Hi,
We want use Squidex for our backend to communicate with other microservices. Can we have an option to display Squidex`s schema as https://json-schema.org/. It’ll help us a lot to interpolate the schema with other programming languages.

As of now I’m reading the source code so I can contribute to the project ASAP.

Thanks,

OpenAPI is JSON schema, isn’t it?

Sorry, I didn’t explain it very well.
Here’s a sample for JSON schema: http://json-schema.org/learn/miscellaneous-examples.html

i.e. “$schema” keyword SHOULD be used in a resource root schema according to http://json-schema.org/latest/json-schema-core.html#rfc.section.8.1.1.

Also, it has specific syntax to deal with validation. Required property should be defined root of the schema to an array of properties instead of mention it as isRequired per property.

Generating POJO,POCO classes is going to be easy from the schema since many serializers support it.

Here’s a sample for existing schema

{
....
"fields": [
        {
            "fieldId": 1,
            "name": "prop1",
            "isHidden": false,
            "isLocked": false,
            "isDisabled": false,
            "partitioning": "invariant",
            "properties": {
                "isUnique": false,
                "inlineEditable": false,
                "editor": "Input",
                "isRequired": true,
                "isListField": false,
                "isReferenceField": false,
                "fieldType": "String"
            }
]
....
}

Suggested solution : a flag in schema controller to return the mentioned schema.

Thanks,

But this is what OpenAPI is for.

e.g. the generic API: https://cloud.squidex.io/api/docs

e.g. a custom API for an app: https://cloud.squidex.io/api/content/squidex-website/docs

You can use NSwag or any other swagger tool to generate classes for it, and it also generates DTOs for JSON.

Event swagger code generator can’t help my current use case. There are other properties that I actually don’t need like the following:
“isUnique”: false,
“inlineEditable”: false
“editor”: “Input”

Something like OData’s “$select” or GraphQL can help us in this case. As far as I see from code, both of them are only supported for assets ,and contents. $select is not supported as of now in OData.

I think you mix up different things. A schema in Squidex defines the data structure and of course the schema has its own JSON-schema. This is what you see with isRequired.

The actual content for this schema has its own json schema, defined in OpenAPI spec, e.g: https://cloud.squidex.io/api/content/squidex-website/swagger/v1/swagger.json

So you can use code generator to generate the classes for your concrete content items.

I actually meant the structure “https://cloud.squidex.io/api/docs#operation/Schemas_GetSchemas” To be clear.

This is the current response for my “test” schema. I only need (fields=>name ,and field=> properties=>isRequired)

I can’t do it with current API ,and it can be solved if we have OData ,or GraphQL for GetSchemas.

{
    "scripts": {},
    "previewUrls": {},
    "fields": [
        {
            "fieldId": 1,
            "name": "prop1",
            "isHidden": false,
            "isLocked": false,
            "isDisabled": false,
            "partitioning": "invariant",
            "properties": {
                "isUnique": false,
                "inlineEditable": false,
                "editor": "Input",
                "isRequired": true,
                "isListField": false,
                "isReferenceField": false,
                "fieldType": "String"
            },
            "_links": {
                "update": {
                    "href": "/api/apps/app/schemas/test/fields/1",
                    "method": "PUT"
                },
                "hide": {
                    "href": "/api/apps/app/schemas/test/fields/1/hide",
                    "method": "PUT"
                },
                "disable": {
                    "href": "/api/apps/app/schemas/test/fields/1/disable",
                    "method": "PUT"
                },
                "lock": {
                    "href": "/api/apps/app/schemas/test/fields/1/lock",
                    "method": "PUT"
                },
                "delete": {
                    "href": "/api/apps/app/schemas/test/fields/1",
                    "method": "DELETE"
                }
            }
        },
        {
            "fieldId": 2,
            "name": "prop2",
            "isHidden": false,
            "isLocked": false,
            "isDisabled": false,
            "partitioning": "invariant",
            "properties": {
                "isUnique": false,
                "inlineEditable": false,
                "editor": "Input",
                "isRequired": false,
                "isListField": false,
                "isReferenceField": false,
                "fieldType": "String"
            },
            "_links": {
                "update": {
                    "href": "/api/apps/app/schemas/test/fields/2",
                    "method": "PUT"
                },
                "hide": {
                    "href": "/api/apps/app/schemas/test/fields/2/hide",
                    "method": "PUT"
                },
                "disable": {
                    "href": "/api/apps/app/schemas/test/fields/2/disable",
                    "method": "PUT"
                },
                "lock": {
                    "href": "/api/apps/app/schemas/test/fields/2/lock",
                    "method": "PUT"
                },
                "delete": {
                    "href": "/api/apps/app/schemas/test/fields/2",
                    "method": "DELETE"
                }
            }
        },
        {
            "fieldId": 3,
            "name": "propdae",
            "isHidden": false,
            "isLocked": false,
            "isDisabled": false,
            "partitioning": "invariant",
            "properties": {
                "editor": "DateTime",
                "isRequired": false,
                "isListField": false,
                "isReferenceField": false,
                "fieldType": "DateTime"
            },
            "_links": {
                "update": {
                    "href": "/api/apps/app/schemas/test/fields/3",
                    "method": "PUT"
                },
                "hide": {
                    "href": "/api/apps/app/schemas/test/fields/3/hide",
                    "method": "PUT"
                },
                "disable": {
                    "href": "/api/apps/app/schemas/test/fields/3/disable",
                    "method": "PUT"
                },
                "lock": {
                    "href": "/api/apps/app/schemas/test/fields/3/lock",
                    "method": "PUT"
                },
                "delete": {
                    "href": "/api/apps/app/schemas/test/fields/3",
                    "method": "DELETE"
                }
            }
        }
    ],
    "id": "95d39c7e-4c3d-43cc-8270-f543d1fb410a",
    "name": "test",
    "properties": {},
    "isSingleton": false,
    "isPublished": true,
    "createdBy": "subject:5d96248caa09684d50ad9ddd",
    "lastModifiedBy": "subject:5d96248caa09684d50ad9ddd",
    "created": "2019-10-03T16:42:52Z",
    "lastModified": "2019-10-03T17:00:52Z",
    "version": 5,
    "_links": {
        "self": {
            "href": "/api/apps/app/schemas/test",
            "method": "GET"
        },
        "contents": {
            "href": "/api/content/app/test",
            "method": "GET"
        },
        "unpublish": {
            "href": "/api/apps/app/schemas/test/unpublish",
            "method": "PUT"
        },
        "fields/order": {
            "href": "/api/apps/app/schemas/test/fields/ordering",
            "method": "PUT"
        },
        "update": {
            "href": "/api/apps/app/schemas/test",
            "method": "PUT"
        },
        "update/category": {
            "href": "/api/apps/app/schemas/test/category",
            "method": "PUT"
        },
        "update/sync": {
            "href": "/api/apps/app/schemas/test/sync",
            "method": "PUT"
        },
        "update/urls": {
            "href": "/api/apps/app/schemas/test/preview-urls",
            "method": "PUT"
        },
        "fields/add": {
            "href": "/api/apps/app/schemas/test/fields",
            "method": "POST"
        },
        "update/scripts": {
            "href": "/api/apps/app/schemas/test/scripts",
            "method": "PUT"
        },
        "delete": {
            "href": "/api/apps/app/schemas/test",
            "method": "DELETE"
        }
    }
}

But where is the big advantage of having only these fields?

OData will probably never happen. I only use the filter system because I did not want to write my own parser and GraphQL is too much effort. I cannot maintain two APIs.

I think I finally go your requirement. You want to have an endpoint to get the JSON-schema of a schema. There is already code for OData, it should be possible to reuse hat:

Advantages:

  • Smaller foot print. I have a schema with 27 properties, so you can imagine the size of json.
  • Simplifying the serialization process.
  • There`re properties only usable for UI. Since my case is server-to-service communication.

I believe the json-schema can help a lot of users to process the schema.

Why can you not use OpenAPI? I am still a little bit confused.

Looking into it as of now. Posted it before reading your reply.

1 Like

Hi Sebastian,
I’ve tried to get DTOs from swagger.json using NSWAG as you suggested ,but it returns each property as class(because of the partitions). What I’m requesting is returning each property as primitive type/object without using any resolver.

by calling http://localhost:5000/api/apps/app/schemas/test?output=jsonschema for example.

I’ll get the following schema:

{
“title”: “Test”,
“type”: “object”,
“properties”: {
“p1”: {
“type”: “string”
}
}
}

The API provides the feature to return different formats based on the X-Flatten and X-Languages parameter: https://docs.squidex.io/concepts/localization#x-flatten-header

Perhaps we can describe this in the Swagger Docs somehow.

1 Like

I just knew about “X-Flatten”. It looks like that it only works for getting content. I can’t create/update content using it, right? It’d be a nice feature, If I can use it for that :wink:.

Yes, but it would like to some problems in other cases, e.g. validation.

Can I know the reason behind the invariant partition?

Sorry, the answer was in the link.

Thanks.

Hi, I found the following:

Can I have different responses based on a request parameter? Such as:

GET /something -> {200, schema_1}
GET /something?foo=bar -> {200, schema_2}

In OpenAPI 3.0, you can use oneOf to specify alternate schemas for the response and document possible dependencies verbally in the response description . However, there is no way to link specific schemas to certain parameter combinations.

From: https://swagger.io/docs/specification/describing-responses/

So it does not seem to work, but we could provide an endpoint for that:

If you want to provide a PR: The code to generate a JSON schema is here: https://github.com/Squidex/squidex/blob/master/src/Squidex.Domain.Apps.Core.Operations/GenerateJsonSchema/JsonSchemaExtensions.cs