├── .github ├── FUNDING.yml └── workflows │ ├── ci.yml │ ├── docker.yml │ └── typehub.yml ├── Dockerfile ├── LICENSE ├── README.md ├── docker-compose.yml ├── specification └── typeschema.json ├── tests ├── README.md ├── level_1_format.json ├── level_1_simple.json ├── level_2_array_inline_reference.json ├── level_2_array_inline_string.json ├── level_2_array_reference.json ├── level_2_array_string.json ├── level_2_map_inline_reference.json ├── level_2_map_inline_string.json ├── level_2_map_reference.json ├── level_2_map_string.json ├── level_3_inheritance.json ├── level_4_generic.json ├── level_5_discriminator.json └── sdkgen.json └── www ├── .env ├── build.php ├── cache └── empty ├── composer.json ├── composer.lock ├── configuration.php ├── container.php ├── psalm.xml ├── public ├── .htaccess ├── css │ ├── app.css │ ├── app.min.css │ ├── bootstrap.min.css │ └── highlight.min.css ├── img │ ├── favicon.ico │ ├── github-32.png │ ├── logo.png │ ├── typeschema_flow.png │ └── typeschema_structure.png ├── index.php └── js │ ├── app.min.js │ ├── bootstrap.min.js │ ├── highlight.min.js │ ├── jquery.min.js │ └── popper.min.js ├── resources ├── container.php ├── examples │ ├── discriminator.json │ ├── generic.json │ ├── import.json │ ├── inheritance.json │ ├── map.json │ ├── map_inline.json │ ├── reference.json │ └── simple.json ├── template │ ├── developer.php │ ├── ecosystem.php │ ├── example.php │ ├── generator.php │ ├── generator │ │ └── form.php │ ├── history.php │ ├── inc │ │ ├── footer.php │ │ └── header.php │ ├── index.php │ ├── integration.php │ ├── integration │ │ ├── csharp_description.html │ │ ├── csharp_dto.txt │ │ ├── csharp_integration.txt │ │ ├── detail.php │ │ ├── go_description.html │ │ ├── go_dto.txt │ │ ├── go_integration.txt │ │ ├── java_description.html │ │ ├── java_dto.txt │ │ ├── java_integration.txt │ │ ├── php_description.html │ │ ├── php_dto.txt │ │ ├── php_integration.txt │ │ ├── python_description.html │ │ ├── python_dto.txt │ │ ├── python_integration.txt │ │ ├── rust_description.html │ │ ├── rust_dto.txt │ │ ├── rust_integration.txt │ │ ├── typescript_description.html │ │ ├── typescript_dto.txt │ │ ├── typescript_integration.txt │ │ ├── visualbasic_description.html │ │ ├── visualbasic_dto.txt │ │ └── visualbasic_integration.txt │ ├── specification.php │ ├── tools.php │ └── tools │ │ ├── changelog.php │ │ ├── json.php │ │ ├── jsonschema.php │ │ └── openapi.php └── typeschema.json └── src ├── Controller ├── Developer.php ├── Ecosystem.php ├── Example.php ├── Generator.php ├── History.php ├── Index.php ├── Integration.php ├── Specification.php ├── Tools.php └── Tools │ ├── Changelog.php │ ├── Json.php │ ├── JsonSchema.php │ └── OpenAPI.php ├── Model ├── Diff.php └── Generate.php └── Service ├── CaptchaVerifier.php └── TypeName.php /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | github: chriskapp 3 | patreon: fusio 4 | custom: https://www.paypal.me/fusioapi 5 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | - pull_request 4 | - push 5 | defaults: 6 | run: 7 | working-directory: www 8 | jobs: 9 | psalm: 10 | name: Psalm 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | - name: Install PHP 16 | uses: shivammathur/setup-php@v2 17 | with: 18 | php-version: 8.4 19 | coverage: none 20 | - name: Composer install 21 | run: composer install --no-interaction --no-ansi --no-progress 22 | - name: Run Psalm 23 | run: vendor/bin/psalm --no-progress --show-info=false --stats 24 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Docker 2 | on: 3 | push: 4 | branches: 5 | - 'master' 6 | tags: 7 | - 'v*' 8 | env: 9 | REGISTRY: ghcr.io 10 | IMAGE_NAME: ${{ github.repository }} 11 | jobs: 12 | push_to_registry: 13 | name: Push Docker image to Docker Hub 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: read 17 | packages: write 18 | steps: 19 | - name: Check out the repo 20 | uses: actions/checkout@v4 21 | - name: Log in to the Container registry 22 | uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 23 | with: 24 | registry: ${{ env.REGISTRY }} 25 | username: ${{ github.actor }} 26 | password: ${{ secrets.GITHUB_TOKEN }} 27 | - name: Extract metadata (tags, labels) for Docker 28 | id: meta 29 | uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 30 | with: 31 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 32 | - name: Build and push Docker image 33 | uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc 34 | with: 35 | context: . 36 | push: true 37 | tags: ${{ steps.meta.outputs.tags }} 38 | labels: ${{ steps.meta.outputs.labels }} 39 | -------------------------------------------------------------------------------- /.github/workflows/typehub.yml: -------------------------------------------------------------------------------- 1 | name: TypeHub 2 | on: 3 | push: 4 | branches: 5 | - 'master' 6 | tags: 7 | - 'v*' 8 | jobs: 9 | push_to_typehub: 10 | name: Push specification to TypeHub 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Check out the repo 14 | uses: actions/checkout@v4 15 | - name: TypeHub push 16 | uses: apioo/typehub-push-action@v0.1.0 17 | with: 18 | client_id: ${{ secrets.TYPEHUB_CLIENT_ID }} 19 | client_secret: ${{ secrets.TYPEHUB_CLIENT_SECRET }} 20 | directory: 'specification' 21 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.4-apache 2 | MAINTAINER Christoph Kappestein 3 | LABEL description="TypeSchema website" 4 | 5 | ENV COMPOSER_VERSION "2.5.5" 6 | ENV COMPOSER_SHA256 "566a6d1cf4be1cc3ac882d2a2a13817ffae54e60f5aa7c9137434810a5809ffc" 7 | 8 | ENV APACHE_DOCUMENT_ROOT "/var/www/html/public" 9 | 10 | # install default packages 11 | RUN apt-get update && apt-get -y install \ 12 | wget \ 13 | git \ 14 | cron \ 15 | libcurl3-dev \ 16 | libzip-dev \ 17 | libonig-dev 18 | 19 | # install php extensions 20 | RUN docker-php-ext-install \ 21 | bcmath \ 22 | curl \ 23 | zip \ 24 | mbstring 25 | 26 | # install composer 27 | RUN wget -O /usr/bin/composer https://getcomposer.org/download/${COMPOSER_VERSION}/composer.phar 28 | RUN echo "${COMPOSER_SHA256} */usr/bin/composer" | sha256sum -c - 29 | RUN chmod +x /usr/bin/composer 30 | 31 | # adjust apache config 32 | RUN sed -ri -e "s!/var/www/html!${APACHE_DOCUMENT_ROOT}!g" /etc/apache2/sites-available/*.conf 33 | RUN sed -ri -e "s!/var/www/!${APACHE_DOCUMENT_ROOT}!g" /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf 34 | RUN a2enmod rewrite 35 | 36 | # install app 37 | COPY www /var/www/html 38 | RUN cd /var/www/html && /usr/bin/composer install 39 | RUN chown -R www-data: /var/www/html 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017-2022 Christoph Kappestein 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TypeSchema 2 | 3 | ## About 4 | 5 | TypeSchema is a JSON specification to describe data models. 6 | 7 | A TypeSchema specification can be easily transformed into code for almost any programming language. 8 | This helps to reuse core data models in different environments. 9 | More information at: [https://typeschema.org](https://typeschema.org) 10 | 11 | The TypeSchema meta specification which describes the specification itself is located at [specification/typeschema.json](./specification/typeschema.json). 12 | We automatically push the specification to the [TypeHub platform](https://app.typehub.cloud/d/typehub/typeschema) where 13 | you can see also a rendered version of the specification and download the auto generated models. 14 | 15 | ## Features 16 | 17 | * An elegant specification optimized for code-generation 18 | * A portable format to share data models across different programming languages 19 | * Generate clean and simple to use DTOs 20 | * Handle advanced concepts like inheritance, polymorphism and generics 21 | * Use reflection to easily turn any class into a TypeSchema specification 22 | * Easily implement your own code generator 23 | 24 | ## Generator 25 | 26 | We provide a hosted version of the code generator at our [website](https://typeschema.org/generator). 27 | To transform a TypeSchema specification into code you can use our [generator](https://github.com/apioo/typeschema-generator) 28 | [docker image](https://hub.docker.com/r/apiootech/typeschema-generator). Simply run `docker-compose up` which reads the `typeschema.json` 29 | specification from the `output/` folder and writes the generated code back into this folder. 30 | 31 | ``` 32 | services: 33 | generator: 34 | image: apiootech/typeschema-generator:0.6 35 | environment: 36 | # possible formats: csharp, go, java, php, python, rust, typescript 37 | FORMAT: "java" 38 | NAMESPACE: "org.acme" 39 | SOURCE: "typeschema.json" 40 | volumes: 41 | - ./output:/usr/src/typeschema/output 42 | ``` 43 | 44 | For more advanced integration options please take a look at the [SDKgen](https://sdkgen.app/) project 45 | which offers various integration options like a CLI, GitHub action or REST API. 46 | 47 | ## Models 48 | 49 | TypeSchema provides auto-generated models which describe the specification itself. These models 50 | can be used if you want to work with a TypeSchema specification. 51 | 52 | | Language | GitHub | Package | 53 | |------------|----------------------------------------------------------------|---------------------------------------------------------------------| 54 | | C# | [GitHub](https://github.com/apioo/typeschema-model-csharp) | [Nuget](https://www.nuget.org/packages/TypeSchema.Model/) | 55 | | Go | [GitHub](https://github.com/apioo/typeschema-model-go) | | 56 | | Java | [GitHub](https://github.com/apioo/typeschema-model-java) | [Maven](https://central.sonatype.com/artifact/org.typeschema/model) | 57 | | JavaScript | [GitHub](https://github.com/apioo/typeschema-model-javascript) | [NPM](https://www.npmjs.com/package/typeschema-model) | 58 | | PHP | [GitHub](https://github.com/apioo/typeschema-model-php) | [Packagist](https://packagist.org/packages/typeschema/model) | 59 | | Python | [GitHub](https://github.com/apioo/typeschema-model-python) | [PyPI](https://pypi.org/project/typeschema-model/) | 60 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | typeschema: 4 | image: ghcr.io/apioo/typeschema:master 5 | environment: 6 | APP_URL: "https://typeschema.org" 7 | APP_ENV: "prod" 8 | APP_DEBUG: "off" 9 | restart: always 10 | ports: 11 | - "9090:80" 12 | -------------------------------------------------------------------------------- /specification/typeschema.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "DefinitionType": { 4 | "description": "Base definition type", 5 | "type": "struct", 6 | "base": true, 7 | "properties": { 8 | "description": { 9 | "description": "", 10 | "type": "string" 11 | }, 12 | "type": { 13 | "description": "", 14 | "type": "string" 15 | }, 16 | "deprecated": { 17 | "description": "", 18 | "type": "boolean" 19 | } 20 | }, 21 | "discriminator": "type", 22 | "mapping": { 23 | "StructDefinitionType": "struct", 24 | "MapDefinitionType": "map", 25 | "ArrayDefinitionType": "array" 26 | } 27 | }, 28 | "StructDefinitionType": { 29 | "description": "A struct represents a class/structure with a fix set of defined properties", 30 | "type": "struct", 31 | "parent": { 32 | "type": "reference", 33 | "target": "DefinitionType" 34 | }, 35 | "properties": { 36 | "parent": { 37 | "description": "Defines a parent type, all properties from the parent type are inherited", 38 | "type": "reference", 39 | "target": "ReferencePropertyType" 40 | }, 41 | "base": { 42 | "description": "Indicates whether this is a base structure, default is false. If true the structure is used a base type, this means it is not possible to create an instance from this structure", 43 | "type": "boolean" 44 | }, 45 | "properties": { 46 | "description": "Contains a map of available properties for this struct", 47 | "type": "map", 48 | "schema": { 49 | "type": "reference", 50 | "target": "PropertyType" 51 | } 52 | }, 53 | "discriminator": { 54 | "description": "Optional the property name of a discriminator property. This should be only used in case this is also a base structure", 55 | "type": "string" 56 | }, 57 | "mapping": { 58 | "description": "In case a discriminator is configured it is required to configure a mapping. The mapping is a map where the key is the type name (a key from the definitions map) and the value the actual discriminator type value", 59 | "type": "map", 60 | "schema": { 61 | "type": "string" 62 | } 63 | } 64 | } 65 | }, 66 | "CollectionDefinitionType": { 67 | "description": "Base collection type", 68 | "type": "struct", 69 | "parent": { 70 | "type": "reference", 71 | "target": "DefinitionType" 72 | }, 73 | "base": true, 74 | "properties": { 75 | "type": { 76 | "description": "", 77 | "type": "string" 78 | }, 79 | "schema": { 80 | "description": "", 81 | "type": "reference", 82 | "target": "PropertyType" 83 | } 84 | }, 85 | "discriminator": "type", 86 | "mapping": { 87 | "MapDefinitionType": "map", 88 | "ArrayDefinitionType": "array" 89 | } 90 | }, 91 | "MapDefinitionType": { 92 | "description": "Represents a map which contains a dynamic set of key value entries of the same type", 93 | "type": "struct", 94 | "parent": { 95 | "type": "reference", 96 | "target": "CollectionDefinitionType" 97 | } 98 | }, 99 | "ArrayDefinitionType": { 100 | "description": "Represents an array which contains a dynamic list of values of the same type", 101 | "type": "struct", 102 | "parent": { 103 | "type": "reference", 104 | "target": "CollectionDefinitionType" 105 | } 106 | }, 107 | "PropertyType": { 108 | "description": "Base property type", 109 | "type": "struct", 110 | "base": true, 111 | "properties": { 112 | "description": { 113 | "description": "", 114 | "type": "string" 115 | }, 116 | "type": { 117 | "description": "", 118 | "type": "string" 119 | }, 120 | "deprecated": { 121 | "description": "", 122 | "type": "boolean" 123 | }, 124 | "nullable": { 125 | "description": "", 126 | "type": "boolean" 127 | } 128 | }, 129 | "discriminator": "type", 130 | "mapping": { 131 | "StringPropertyType": "string", 132 | "IntegerPropertyType": "integer", 133 | "NumberPropertyType": "number", 134 | "BooleanPropertyType": "boolean", 135 | "MapPropertyType": "map", 136 | "ArrayPropertyType": "array", 137 | "AnyPropertyType": "any", 138 | "GenericPropertyType": "generic", 139 | "ReferencePropertyType": "reference" 140 | } 141 | }, 142 | "ScalarPropertyType": { 143 | "description": "Base scalar property type", 144 | "type": "struct", 145 | "parent": { 146 | "type": "reference", 147 | "target": "PropertyType" 148 | }, 149 | "base": true, 150 | "properties": { 151 | "type": { 152 | "description": "", 153 | "type": "string" 154 | } 155 | }, 156 | "discriminator": "type", 157 | "mapping": { 158 | "StringPropertyType": "string", 159 | "IntegerPropertyType": "integer", 160 | "NumberPropertyType": "number", 161 | "BooleanPropertyType": "boolean" 162 | } 163 | }, 164 | "StringPropertyType": { 165 | "description": "Represents a string value", 166 | "type": "struct", 167 | "parent": { 168 | "type": "reference", 169 | "target": "ScalarPropertyType" 170 | }, 171 | "properties": { 172 | "format": { 173 | "description": "Optional describes the format of the string. Supported are the following types: date, date-time and time. A code generator may use a fitting data type to represent such a format, if not supported it should fallback to a string", 174 | "type": "string" 175 | } 176 | } 177 | }, 178 | "IntegerPropertyType": { 179 | "description": "Represents an integer value", 180 | "type": "struct", 181 | "parent": { 182 | "type": "reference", 183 | "target": "ScalarPropertyType" 184 | } 185 | }, 186 | "NumberPropertyType": { 187 | "description": "Represents a float value", 188 | "type": "struct", 189 | "parent": { 190 | "type": "reference", 191 | "target": "ScalarPropertyType" 192 | } 193 | }, 194 | "BooleanPropertyType": { 195 | "description": "Represents a boolean value", 196 | "type": "struct", 197 | "parent": { 198 | "type": "reference", 199 | "target": "ScalarPropertyType" 200 | } 201 | }, 202 | "CollectionPropertyType": { 203 | "description": "Base collection property type", 204 | "type": "struct", 205 | "parent": { 206 | "type": "reference", 207 | "target": "PropertyType" 208 | }, 209 | "base": true, 210 | "properties": { 211 | "type": { 212 | "description": "", 213 | "type": "string" 214 | }, 215 | "schema": { 216 | "description": "", 217 | "type": "reference", 218 | "target": "PropertyType" 219 | } 220 | }, 221 | "discriminator": "type", 222 | "mapping": { 223 | "MapPropertyType": "map", 224 | "ArrayPropertyType": "array" 225 | } 226 | }, 227 | "MapPropertyType": { 228 | "description": "Represents a map which contains a dynamic set of key value entries of the same type", 229 | "type": "struct", 230 | "parent": { 231 | "type": "reference", 232 | "target": "CollectionPropertyType" 233 | } 234 | }, 235 | "ArrayPropertyType": { 236 | "description": "Represents an array which contains a dynamic list of values of the same type", 237 | "type": "struct", 238 | "parent": { 239 | "type": "reference", 240 | "target": "CollectionPropertyType" 241 | } 242 | }, 243 | "AnyPropertyType": { 244 | "description": "Represents an any value which allows any kind of value", 245 | "type": "struct", 246 | "parent": { 247 | "type": "reference", 248 | "target": "PropertyType" 249 | } 250 | }, 251 | "GenericPropertyType": { 252 | "description": "Represents a generic value which can be replaced with a concrete type", 253 | "type": "struct", 254 | "parent": { 255 | "type": "reference", 256 | "target": "PropertyType" 257 | }, 258 | "properties": { 259 | "name": { 260 | "description": "The name of the generic, it is recommended to use common generic names like T or TValue. These generics can then be replaced on usage with a concrete type through the template property at a reference", 261 | "type": "string" 262 | } 263 | } 264 | }, 265 | "ReferencePropertyType": { 266 | "description": "Represents a reference to a definition type", 267 | "type": "struct", 268 | "parent": { 269 | "type": "reference", 270 | "target": "PropertyType" 271 | }, 272 | "properties": { 273 | "target": { 274 | "description": "The target type, this must be a key which is available at the definitions map", 275 | "type": "string" 276 | }, 277 | "template": { 278 | "description": "A map where the key is the name of the generic and the value must point to a key under the definitions keyword. This can be used in case the target points to a type which contains generics, then it is possible to replace those generics with a concrete type", 279 | "type": "map", 280 | "schema": { 281 | "type": "string" 282 | } 283 | } 284 | } 285 | }, 286 | "TypeSchema": { 287 | "description": "TypeSchema specification", 288 | "type": "struct", 289 | "properties": { 290 | "import": { 291 | "description": "Allows to import other TypeSchema documents. It contains a map where the key is the namespace and the value points to a remote document. The value is a URL and a code generator should support at least the following schemes: file, http, https", 292 | "type": "map", 293 | "schema": { 294 | "type": "string" 295 | } 296 | }, 297 | "definitions": { 298 | "description": "", 299 | "type": "map", 300 | "schema": { 301 | "type": "reference", 302 | "target": "DefinitionType" 303 | } 304 | }, 305 | "root": { 306 | "description": "Specifies the root type of your specification, this must be a key which is available at the definitions map", 307 | "type": "string" 308 | } 309 | } 310 | } 311 | }, 312 | "root": "TypeSchema" 313 | } -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # TypeSchema 2 | 3 | ## Tests 4 | 5 | This directory contains test TypeSchema specifications using different features of the specification. 6 | If you want to build a generator or parser it is recommended to test your library against these tests. 7 | Your generator is level 5 compliant in case it can work with all levels of the specification. 8 | 9 | ## Level 1 10 | 11 | Tests whether it is possible to work with a simple structures. It tests also that it is possible to 12 | reference a different struct. This is the most basic feature which every processor should support. 13 | 14 | ## Level 2 15 | 16 | Tests whether it is possible to use array and map data types. Inline means that the native array/map 17 | data types are used. Array and map definition types result in a custom array or map implementation. 18 | 19 | ## Level 3 20 | 21 | Tests whether it is possible to extend a struct. If supported a generator should produce two classes 22 | with an actual extend. 23 | 24 | ## Level 4 25 | 26 | Tests whether it is possible to use generics. Generic placeholder at a struct can be replaced on usage 27 | with actual types. A generator should produce actual generics if the language supports this. 28 | 29 | ## Level 5 30 | 31 | Tests whether it is possible to use discriminated union. Through a discriminated union it is possible 32 | to use different schemas depending on a discriminated type value. 33 | -------------------------------------------------------------------------------- /tests/level_1_format.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Student": { 4 | "type": "struct", 5 | "properties": { 6 | "firstName": { 7 | "type": "string" 8 | }, 9 | "lastName": { 10 | "type": "string" 11 | }, 12 | "date": { 13 | "type": "string", 14 | "format": "date" 15 | }, 16 | "dateTime": { 17 | "type": "string", 18 | "format": "date-time" 19 | }, 20 | "time": { 21 | "type": "string", 22 | "format": "time" 23 | } 24 | } 25 | } 26 | }, 27 | "root": "Student" 28 | } -------------------------------------------------------------------------------- /tests/level_1_simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Student": { 4 | "type": "struct", 5 | "properties": { 6 | "firstName": { 7 | "type": "string" 8 | }, 9 | "lastName": { 10 | "type": "string" 11 | }, 12 | "age": { 13 | "type": "integer" 14 | }, 15 | "active": { 16 | "type": "boolean" 17 | }, 18 | "score": { 19 | "type": "number" 20 | }, 21 | "faculty": { 22 | "type": "reference", 23 | "target": "Faculty" 24 | } 25 | } 26 | }, 27 | "Faculty": { 28 | "type": "struct", 29 | "properties": { 30 | "name": { 31 | "type": "string" 32 | } 33 | } 34 | } 35 | }, 36 | "root": "Student" 37 | } -------------------------------------------------------------------------------- /tests/level_2_array_inline_reference.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Student": { 4 | "type": "struct", 5 | "properties": { 6 | "firstName": { 7 | "type": "string" 8 | }, 9 | "lastName": { 10 | "type": "string" 11 | }, 12 | "age": { 13 | "type": "integer" 14 | }, 15 | "properties": { 16 | "type": "array", 17 | "schema": { 18 | "type": "reference", 19 | "target": "StudentProperty" 20 | } 21 | } 22 | } 23 | }, 24 | "StudentProperty": { 25 | "type": "struct", 26 | "properties": { 27 | "name": { 28 | "type": "string" 29 | }, 30 | "value": { 31 | "type": "string" 32 | } 33 | } 34 | } 35 | }, 36 | "root": "Student" 37 | } -------------------------------------------------------------------------------- /tests/level_2_array_inline_string.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Student": { 4 | "type": "struct", 5 | "properties": { 6 | "firstName": { 7 | "type": "string" 8 | }, 9 | "lastName": { 10 | "type": "string" 11 | }, 12 | "age": { 13 | "type": "integer" 14 | }, 15 | "properties": { 16 | "type": "array", 17 | "schema": { 18 | "type": "string" 19 | } 20 | } 21 | } 22 | } 23 | }, 24 | "root": "Student" 25 | } -------------------------------------------------------------------------------- /tests/level_2_array_reference.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Student": { 4 | "type": "struct", 5 | "properties": { 6 | "firstName": { 7 | "type": "string" 8 | }, 9 | "lastName": { 10 | "type": "string" 11 | }, 12 | "age": { 13 | "type": "integer" 14 | }, 15 | "properties": { 16 | "type": "reference", 17 | "target": "StudentArrayReference" 18 | } 19 | } 20 | }, 21 | "StudentArrayReference": { 22 | "type": "array", 23 | "schema": { 24 | "type": "reference", 25 | "target": "StudentProperty" 26 | } 27 | }, 28 | "StudentProperty": { 29 | "type": "struct", 30 | "properties": { 31 | "name": { 32 | "type": "string" 33 | }, 34 | "value": { 35 | "type": "string" 36 | } 37 | } 38 | } 39 | }, 40 | "root": "Student" 41 | } -------------------------------------------------------------------------------- /tests/level_2_array_string.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Student": { 4 | "type": "struct", 5 | "properties": { 6 | "firstName": { 7 | "type": "string" 8 | }, 9 | "lastName": { 10 | "type": "string" 11 | }, 12 | "age": { 13 | "type": "integer" 14 | }, 15 | "properties": { 16 | "type": "reference", 17 | "target": "StudentArrayString" 18 | } 19 | } 20 | }, 21 | "StudentArrayString": { 22 | "type": "array", 23 | "schema": { 24 | "type": "string" 25 | } 26 | } 27 | }, 28 | "root": "Student" 29 | } -------------------------------------------------------------------------------- /tests/level_2_map_inline_reference.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Student": { 4 | "type": "struct", 5 | "properties": { 6 | "firstName": { 7 | "type": "string" 8 | }, 9 | "lastName": { 10 | "type": "string" 11 | }, 12 | "age": { 13 | "type": "integer" 14 | }, 15 | "properties": { 16 | "type": "map", 17 | "schema": { 18 | "type": "reference", 19 | "target": "StudentProperty" 20 | } 21 | } 22 | } 23 | }, 24 | "StudentProperty": { 25 | "type": "struct", 26 | "properties": { 27 | "name": { 28 | "type": "string" 29 | }, 30 | "value": { 31 | "type": "string" 32 | } 33 | } 34 | } 35 | }, 36 | "root": "Student" 37 | } -------------------------------------------------------------------------------- /tests/level_2_map_inline_string.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Student": { 4 | "type": "struct", 5 | "properties": { 6 | "firstName": { 7 | "type": "string" 8 | }, 9 | "lastName": { 10 | "type": "string" 11 | }, 12 | "age": { 13 | "type": "integer" 14 | }, 15 | "properties": { 16 | "type": "map", 17 | "schema": { 18 | "type": "string" 19 | } 20 | } 21 | } 22 | } 23 | }, 24 | "root": "Student" 25 | } -------------------------------------------------------------------------------- /tests/level_2_map_reference.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Student": { 4 | "type": "struct", 5 | "properties": { 6 | "firstName": { 7 | "type": "string" 8 | }, 9 | "lastName": { 10 | "type": "string" 11 | }, 12 | "age": { 13 | "type": "integer" 14 | }, 15 | "properties": { 16 | "type": "reference", 17 | "target": "StudentMapReference" 18 | } 19 | } 20 | }, 21 | "StudentMapReference": { 22 | "type": "map", 23 | "schema": { 24 | "type": "reference", 25 | "target": "StudentProperty" 26 | } 27 | }, 28 | "StudentProperty": { 29 | "type": "struct", 30 | "properties": { 31 | "name": { 32 | "type": "string" 33 | }, 34 | "value": { 35 | "type": "string" 36 | } 37 | } 38 | } 39 | }, 40 | "root": "Student" 41 | } -------------------------------------------------------------------------------- /tests/level_2_map_string.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Student": { 4 | "type": "struct", 5 | "properties": { 6 | "firstName": { 7 | "type": "string" 8 | }, 9 | "lastName": { 10 | "type": "string" 11 | }, 12 | "age": { 13 | "type": "integer" 14 | }, 15 | "properties": { 16 | "type": "reference", 17 | "target": "StudentMapString" 18 | } 19 | } 20 | }, 21 | "StudentMapString": { 22 | "type": "map", 23 | "schema": { 24 | "type": "string" 25 | } 26 | } 27 | }, 28 | "root": "Student" 29 | } -------------------------------------------------------------------------------- /tests/level_3_inheritance.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Human": { 4 | "type": "struct", 5 | "properties": { 6 | "firstName": { 7 | "type": "string" 8 | }, 9 | "lastName": { 10 | "type": "string" 11 | }, 12 | "age": { 13 | "type": "integer" 14 | } 15 | } 16 | }, 17 | "Student": { 18 | "parent": { 19 | "type": "reference", 20 | "target": "Human" 21 | }, 22 | "type": "struct", 23 | "properties": { 24 | "studentId": { 25 | "type": "string" 26 | } 27 | } 28 | } 29 | }, 30 | "root": "Student" 31 | } -------------------------------------------------------------------------------- /tests/level_4_generic.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Student": { 4 | "type": "struct", 5 | "properties": { 6 | "matricleNumber": { 7 | "type": "integer" 8 | } 9 | } 10 | }, 11 | "StudentMap": { 12 | "type": "struct", 13 | "parent": { 14 | "type": "reference", 15 | "target": "Map", 16 | "template": { 17 | "T": "Student" 18 | } 19 | }, 20 | "properties": {} 21 | }, 22 | "Map": { 23 | "type": "struct", 24 | "properties": { 25 | "totalResults": { 26 | "type": "integer" 27 | }, 28 | "entries": { 29 | "type": "array", 30 | "schema": { 31 | "type": "generic", 32 | "name": "T" 33 | } 34 | } 35 | } 36 | } 37 | }, 38 | "root": "StudentMap" 39 | } -------------------------------------------------------------------------------- /tests/level_5_discriminator.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Human": { 4 | "type": "struct", 5 | "properties": { 6 | "firstName": { 7 | "type": "string" 8 | }, 9 | "lastName": { 10 | "type": "string" 11 | }, 12 | "location": { 13 | "type": "reference", 14 | "target": "Location" 15 | } 16 | } 17 | }, 18 | "Location": { 19 | "type": "struct", 20 | "base": true, 21 | "discriminator": "type", 22 | "mapping": { 23 | "Web": "web", 24 | "World": "world" 25 | }, 26 | "properties": { 27 | "type": { 28 | "type": "string" 29 | } 30 | } 31 | }, 32 | "Web": { 33 | "parent": { 34 | "type": "reference", 35 | "target": "Location" 36 | }, 37 | "type": "struct", 38 | "properties": { 39 | "url": { 40 | "type": "string" 41 | } 42 | } 43 | }, 44 | "World": { 45 | "parent": { 46 | "type": "reference", 47 | "target": "Location" 48 | }, 49 | "type": "struct", 50 | "properties": { 51 | "lat": { 52 | "type": "string" 53 | }, 54 | "long": { 55 | "type": "string" 56 | } 57 | } 58 | } 59 | }, 60 | "root": "Human" 61 | } -------------------------------------------------------------------------------- /tests/sdkgen.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "model-java", 3 | "require": { 4 | "./level_1_format.json": { 5 | "target": "../java/org/typeschema/reflection/dto/level_1_format", 6 | "namespace": "org.typeschema.reflection.dto.level_1_format" 7 | }, 8 | "./level_1_simple.json": { 9 | "target": "../java/org/typeschema/reflection/dto/level_1_simple", 10 | "namespace": "org.typeschema.reflection.dto.level_1_simple" 11 | }, 12 | "./level_2_array_inline_reference.json": { 13 | "target": "../java/org/typeschema/reflection/dto/level_2_array_inline_reference", 14 | "namespace": "org.typeschema.reflection.dto.level_2_array_inline_reference" 15 | }, 16 | "./level_2_array_inline_string.json": { 17 | "target": "../java/org/typeschema/reflection/dto/level_2_array_inline_string", 18 | "namespace": "org.typeschema.reflection.dto.level_2_array_inline_string" 19 | }, 20 | "./level_2_array_reference.json": { 21 | "target": "../java/org/typeschema/reflection/dto/level_2_array_reference", 22 | "namespace": "org.typeschema.reflection.dto.level_2_array_reference" 23 | }, 24 | "./level_2_array_string.json": { 25 | "target": "../java/org/typeschema/reflection/dto/level_2_array_string", 26 | "namespace": "org.typeschema.reflection.dto.level_2_array_string" 27 | }, 28 | "./level_2_map_inline_reference.json": { 29 | "target": "../java/org/typeschema/reflection/dto/level_2_map_inline_reference", 30 | "namespace": "org.typeschema.reflection.dto.level_2_map_inline_reference" 31 | }, 32 | "./level_2_map_inline_string.json": { 33 | "target": "../java/org/typeschema/reflection/dto/level_2_map_inline_string", 34 | "namespace": "org.typeschema.reflection.dto.level_2_map_inline_string" 35 | }, 36 | "./level_2_map_reference.json": { 37 | "target": "../java/org/typeschema/reflection/dto/level_2_map_reference", 38 | "namespace": "org.typeschema.reflection.dto.level_2_map_reference" 39 | }, 40 | "./level_2_map_string.json": { 41 | "target": "../java/org/typeschema/reflection/dto/level_2_map_string", 42 | "namespace": "org.typeschema.reflection.dto.level_2_map_string" 43 | }, 44 | "./level_3_inheritance.json": { 45 | "target": "../java/org/typeschema/reflection/dto/level_3_inheritance", 46 | "namespace": "org.typeschema.reflection.dto.level_3_inheritance" 47 | }, 48 | "./level_4_generic.json": { 49 | "target": "../java/org/typeschema/reflection/dto/level_4_generic", 50 | "namespace": "org.typeschema.reflection.dto.level_4_generic" 51 | }, 52 | "./level_5_discriminator.json": { 53 | "target": "../java/org/typeschema/reflection/dto/level_5_discriminator", 54 | "namespace": "org.typeschema.reflection.dto.level_5_discriminator" 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /www/.env: -------------------------------------------------------------------------------- 1 | APP_URL=http://127.0.0.1/projects/typeschema/www/public 2 | APP_ENV=dev 3 | APP_DEBUG=on 4 | APP_CONNECTION=mysql://root:test1234@localhost/psx 5 | APP_MAILER=native://default 6 | -------------------------------------------------------------------------------- /www/build.php: -------------------------------------------------------------------------------- 1 | =8.1", 4 | "psx/framework": "^7.0" 5 | }, 6 | "require-dev": { 7 | "vimeo/psalm": "^6.0" 8 | }, 9 | "autoload": { 10 | "psr-4": { 11 | "App\\": "src/" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /www/configuration.php: -------------------------------------------------------------------------------- 1 | env('APP_RECAPTCHA_KEY')->string(), 9 | 'recaptcha_secret' => env('APP_RECAPTCHA_SECRET')->string(), 10 | 11 | // The url to the psx public folder (i.e. http://api.acme.com or http://127.0.0.1/psx/public) 12 | 'psx_url' => env('APP_URL')->string(), 13 | 14 | // The input path 'index.php/' or '' if every request is served to the index.php file 15 | 'psx_dispatch' => '', 16 | 17 | // Defines the current environment i.e. prod or dev 18 | 'psx_env' => env('APP_ENV')->string()->default('prod'), 19 | 20 | // Whether the app runs in debug mode or not. If not error reporting is set to 0, also several caches are used if 21 | // the debug mode is false 22 | 'psx_debug' => env('APP_DEBUG')->bool()->default(false), 23 | 24 | // Database parameters which are used for the doctrine DBAL connection 25 | // https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html 26 | 'psx_connection' => env('APP_CONNECTION')->string(), 27 | 28 | // Mailer connection which is used to send mails 29 | // https://symfony.com/doc/current/mailer.html#using-built-in-transports 30 | 'psx_mailer' => env('APP_MAILER')->string(), 31 | 32 | // The log level 33 | 'psx_log_level' => Logger::ERROR, 34 | 35 | // Folder locations 36 | 'psx_path_cache' => __DIR__ . '/cache', 37 | 'psx_path_src' => __DIR__ . '/src', 38 | 'psx_path_log' => __DIR__ . '/log', 39 | 40 | // Supported writers 41 | 'psx_supported_writer' => [ 42 | \PSX\Data\Writer\Json::class, 43 | \PSX\Data\Writer\Jsonp::class, 44 | \PSX\Data\Writer\Jsonx::class, 45 | ], 46 | 47 | ]; 48 | -------------------------------------------------------------------------------- /www/container.php: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /www/public/.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | RewriteBase / 3 | RewriteCond %{REQUEST_FILENAME} !-f 4 | RewriteCond %{REQUEST_FILENAME} !-d 5 | RewriteRule (.*) /index.php/$1 [L] 6 | -------------------------------------------------------------------------------- /www/public/css/app.css: -------------------------------------------------------------------------------- 1 | textarea { 2 | font-family: monospace; 3 | } 4 | 5 | .form-control, .btn { 6 | border-radius: 0; 7 | } 8 | 9 | .example-box { 10 | max-height:600px; 11 | overflow:auto; 12 | } 13 | 14 | .example-box > pre { 15 | margin:0; 16 | } 17 | 18 | .typeschema-edit { 19 | margin-top:32px; 20 | margin-bottom:32px; 21 | text-align:right; 22 | } 23 | 24 | /* psx schema */ 25 | .psx-object { 26 | margin:0; 27 | margin-bottom:8px; 28 | border:1px solid #ccc; 29 | } 30 | 31 | .psx-object > h1 { 32 | font-weight:bold; 33 | font-size:1em; 34 | margin:0; 35 | padding: 10px 15px; 36 | padding-left: 8px; 37 | border-bottom: 1px solid #999; 38 | color: #333; 39 | background-color: #f5f5f5; 40 | background-repeat: repeat-x; 41 | background-image: -webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%); 42 | background-image: -o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%); 43 | background-image: -webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8)); 44 | background-image: linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%); 45 | } 46 | 47 | .psx-object-description { 48 | margin:8px 0; 49 | padding:0 8px; 50 | } 51 | 52 | .psx-object-json { 53 | border-radius:0; 54 | border:0; 55 | margin:0; 56 | padding:8px; 57 | background-color:#F0F0F0; 58 | } 59 | 60 | .psx-object-json-key { 61 | color:#880000; 62 | } 63 | 64 | .psx-object-json-pun { 65 | color:#65b042; 66 | } 67 | 68 | .psx-object-properties { 69 | } 70 | 71 | .psx-property-type { 72 | font-family:monospace; 73 | font-weight:bold; 74 | } 75 | 76 | .psx-property-name { 77 | font-family:monospace; 78 | font-weight:bold; 79 | } 80 | 81 | .psx-property-description { 82 | margin-top:8px; 83 | font-size:0.9em; 84 | } 85 | 86 | .psx-property-constraint { 87 | margin-top:8px; 88 | font-size:0.9em; 89 | } 90 | 91 | .psx-property-required { 92 | border-bottom:1px dotted #999; 93 | } 94 | 95 | .table { 96 | width:100%; 97 | border-spacing:0; 98 | } 99 | 100 | .table th { 101 | background-color:#fff; 102 | padding:8px; 103 | text-align:left; 104 | } 105 | 106 | .table td { 107 | padding:8px; 108 | border-bottom:1px solid #ccc; 109 | } 110 | 111 | .psx-out { 112 | overflow-x:auto; 113 | } 114 | 115 | .apioo-brand { 116 | background-color:#000; 117 | text-align: center; 118 | color:#eee; 119 | padding:24px; 120 | font-weight:bold; 121 | } 122 | 123 | .apioo-brand a { 124 | color:#00d1b2; 125 | text-decoration:none; 126 | } 127 | 128 | -------------------------------------------------------------------------------- /www/public/css/highlight.min.css: -------------------------------------------------------------------------------- 1 | .hljs{display:block;overflow-x:auto;padding:.5em;background:#F0F0F0}.hljs,.hljs-subst{color:#444}.hljs-comment{color:#888888}.hljs-keyword,.hljs-attribute,.hljs-selector-tag,.hljs-meta-keyword,.hljs-doctag,.hljs-name{font-weight:bold}.hljs-type,.hljs-string,.hljs-number,.hljs-selector-id,.hljs-selector-class,.hljs-quote,.hljs-template-tag,.hljs-deletion{color:#880000}.hljs-title,.hljs-section{color:#880000;font-weight:bold}.hljs-regexp,.hljs-symbol,.hljs-variable,.hljs-template-variable,.hljs-link,.hljs-selector-attr,.hljs-selector-pseudo{color:#BC6060}.hljs-literal{color:#78A960}.hljs-built_in,.hljs-bullet,.hljs-code,.hljs-addition{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta-string{color:#4d99bf}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold} 2 | -------------------------------------------------------------------------------- /www/public/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apioo/typeschema/167952dc7ea4a7b3e85eff068d622559c66fd52a/www/public/img/favicon.ico -------------------------------------------------------------------------------- /www/public/img/github-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apioo/typeschema/167952dc7ea4a7b3e85eff068d622559c66fd52a/www/public/img/github-32.png -------------------------------------------------------------------------------- /www/public/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apioo/typeschema/167952dc7ea4a7b3e85eff068d622559c66fd52a/www/public/img/logo.png -------------------------------------------------------------------------------- /www/public/img/typeschema_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apioo/typeschema/167952dc7ea4a7b3e85eff068d622559c66fd52a/www/public/img/typeschema_flow.png -------------------------------------------------------------------------------- /www/public/img/typeschema_structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apioo/typeschema/167952dc7ea4a7b3e85eff068d622559c66fd52a/www/public/img/typeschema_structure.png -------------------------------------------------------------------------------- /www/public/index.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * Copyright 2010-2023 Christoph Kappestein 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | require_once(__DIR__ . '/../vendor/autoload.php'); 22 | 23 | $container = require_once(__DIR__ . '/../container.php'); 24 | 25 | $engine = new \PSX\Engine\WebServer\Engine($container->getParameter('psx_url')); 26 | $dispatcher = $container->get(\PSX\Engine\DispatchInterface::class); 27 | $environment = new \PSX\Framework\Environment\Environment($dispatcher, $engine); 28 | 29 | return $environment->serve(); 30 | -------------------------------------------------------------------------------- /www/public/js/popper.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) Federico Zivolo 2020 3 | Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT). 4 | */(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=e.ownerDocument.defaultView,n=o.getComputedStyle(e,null);return t?n[t]:n}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e)return document.body;switch(e.nodeName){case'HTML':case'BODY':return e.ownerDocument.body;case'#document':return e.body;}var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll|overlay)/.test(r+s+p)?e:n(o(e))}function i(e){return e&&e.referenceNode?e.referenceNode:e}function r(e){return 11===e?re:10===e?pe:re||pe}function p(e){if(!e)return document.documentElement;for(var o=r(10)?document.body:null,n=e.offsetParent||null;n===o&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TH','TD','TABLE'].indexOf(n.nodeName)&&'static'===t(n,'position')?p(n):n:e?e.ownerDocument.documentElement:document.documentElement}function s(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||p(e.firstElementChild)===e)}function d(e){return null===e.parentNode?e:d(e.parentNode)}function a(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,n=o?e:t,i=o?t:e,r=document.createRange();r.setStart(n,0),r.setEnd(i,0);var l=r.commonAncestorContainer;if(e!==l&&t!==l||n.contains(i))return s(l)?l:p(l);var f=d(e);return f.host?a(f.host,t):a(e,d(t).host)}function l(e){var t=1=o.clientWidth&&n>=o.clientHeight}),l=0a[e]&&!t.escapeWithReference&&(n=Q(f[o],a[e]-('right'===e?f.width:f.height))),ae({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=le({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=Z,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!K(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-us[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,w=t(e.instance.popper),y=parseFloat(w['margin'+f]),E=parseFloat(w['border'+f+'Width']),v=b-e.offsets.popper[m]-y-E;return v=ee(Q(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},ae(n,m,$(v)),ae(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case ce.FLIP:p=[n,i];break;case ce.CLOCKWISE:p=G(n);break;case ce.COUNTERCLOCKWISE:p=G(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=Z,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)f(l.top)||'bottom'===n&&f(a.top)f(o.right),g=f(a.top)f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,w=-1!==['top','bottom'].indexOf(n),y=!!t.flipVariations&&(w&&'start'===r&&h||w&&'end'===r&&c||!w&&'start'===r&&g||!w&&'end'===r&&u),E=!!t.flipVariationsByContent&&(w&&'start'===r&&c||w&&'end'===r&&h||!w&&'start'===r&&u||!w&&'end'===r&&g),v=y||E;(m||b||v)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),v&&(r=z(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=le({},e.offsets.popper,C(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport',flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!K(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=D(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottomo.right||t.top>o.bottom||t.rightwindow.devicePixelRatio||!fe),c='bottom'===o?'top':'bottom',g='right'===n?'left':'right',b=B('transform');if(d='bottom'==c?'HTML'===l.nodeName?-l.clientHeight+h.bottom:-f.height+h.bottom:h.top,s='right'==g?'HTML'===l.nodeName?-l.clientWidth+h.right:-f.width+h.right:h.left,a&&b)m[b]='translate3d('+s+'px, '+d+'px, 0)',m[c]=0,m[g]=0,m.willChange='transform';else{var w='bottom'==c?-1:1,y='right'==g?-1:1;m[c]=d*w,m[g]=s*y,m.willChange=c+', '+g}var E={"x-placement":e.placement};return e.attributes=le({},E,e.attributes),e.styles=le({},m,e.styles),e.arrowStyles=le({},e.offsets.arrow,e.arrowStyles),e},gpuAcceleration:!0,x:'bottom',y:'right'},applyStyle:{order:900,enabled:!0,fn:function(e){return V(e.instance.popper,e.styles),j(e.instance.popper,e.attributes),e.arrowElement&&Object.keys(e.arrowStyles).length&&V(e.arrowElement,e.arrowStyles),e},onLoad:function(e,t,o,n,i){var r=L(i,t,e,o.positionFixed),p=O(o.placement,r,t,e,o.modifiers.flip.boundariesElement,o.modifiers.flip.padding);return t.setAttribute('x-placement',p),V(t,{position:o.positionFixed?'fixed':'absolute'}),o},gpuAcceleration:void 0}}},ge}); 5 | //# sourceMappingURL=popper.min.js.map 6 | -------------------------------------------------------------------------------- /www/resources/container.php: -------------------------------------------------------------------------------- 1 | services(); 11 | $services->defaults() 12 | ->autowire() 13 | ->autoconfigure(); 14 | 15 | $services 16 | ->instanceof(ControllerInterface::class) 17 | ->tag('psx.controller'); 18 | 19 | $services->load('App\\Controller\\', __DIR__ . '/../src/Controller') 20 | ->public(); 21 | 22 | $services->load('App\\Service\\', __DIR__ . '/../src/Service'); 23 | }; 24 | -------------------------------------------------------------------------------- /www/resources/examples/discriminator.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Human": { 4 | "type": "struct", 5 | "properties": { 6 | "firstName": { 7 | "type": "string" 8 | }, 9 | "lastName": { 10 | "type": "string" 11 | }, 12 | "location": { 13 | "type": "reference", 14 | "target": "Location" 15 | } 16 | } 17 | }, 18 | "Location": { 19 | "type": "struct", 20 | "base": true, 21 | "properties": { 22 | "type": { 23 | "type": "string" 24 | } 25 | }, 26 | "discriminator": "type", 27 | "mapping": { 28 | "Web": "web", 29 | "World": "world" 30 | } 31 | }, 32 | "Web": { 33 | "parent": { 34 | "type": "reference", 35 | "target": "Location" 36 | }, 37 | "type": "struct", 38 | "properties": { 39 | "url": { 40 | "type": "string" 41 | } 42 | } 43 | }, 44 | "World": { 45 | "parent": { 46 | "type": "reference", 47 | "target": "Location" 48 | }, 49 | "type": "struct", 50 | "properties": { 51 | "lat": { 52 | "type": "string" 53 | }, 54 | "long": { 55 | "type": "string" 56 | } 57 | } 58 | } 59 | }, 60 | "root": "Human" 61 | } -------------------------------------------------------------------------------- /www/resources/examples/generic.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Student": { 4 | "type": "struct", 5 | "properties": { 6 | "matricleNumber": { 7 | "type": "integer" 8 | } 9 | } 10 | }, 11 | "StudentMap": { 12 | "type": "struct", 13 | "parent": { 14 | "type": "reference", 15 | "target": "Map", 16 | "template": { 17 | "T": "Student" 18 | } 19 | } 20 | }, 21 | "Map": { 22 | "type": "struct", 23 | "properties": { 24 | "totalResults": { 25 | "type": "integer" 26 | }, 27 | "entries": { 28 | "type": "array", 29 | "schema": { 30 | "type": "generic", 31 | "name": "T" 32 | } 33 | } 34 | } 35 | } 36 | }, 37 | "root": "StudentMap" 38 | } -------------------------------------------------------------------------------- /www/resources/examples/import.json: -------------------------------------------------------------------------------- 1 | { 2 | "import": { 3 | "my_ns": "file:///generic.json" 4 | }, 5 | "definitions": { 6 | "Faculty": { 7 | "type": "struct", 8 | "properties": { 9 | "description": { 10 | "type": "string" 11 | }, 12 | "students": { 13 | "type": "array", 14 | "schema": { 15 | "type": "reference", 16 | "target": "my_ns:StudentMap" 17 | } 18 | } 19 | } 20 | } 21 | }, 22 | "root": "Faculty" 23 | } -------------------------------------------------------------------------------- /www/resources/examples/inheritance.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Human": { 4 | "type": "struct", 5 | "properties": { 6 | "firstName": { 7 | "type": "string" 8 | }, 9 | "lastName": { 10 | "type": "string" 11 | }, 12 | "age": { 13 | "type": "integer" 14 | } 15 | } 16 | }, 17 | "Student": { 18 | "parent": { 19 | "type": "reference", 20 | "target": "Human" 21 | }, 22 | "type": "struct", 23 | "properties": { 24 | "studentId": { 25 | "type": "string" 26 | } 27 | } 28 | } 29 | }, 30 | "root": "Student" 31 | } -------------------------------------------------------------------------------- /www/resources/examples/map.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Student": { 4 | "type": "struct", 5 | "properties": { 6 | "firstName": { 7 | "type": "string" 8 | }, 9 | "lastName": { 10 | "type": "string" 11 | }, 12 | "age": { 13 | "type": "integer" 14 | }, 15 | "properties": { 16 | "type": "reference", 17 | "target": "Student_Properties" 18 | } 19 | } 20 | }, 21 | "Student_Properties": { 22 | "type": "map", 23 | "schema": { 24 | "type": "string" 25 | } 26 | } 27 | }, 28 | "root": "Student" 29 | } -------------------------------------------------------------------------------- /www/resources/examples/map_inline.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Student": { 4 | "type": "struct", 5 | "properties": { 6 | "firstName": { 7 | "type": "string" 8 | }, 9 | "lastName": { 10 | "type": "string" 11 | }, 12 | "age": { 13 | "type": "integer" 14 | }, 15 | "properties": { 16 | "type": "map", 17 | "schema": { 18 | "type": "string" 19 | } 20 | } 21 | } 22 | } 23 | }, 24 | "root": "Student" 25 | } -------------------------------------------------------------------------------- /www/resources/examples/reference.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Student": { 4 | "description": "A student struct with an assigned faculty", 5 | "type": "struct", 6 | "properties": { 7 | "firstName": { 8 | "type": "string" 9 | }, 10 | "lastName": { 11 | "type": "string" 12 | }, 13 | "age": { 14 | "type": "integer" 15 | }, 16 | "faculty": { 17 | "type": "reference", 18 | "target": "Faculty" 19 | } 20 | } 21 | }, 22 | "Faculty": { 23 | "type": "struct", 24 | "properties": { 25 | "name": { 26 | "type": "string" 27 | } 28 | } 29 | } 30 | }, 31 | "root": "Student" 32 | } -------------------------------------------------------------------------------- /www/resources/examples/simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Student": { 4 | "description": "A simple student struct", 5 | "type": "struct", 6 | "properties": { 7 | "firstName": { 8 | "type": "string" 9 | }, 10 | "lastName": { 11 | "type": "string" 12 | }, 13 | "age": { 14 | "type": "integer" 15 | } 16 | } 17 | } 18 | }, 19 | "root": "Student" 20 | } -------------------------------------------------------------------------------- /www/resources/template/developer.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 |
11 |

Developer

12 |

This chapter provides some guidelines for developers how to build a TypeSchema code generator.

13 |
    14 |
  • TypeSchema is designed so that a processor can create an in-memory object representation of a schema. Every 15 | schema can be exactly assigned to a specific type. A processor should create a fitting class for each type and a 16 | factory which creates the fitting type depending on the used keywords.
  • 17 |
  • There is one key where all reusable types are located. For plain TypeSchema documents this is 18 | /definitions for OpenAPI, AsyncAPI 19 | and OpenRPC documents it is /components/schemas. A processor 20 | should have an option to set the location of the definitions location. There is also no need for a 21 | JSON Pointer implementation since we only resolve local schemas.
  • 22 |
  • There are no nested objects instead every object must be defined under the definitions location. 23 | Object types can then reference these types. This is required since a processor needs a unique name for each 24 | object type, which is the definition key of the type (i.e. which is used as class name).
  • 25 |
  • To parse a TypeSchema we transform at the first step only the JSON into fitting objects representing the types. 26 | It is important that we _do not_ resolve references at the parsing step. This step is always executed when 27 | processing a schema. 28 |
  • 29 |
30 | 31 |
32 |

Overview

33 | 34 |

The following TypeSchema shows most keywords with a concrete reference to the specification, which should give you 35 | a first overview of the available keywords.

36 | 37 |
{
 38 |   "$import": {
 39 |     "my_ns": "file:///generic.json"
 40 |   },
 41 |   "definitions": {
 42 |     "Human": {
 43 |       "type": "struct",
 44 |       "properties": {
 45 |         "firstName": {
 46 |           "type": "string"
 47 |         },
 48 |         "lastName": {
 49 |           "type": "string"
 50 |         },
 51 |         "age": {
 52 |           "type": "integer"
 53 |         }
 54 |       }
 55 |     },
 56 |     "Student": {
 57 |       "parent": "Human",
 58 |       "type": "struct",
 59 |       "properties": {
 60 |         "matricleNumber": {
 61 |           "type": "integer"
 62 |         }
 63 |       }
 64 |     },
 65 |     "StudentMap": {
 66 |       "$ref": "Map",
 67 |       "$template": {
 68 |         "T": "Student"
 69 |       }
 70 |     },
 71 |     "Map": {
 72 |       "type": "struct",
 73 |       "properties": {
 74 |         "totalResults": {
 75 |           "type": "integer"
 76 |         },
 77 |         "entries": {
 78 |           "type": "array",
 79 |           "items": {
 80 |             "$generic": "T"
 81 |           }
 82 |         }
 83 |       }
 84 |     }
 85 |   },
 86 |   "$ref": "StudentMap"
 87 | }
88 | 89 |
90 |

Parse type

91 |

The following algorithm shows how to create an object in-memory representation of a type. It assumes that you 92 | pass the raw JSON decoded object into this function and it returns a concrete type.

93 | 94 |

function parseType(object json): Type

95 |
    96 |
  • Type type = newType(json)
  • 97 |
  • If type is an instance of StructType 98 |
      99 |
    • Map<string, Type> properties = Create a new empty map
    • 100 |
    • For each key : property in json.properties 101 |
        102 |
      • properties[key] = parseType(property)
      • 103 |
      104 |
    • 105 |
    • Set properties to type
    • 106 |
    • Parse the remaining struct type properties from json and set them to type
    • 107 |
    • return type
    • 108 |
    109 |
  • 110 |
  • Else If type is an instance of MapType 111 |
      112 |
    • Type additionalProperties = parseType(json.additionalProperties)
    • 113 |
    • Set additionalProperties to type
    • 114 |
    • Parse the remaining map type properties from json and set them to the type
    • 115 |
    • return type
    • 116 |
    117 |
  • 118 |
  • Else If type is an instance of ArrayType 119 |
      120 |
    • Type items = parseType(json.items)
    • 121 |
    • Set items to type
    • 122 |
    • Parse the remaining array type properties from json and set them to type
    • 123 |
    • return type
    • 124 |
    125 |
  • 126 |
  • Else If type is an instance of BooleanType 127 |
      128 |
    • return type
    • 129 |
    130 |
  • 131 |
  • Else If type is an instance of NumberType 132 |
      133 |
    • Parse the number type properties from json and set them to type
    • 134 |
    • return type
    • 135 |
    136 |
  • 137 |
  • Else If type is an instance of StringType 138 |
      139 |
    • Parse the string type properties from json and set them to type
    • 140 |
    • return type
    • 141 |
    142 |
  • 143 |
  • Else If type is an instance of AnyType 144 |
      145 |
    • return type
    • 146 |
    147 |
  • 148 |
  • Else If type is an instance of GenericType 149 |
      150 |
    • return type
    • 151 |
    152 |
  • 153 |
  • Else If type is an instance of UnionType 154 |
      155 |
    • Array<Type> items = Create an empty array
    • 156 |
    • For each property in json.oneOf 157 | 160 |
    • 161 |
    • Set items to type
    • 162 |
    • Parse the remaining union type properties from json and set them to type
    • 163 |
    • return type
    • 164 |
    165 |
  • 166 |
  • Else If type is an instance of IntersectionType 167 |
      168 |
    • Array<Type> items = Create an empty array
    • 169 |
    • For each property in json.oneOf 170 | 173 |
    • 174 |
    • Set items to type
    • 175 |
    • Parse the remaining intersection type properties from json and set them to type
    • 176 |
    • return type
    • 177 |
    178 |
  • 179 |
  • Else If type is an instance of ReferenceType 180 |
      181 |
    • Parse the reference type properties from json and set them to type
    • 182 |
    • return type
    • 183 |
    184 |
  • 185 |
  • Else 186 |
      187 |
    • Throw an exception that we could not resolve the type
    • 188 |
    189 |
  • 190 |
191 | 192 |
193 |

Type detection

194 |

The following algorithm shows how to detect the correct type from a decoded JSON value.

195 | 196 |

function newType(object json): Type|null

197 |
    198 |
  • If json.type is available 199 |
      200 |
    • If json.type is equal to object 201 |
        202 |
      • If json.properties is available 203 |
          204 |
        • return new StructType
        • 205 |
        206 |
      • 207 |
      • Else If json.additionalProperties is available 208 |
          209 |
        • return new MapType
        • 210 |
        211 |
      • 212 |
      213 |
    • 214 |
    • Else If json.type is equal to array 215 |
        216 |
      • return new ArrayType
      • 217 |
      218 |
    • 219 |
    • Else If json.type is equal to boolean 220 |
        221 |
      • return new BooleanType
      • 222 |
      223 |
    • 224 |
    • Else If json.type is equal to number 225 |
        226 |
      • return new NumberType
      • 227 |
      228 |
    • 229 |
    • Else If json.type is equal to string 230 |
        231 |
      • return new StringType
      • 232 |
      233 |
    • 234 |
    • Else If json.type is equal to any 235 |
        236 |
      • return new AnyType
      • 237 |
      238 |
    • 239 |
    240 |
  • 241 |
  • Else If json.allOf is available 242 |
      243 |
    • return new IntersectionType
    • 244 |
    245 |
  • 246 |
  • Else If json.oneOf is available 247 |
      248 |
    • return new UnionType
    • 249 |
    250 |
  • 251 |
  • Else If json.$ref is available 252 |
      253 |
    • return new ReferenceType
    • 254 |
    255 |
  • 256 |
  • Else If json.$generic is available 257 |
      258 |
    • return new GenericType
    • 259 |
    260 |
  • 261 |
  • return null
  • 262 |
263 | 264 |
265 |

Reference resolution

266 |

The following algorithm shows how to resolve a reference.

267 | 268 |

function resolveReference(ReferenceType type, Definitions definitions): Type

269 |
    270 |
  • string ref = type.ref
  • 271 |
  • Replace the string #/definitions/ and #/components/schemas/ on ref with an empty string
  • 272 | 273 |
  • If the ref contains not a colon : 274 |
      275 |
    • If the ref is available under the definitions 276 |
        277 |
      • return definitions[ref]
      • 278 |
      279 |
    • 280 |
    • Else 281 |
        282 |
      • Throw an exception that we could not resolve the type
      • 283 |
      284 |
    • 285 |
    286 |
  • 287 |
  • Else 288 |
      289 |
    • Split up the ref string based on the colon : into two parts
    • 290 |
    • string namespace = parts[0]
    • 291 |
    • string name = parts[1]
    • 292 |
    • If the namespace is available under the $import keyword 293 |
        294 |
      • Lookup the imported schema
      • 295 |
      • If the resolved schema contains a type with the provided name 296 |
          297 |
        • Return the type of the remote schema
        • 298 |
        299 |
      • 300 |
      • Else 301 |
          302 |
        • Throw an exception that the remote schema does not contain the referenced type
        • 303 |
        304 |
      • 305 |
      306 |
    • 307 |
    • Else 308 |
        309 |
      • Throw an exception that we could not resolve the namespace
      • 310 |
      311 |
    • 312 |
    313 |
  • 314 |
315 | 316 |
317 | Edit this page 318 |
319 |
320 | 321 | 322 | -------------------------------------------------------------------------------- /www/resources/template/ecosystem.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 |
11 |

Ecosystem

12 |

The following page lists libraries, integrations and other projects related to TypeSchema.

13 | 14 |

Model

15 |

We provide auto-generated models of the TypeSchema meta specification which describes itself. 16 | These models can be used to parse or generate a TypeSchema specification.

17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
LanguageGitHubPackage
C#GitHubNuget
GoGitHub
JavaGitHubMaven
JavaScriptGitHubNPM
PHPGitHubPackagist
PythonGitHubPyPI
63 | 64 |

Reflection

65 |

The reflection libraries help to automatically generate a TypeSchema specification based on a class. 66 | These libraries use the reflection mechanism of each language to inspect the class and create the fitting 67 | specification.

68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 |
LanguageGitHubPackage
JavaGitHubMaven
PHPGitHubPackagist
94 | 95 |

Integration

96 |

To integrate the code generator you can take a look at the following options. For more advanced 97 | integration options you can also take a look at the SDKgen project.

98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 |
NameGitHubLink
DockerGitHubDocker
GitHub ActionGitHubMarketplace
124 | 125 |

Frontend

126 |

127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 |
NameDescription
ngx-typeschema-editorAn Angular component to visual edit a TypeSchema
145 | 146 |

Project

147 |

148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 |
NameDescription
TypeHubA service to manage and share TypeSchema specifications
TypeAPIAn OpenAPI alternative to describe REST APIs for type-safe code generation
SDKgenA service to generate client SDKs
174 | 175 |
176 | Edit this page 177 |
178 |
179 | 180 | 181 | -------------------------------------------------------------------------------- /www/resources/template/example.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 |
11 |

examples

12 | $example): ?> 13 |
14 |
15 |
16 |

title; ?>

17 |
description; ?>
18 |
schema; ?>
19 |
20 |
21 |
22 | types as $type => $code): ?> 23 | 24 | $chunk): ?> 25 |
26 |

27 |
28 |
29 | 30 | 31 |
32 |

Output

33 |
34 |
35 | 36 | 37 |
38 |
39 |
40 | 41 | 42 |
43 | Edit this page 44 |
45 |
46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /www/resources/template/generator.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 |
11 |

DTO Generator

12 |

This list gives you access to the reference code generator implementation. 13 | To prevent misuse the code generator is protected by recaptcha, if you want to invoke the code generator 14 | programmatically please take a look at the SDKgen project 15 | which offers various integration options like an CLI, GitHub action or REST API. 16 |

17 |
18 | 19 |
20 |
21 | $typeTitle): ?> 22 | 23 | 24 |
25 |
26 | 27 |
28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /www/resources/template/generator/form.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 |
11 |

DTO Generator

12 |
13 |
14 |
15 |
16 | 17 |
18 |
19 | 20 |
21 | 22 |
23 |
24 |
25 | 26 |
27 | 28 | 29 | 30 | Report Issue 31 |
32 |
33 | 34 | $chunk): ?> 35 |
36 |

37 |
38 |
39 | 40 | 41 |
42 |

Output

43 |
44 |
45 | 46 | 47 |
48 |
49 |
50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /www/resources/template/history.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 |
11 |

History

12 |

TypeSchema has evolved while developing a new Swagger/OpenAPI code generator. 13 | During this development process we have noticed many problems 14 | which a code generator needs to solve in 15 | order to generate clean code. We have tried to solve those problems and over time TypeSchema has evolved into a separate specification which describes JSON payloads 16 | and is optimized for code generation.

17 | 18 |

Type-safe programming languages

19 |

If we look at the history of programming languages we see a trend towards type-safety. Besides strongly typed programming languages 20 | like Java, C# and Go which are automatically type-safe also weakly typed languages have added support for more type-safety, like i.e. 21 | PHP, Python and even Ruby which have all improved and added support for type-hints, for JavaScript Microsoft has even developed 22 | TypeScript which adds type-safety to JavaScript.

23 | 24 |

A big reason for this trend is that type-safe applications make it easier to find or prevent bugs. 25 | Since at you code you always have explicit properties which you can access and in case the schema changes your will directly 26 | get an error, that the property no longer exist.

27 | 28 |

TypeSchema can help you to build those type-safe applications by automatically generating clean DTOs to represent JSON payload.

29 | 30 |

Thought model

31 |

TypeSchema has a specific thought model which fits perfectly with classical OOP languages like Java, C#, PHP or TypeScript. 32 | But it supports also languages like Go, which have a different approach. In this case the generator will try to apply these concepts on 33 | generation so that you can still use them at the specification. For example Go does not support inheritance, in this case the code generator 34 | copies all inherited properties into the structure.

35 | 36 |
    37 |
  • TypeSchema is designed to model data structures
  • 38 |
  • TypeSchema abstracts OOP concepts like inheritance, polymorphism and generics
  • 39 |
  • TypeSchema encourages code-first, this means it is easy possible to generate a TypeSchema through reflection without additional annotations
  • 40 |
  • TypeSchema has no keywords to validate data
  • 41 |
42 | 43 |

What is the difference to JSON Schema?

44 |

JSON Schema is a constraint system 45 | which is designed to validate JSON data. Such a constraint system is not great for code generation, with TypeSchema 46 | our focus is to model data to be able to generate high quality code.

47 | 48 |

For code generators it is difficult to work with JSON Schema since it is designed to validate JSON data. In JSON 49 | Schema you dont need to provide any keywords i.e. {} is a valid JSON Schema which basically allows every 50 | value and the defined keywords are applied based on the actual data. This means you can interpret a schema only if you 51 | have also the actual data. A code generator on the other hand needs to determine a concrete type of a schema without 52 | the actual data.

53 | 54 |

JSON Schema has many keywords which contain logic like dependencies, not, 55 | if/then/else which are basically not needed for code generators and really complicates building them.

56 | 57 |

TypeSchema does not work with JSON pointer. In TypeSchema you reference every type simply by the name i.e. 58 | Student. In JSON Schema you need to provide the path i.e. #/definitions/Student to the 59 | schema. In TypeSchema you can also reference only local types. If you want to import a remote schema you need to 60 | explicit declare it via import.

61 | 62 |

I have a problem with the generated code?

63 |

In case there are problems with the generated code you can always create an issue at our repository.

64 | 65 |

I have a question regarding the project?

66 |

You can contact us directly in case you have a question regarding 67 | the project, or you can also take a look at our repository.

68 | 69 |
70 | Edit this page 71 |
72 |
73 | 74 | 75 | -------------------------------------------------------------------------------- /www/resources/template/inc/footer.php: -------------------------------------------------------------------------------- 1 | 2 | 36 | 37 |
part of the Apioo-Project
38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /www/resources/template/inc/header.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <?php if(isset($title)): ?><?php echo $title; ?><?php else: ?>TypeSchema<?php endif; ?> 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 26 | 27 | 28 | 55 | -------------------------------------------------------------------------------- /www/resources/template/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |
6 |

TypeSchema

7 |

TypeSchema is a JSON specification to describe data models.

8 |

9 | Specification 10 | Editor 11 | Generator 12 |

13 |
14 |
15 | 16 |
17 |
18 |
19 |

Features

20 |

21 |
    22 |
  • An elegant specification optimized for code-generation
  • 23 |
  • A portable format to share data models across different programming languages
  • 24 |
  • Generate clean and simple to use DTOs
  • 25 |
  • Handle advanced concepts like inheritance, polymorphism and generics
  • 26 |
  • Use reflection to easily turn any class into a TypeSchema specification
  • 27 |
  • Easily implement your own code generator
  • 28 |
29 |
30 |
31 |
32 |
33 |
34 |

Examples

35 |

At the following list you can take a look at example output for each supported programming language.

36 |
37 | 38 |
39 |
40 | $typeTitle): ?> 41 | 42 | 43 |
44 |
45 | 46 |
47 |
48 | Edit this page 49 |
50 |
51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /www/resources/template/integration.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 |
11 |

Integration

12 |

This list provides examples how you can work with the generated DTOs, it shows how you can read raw JSON data 13 | into a DTO and transform the DTO back into raw JSON data. Since the generator uses mostly standard or well-known libraries 14 | most developers should be familiar with this process. 15 |

16 |
17 | 18 |
19 |
20 | $typeTitle): ?> 21 | 22 | 23 |
24 |
25 | 26 |
27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /www/resources/template/integration/csharp_description.html: -------------------------------------------------------------------------------- 1 | The C# TypeSchema integration uses the native System.Text.Json 2 | implementation, this means you don`t need to install any additional package to use the generated DTOs. -------------------------------------------------------------------------------- /www/resources/template/integration/csharp_dto.txt: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace TypeSchema.DTO; 4 | 5 | /// 6 | /// A simple student struct 7 | /// 8 | public class Student 9 | { 10 | [JsonPropertyName("firstName")] 11 | public string? FirstName { get; set; } 12 | 13 | [JsonPropertyName("lastName")] 14 | public string? LastName { get; set; } 15 | 16 | [JsonPropertyName("age")] 17 | public int? Age { get; set; } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /www/resources/template/integration/csharp_integration.txt: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace TypeSchema.DTO; 4 | 5 | class Program 6 | { 7 | static void Main() 8 | { 9 | string input = File.ReadAllText("./input.json"); 10 | 11 | Student student = JsonSerializer.Deserialize(input); 12 | 13 | string output = JsonSerializer.Serialize(student); 14 | 15 | File.WriteAllText("./output.json", output); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /www/resources/template/integration/detail.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 |
11 |

Integration

12 |
13 |
14 |
15 |
16 |

DTO

17 |
18 |
19 |
20 |
21 |
22 |

Integration

23 |
24 |
25 |
26 |
27 |
28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /www/resources/template/integration/go_description.html: -------------------------------------------------------------------------------- 1 | The Go TypeSchema integration uses the native JSON implementation, this means you don`t need to install any additional package to use the generated DTOs. 2 | -------------------------------------------------------------------------------- /www/resources/template/integration/go_dto.txt: -------------------------------------------------------------------------------- 1 | package TypeSchema 2 | 3 | // A simple student struct 4 | type Student struct { 5 | FirstName string `json:"firstName"` 6 | LastName string `json:"lastName"` 7 | Age int `json:"age"` 8 | } 9 | -------------------------------------------------------------------------------- /www/resources/template/integration/go_integration.txt: -------------------------------------------------------------------------------- 1 | package TypeSchema; 2 | 3 | import "os" 4 | import "encoding/json" 5 | 6 | func main() { 7 | input, errRead := os.ReadFile("./input.json") 8 | if errRead != nil { 9 | panic(errRead) 10 | } 11 | 12 | var student Student 13 | errUnmarshal := json.Unmarshal(input, &student) 14 | if errUnmarshal != nil { 15 | panic(errUnmarshal) 16 | } 17 | 18 | output, errMarshal := json.Marshal(student) 19 | if errMarshal != nil { 20 | panic(errMarshal) 21 | } 22 | 23 | errWrite := os.WriteFile("./output.json", output, 0644) 24 | if errWrite != nil { 25 | panic(errWrite) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /www/resources/template/integration/java_description.html: -------------------------------------------------------------------------------- 1 | The Java TypeSchema integration uses the well known Jackson ObjectMapper library. 2 | To use the generated DTO classes you need to add this library to your project: 3 | 4 |
<dependencies>
 5 |     <dependency>
 6 |         <groupId>com.fasterxml.jackson.core</groupId>
 7 |         <artifactId>jackson-core</artifactId>
 8 |         <version>2.13.4</version>
 9 |     </dependency>
10 |     <dependency>
11 |         <groupId>com.fasterxml.jackson.core</groupId>
12 |         <artifactId>jackson-databind</artifactId>
13 |         <version>2.13.4</version>
14 |     </dependency>
15 |     <dependency>
16 |         <groupId>com.fasterxml.jackson.datatype</groupId>
17 |         <artifactId>jackson-datatype-jsr310</artifactId>
18 |         <version>2.13.4</version>
19 |     </dependency>
20 | </dependencies>
21 | 
22 | -------------------------------------------------------------------------------- /www/resources/template/integration/java_dto.txt: -------------------------------------------------------------------------------- 1 | package org.typeschema.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonGetter; 4 | import com.fasterxml.jackson.annotation.JsonSetter; 5 | 6 | /** 7 | * A simple student struct 8 | */ 9 | public class Student { 10 | private String firstName; 11 | private String lastName; 12 | private Integer age; 13 | 14 | @JsonSetter("firstName") 15 | public void setFirstName(String firstName) { 16 | this.firstName = firstName; 17 | } 18 | 19 | @JsonGetter("firstName") 20 | public String getFirstName() { 21 | return this.firstName; 22 | } 23 | 24 | @JsonSetter("lastName") 25 | public void setLastName(String lastName) { 26 | this.lastName = lastName; 27 | } 28 | 29 | @JsonGetter("lastName") 30 | public String getLastName() { 31 | return this.lastName; 32 | } 33 | 34 | @JsonSetter("age") 35 | public void setAge(Integer age) { 36 | this.age = age; 37 | } 38 | 39 | @JsonGetter("age") 40 | public Integer getAge() { 41 | return this.age; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /www/resources/template/integration/java_integration.txt: -------------------------------------------------------------------------------- 1 | package org.typeschema.dto; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.io.IOException; 7 | 8 | public class Main { 9 | public static void main(String[] args) throws IOException { 10 | String input = Files.readString(Path.of("./input.json")); 11 | 12 | ObjectMapper objectMapper = (new ObjectMapper()) 13 | .findAndRegisterModules() 14 | .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 15 | 16 | Student student = objectMapper.readValue(input, Student.class); 17 | 18 | String output = objectMapper.writeValueAsString(student); 19 | 20 | Files.write(Path.of("./output.json"), output.getBytes()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /www/resources/template/integration/php_description.html: -------------------------------------------------------------------------------- 1 | The PHP TypeSchema integration uses the psx/schema library. To use the generated DTO classes you need 2 | to add the following composer dependency: 3 | 4 |
{
 5 |   "require": {
 6 |     "psx/schema": "^7.0"
 7 |   }
 8 | }
 9 | 
10 | -------------------------------------------------------------------------------- /www/resources/template/integration/php_dto.txt: -------------------------------------------------------------------------------- 1 | firstName = $firstName; 18 | } 19 | public function getFirstName() : ?string 20 | { 21 | return $this->firstName; 22 | } 23 | public function setLastName(?string $lastName) : void 24 | { 25 | $this->lastName = $lastName; 26 | } 27 | public function getLastName() : ?string 28 | { 29 | return $this->lastName; 30 | } 31 | public function setAge(?int $age) : void 32 | { 33 | $this->age = $age; 34 | } 35 | public function getAge() : ?int 36 | { 37 | return $this->age; 38 | } 39 | public function toRecord() : \PSX\Record\RecordInterface 40 | { 41 | /** @var \PSX\Record\Record $record */ 42 | $record = new \PSX\Record\Record(); 43 | $record->put('firstName', $this->firstName); 44 | $record->put('lastName', $this->lastName); 45 | $record->put('age', $this->age); 46 | return $record; 47 | } 48 | public function jsonSerialize() : object 49 | { 50 | return (object) $this->toRecord()->getAll(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /www/resources/template/integration/php_integration.txt: -------------------------------------------------------------------------------- 1 | readJson($data, \PSX\Schema\SchemaSource::fromClass(Student::class)); 10 | 11 | file_put_contents(__DIR__ . '/output.json', \json_encode($student)); 12 | -------------------------------------------------------------------------------- /www/resources/template/integration/python_description.html: -------------------------------------------------------------------------------- 1 | The Python TypeSchema integration uses the well known Pydantic library. 2 | To use the generated DTO classes you need to add this library to your project: 3 | 4 |
pydantic~=2.9.2
5 | -------------------------------------------------------------------------------- /www/resources/template/integration/python_dto.txt: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field, GetCoreSchemaHandler 2 | from pydantic_core import CoreSchema, core_schema 3 | from typing import Any, Dict, Generic, List, Optional, TypeVar 4 | 5 | 6 | # A simple student struct 7 | class Student(BaseModel): 8 | first_name: Optional[str] = Field(default=None, alias="firstName") 9 | last_name: Optional[str] = Field(default=None, alias="lastName") 10 | age: Optional[int] = Field(default=None, alias="age") 11 | pass 12 | 13 | -------------------------------------------------------------------------------- /www/resources/template/integration/python_integration.txt: -------------------------------------------------------------------------------- 1 | input = Path('input.json').read_text() 2 | 3 | student = Student.model_validate_json(input) 4 | 5 | output = student.model_dump_json() 6 | 7 | Path('output.json').write_text(output) 8 | 9 | -------------------------------------------------------------------------------- /www/resources/template/integration/rust_description.html: -------------------------------------------------------------------------------- 1 | The Rust TypeSchema integration uses the well known Serde library. 2 | To use the generated DTO classes you need to add this library to your project: 3 | 4 |
[dependencies]
5 | serde = { version = "1.0", features = ["derive"] }
6 | serde_json = "1.0"
7 | chrono = { version = "0.4", features = ["serde"] }
8 | 
9 | -------------------------------------------------------------------------------- /www/resources/template/integration/rust_dto.txt: -------------------------------------------------------------------------------- 1 | use serde::{Serialize, Deserialize}; 2 | 3 | // A simple student struct 4 | #[derive(Serialize, Deserialize)] 5 | pub struct Student { 6 | #[serde(rename = "firstName")] 7 | first_name: Option, 8 | 9 | #[serde(rename = "lastName")] 10 | last_name: Option, 11 | 12 | #[serde(rename = "age")] 13 | age: Option, 14 | 15 | } 16 | -------------------------------------------------------------------------------- /www/resources/template/integration/rust_integration.txt: -------------------------------------------------------------------------------- 1 | extern crate serde; 2 | extern crate serde_json; 3 | extern crate chrono; 4 | 5 | use std::fs; 6 | use student::Student; 7 | 8 | mod student; 9 | 10 | fn main() { 11 | let input = fs::read_to_string("input.json").expect("Could not read input"); 12 | 13 | let student: Student = serde_json::from_str(&input).unwrap(); 14 | 15 | let output = serde_json::to_string(&student).unwrap(); 16 | 17 | fs::write("output.json", output).expect("Could not write output"); 18 | } 19 | -------------------------------------------------------------------------------- /www/resources/template/integration/typescript_description.html: -------------------------------------------------------------------------------- 1 | The TypeScript TypeSchema integration uses only interfaces to describe the JSON payload. 2 | Note TypeScript is a special case since with TypeScript we only describe the JSON payload 3 | we don`t actually parse the JSON payload and transform it into actual objects. 4 | -------------------------------------------------------------------------------- /www/resources/template/integration/typescript_dto.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * A simple student struct 3 | */ 4 | export interface Student { 5 | firstName?: string 6 | lastName?: string 7 | age?: number 8 | } 9 | -------------------------------------------------------------------------------- /www/resources/template/integration/typescript_integration.txt: -------------------------------------------------------------------------------- 1 | const student: Student = JSON.parse('{...}'); 2 | 3 | const output = JSON.stringify(student); 4 | 5 | console.log(output); 6 | -------------------------------------------------------------------------------- /www/resources/template/integration/visualbasic_description.html: -------------------------------------------------------------------------------- 1 | The VisualBasic TypeSchema integration uses the native System.Text.Json 2 | implementation, this means you don`t need to install any additional package to use the generated DTOs. -------------------------------------------------------------------------------- /www/resources/template/integration/visualbasic_dto.txt: -------------------------------------------------------------------------------- 1 | Imports System.Text.Json.Serialization 2 | 3 | ' A simple student struct 4 | Public Class Student 5 | 6 | Public Property FirstName As String 7 | 8 | 9 | Public Property LastName As String 10 | 11 | 12 | Public Property Age As Integer 13 | 14 | End Class 15 | -------------------------------------------------------------------------------- /www/resources/template/integration/visualbasic_integration.txt: -------------------------------------------------------------------------------- 1 | Imports System.Text.Json 2 | Imports Generator.Generator 3 | Imports Microsoft.VisualBasic.FileIO 4 | 5 | Module Program 6 | Sub Main(args as String()) 7 | Dim input As String 8 | Dim output As String 9 | Dim student As Student 10 | 11 | input = FileSystem.ReadAllText("input.json") 12 | 13 | student = JsonSerializer.Deserialize(Of Student)(input) 14 | 15 | output = JsonSerializer.Serialize(student) 16 | 17 | FileSystem.WriteAllText("output.json", output, True) 18 | End Sub 19 | End Module 20 | -------------------------------------------------------------------------------- /www/resources/template/specification.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 |
11 |

Specification

12 | 13 |

Table of Contents

14 | 40 | 41 |
42 | 43 | 44 |

Introduction

45 | 46 |

This document describes the TypeSchema specification. 47 | TypeSchema is a JSON format to model data structures. It abstracts common OOP concepts like inheritance, polymorphism and generics 48 | into a simple and deterministic JSON format which can be transformed into code for many different programming languages. 49 | The main use case of TypeSchema is to describe data models, it is not designed to validate JSON structures. A data model 50 | described in TypeSchema can be used as single source of truth, which can be used across many different environments.

51 | 52 |
53 | 54 | 55 |

Design

56 | 57 |

TypeSchema distinguishes between two types, a Definition-Type and a Property-Type. 58 | A Definition-Type is an entry under the definitions keyword and a Property-Type can only be used inside such a Definition-Type. 59 | The following example illustrates how the Definition-Types and Property-Types are nested.

60 | 61 |
{
 62 |     "definitions": {
 63 |         "TypeA": {
 64 |             "type": "struct",
 65 |             "properties": {
 66 |                 "PropertyA": { Property-Type }
 67 |             }
 68 |         },
 69 |         "TypeB": {
 70 |             "type": "map",
 71 |             "schema": { Property-Type }
 72 |         },
 73 |         "TypeC": { Definition-Type }
 74 |     },
 75 |     "root": "TypeA"
 76 | }
77 | 78 | 79 |

Definition Types

80 |

81 | 82 | 83 |

struct

84 |

A struct represents a class/structure with a fix set of defined properties.

85 |
{
 86 |     "type": "struct",
 87 |     "parent": { Reference-Type },
 88 |     "base": true,
 89 |     "properties": {
 90 |         "PropertyA": { Property-Type }
 91 |     },
 92 |     "discriminator": "type",
 93 |     "mapping": {
 94 |         "CatType": "cat",
 95 |         "DogType": "dog",
 96 |     }
 97 | }
98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 114 | 115 | 116 | 117 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 |
PropertyDescription
parentDefines a parent type for this structure. Some programming languages like Go do not support the concept of an extends, in this case the code 113 | generator simply copies all properties into this structure.
baseIndicates whether this is a base structure, default is false. If true the structure is used a base type, this means it is not possible 118 | to create an instance from this structure.
propertiesContains a map where they key is the property name and the value must be a Property-Type.
discriminatorOptional the property name of a discriminator property. This should be only used in case this is also a base structure.
mappingIn case a discriminator is configured it is required to configure a mapping. The mapping is a map where the key is the type name and the value the actual discriminator type value.
134 | 135 |
136 | 137 | 138 |

map

139 |

A map represents a map/dictionary with variable key/value entries of the same type.

140 |
{
141 |     "type": "map",
142 |     "schema": { Property-Type }
143 | }
144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 |
PropertyDescription
schemaThe Property-Type which defines the value of the map.
162 | 163 |
164 | 165 | 166 |

array

167 |

An array represents an array/list with variable entries of the same type.

168 |
{
169 |     "type": "array",
170 |     "schema": { Property-Type }
171 | }
172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 |
PropertyDescription
schemaThe Property-Type which defines the entry of the array.
190 | 191 |
192 | 193 | 194 |

Property Types

195 |

196 | 197 | 198 |

string

199 |
{
200 |     "type": "string",
201 |     "format": "date-time"
202 | }
203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 219 | 220 | 221 |
PropertyDescription
formatOptional describes the format of the string. Supported are the following types: date, date-time and time. 218 | A code generator may use a fitting data type to represent such a format, if not supported it should fall back to a string.
222 | 223 |
224 | 225 | 226 |

integer

227 |
{
228 |     "type": "integer"
229 | }
230 | 231 |
232 | 233 | 234 |

number

235 |
{
236 |     "type": "number"
237 | }
238 | 239 |
240 | 241 | 242 |

boolean

243 |
{
244 |     "type": "boolean"
245 | }
246 | 247 |
248 | 249 | 250 |

map

251 |

A map represents a map/dictionary with variable key/value entries of the same type. 252 | The code generator uses the native map/dictionary type of the programming language.

253 |
{
254 |     "type": "map",
255 |     "schema": { Property-Type }
256 | }
257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 |
PropertyDescription
schemaThe Property-Type which defines the value of the map.
275 | 276 |
277 | 278 | 279 |

array

280 |

An array represents an array/list with variable entries of the same type. 281 | The code generator uses the native array/list type of the programming language.

282 |
{
283 |     "type": "array",
284 |     "schema": { Property-Type }
285 | }
286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 |
PropertyDescription
schemaThe Property-Type which defines the entry of the array.
304 | 305 |
306 | 307 | 308 |

any

309 |
{
310 |     "type": "any"
311 | }
312 | 313 |
314 | 315 | 316 |

generic

317 |
{
318 |     "type": "generic",
319 |     "name": "T"
320 | }
321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 337 | 338 | 339 |
PropertyDescription
nameThe name of the generic, it is recommended to use common generic names like T or TValue. These generics 336 | can then be replaced on usage with a concrete type through the template property at a reference.
340 | 341 |
342 | 343 | 344 |

reference

345 |
{
346 |     "type": "reference",
347 |     "target": "TypeB",
348 |     "template": {
349 |         "T": "TypeC"
350 |     }
351 | }
352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 373 | 374 | 375 |
PropertyDescription
targetThe target type, this must be a key which is available under the definitions keyword.
templateA map where the key is the name of the generic and the value must point to a key under the definitions keyword. 371 | This can be used in case the target points to a type which contains generics, then it is possible to replace those generics 372 | with a concrete type.
376 | 377 |
378 | 379 | 380 |

Import

381 |

Optional it is possible to import other TypeSchema documents through the import keyword. It contains a map 382 | where the key is the namespace and the value points to a remote document. The value is a URL and a code generator should 383 | support at least the following schemes: file, http, https.

384 | 385 |
{
386 |     "import": {
387 |         "MyNamespace": "file:///my_schema.json"
388 |     }
389 | }
390 | 391 |

Inside a reference it is then possible to reuse all types under the namespace which are defined at the remote document i.e.:

392 | 393 |
{
394 |     "type": "reference",
395 |     "target": "MyNamespace:MyType"
396 | }
397 | 398 | 399 |

Root

400 |

In some circumstances a parse needs to know the root type of your specification, through the root 401 | keyword it is possible to define such a root type.

402 | 403 |
{
404 |     "definitions": {
405 |         "TypeA": { ... }
406 |     },
407 |     "root": "TypeA"
408 | }
409 | 410 |
411 | 412 |
413 | Edit this page 414 |
415 |
416 | 417 | 418 | 419 | 420 | -------------------------------------------------------------------------------- /www/resources/template/tools.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 |
11 |

Tools

12 |
13 |
14 |
15 |
  • Migration
  • 16 | JSON 17 | JSON Schema 18 | OpenAPI 19 |
    20 |
    21 |
    22 |
    23 |
  • Generate
  • 24 | Changelog 25 |
    26 |
    27 |
    28 |
    29 | 30 | 31 | -------------------------------------------------------------------------------- /www/resources/template/tools/changelog.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 |
    11 |

    Changelog

    12 |

    Through this form you can generate a changelog between two TypeSchema versions.

    13 |
    14 |
    15 |
    16 |
    17 |
    18 | 19 | 20 |
    21 |
    22 |
    23 |
    24 | 25 | 26 |
    27 |
    28 |
    29 | 30 |
    31 |
    32 |
    33 |
    34 |
    35 |
      36 | 37 | 38 |
    • [MAJOR]:
    • 39 | 40 |
    • [MINOR]:
    • 41 | 42 |
    • [PATCH]:
    • 43 | 44 | 45 |
    46 |
    47 |
    48 |
    49 |
    50 | 51 | 52 | -------------------------------------------------------------------------------- /www/resources/template/tools/json.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 |
    11 |

    JSON

    12 |

    13 | Through this form you can generate a schema from existing JSON data. It contains also a logic to detect objects of 14 | the same type. You should see this as a starting point since you need to add proper names to each type. 15 |

    16 |
    17 |
    18 |
    19 |
    20 |
    21 | 22 | 23 |
    24 | 25 |
    26 |
    27 |
    28 |
    29 |
    30 |
    31 |
    32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /www/resources/template/tools/jsonschema.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 |
    11 |

    JSON Schema

    12 |

    13 | Through this form you can migrate an existing JSON Schema to a TypeSchema. If you see a name like i.e. Inlinec650cd78 14 | this means that there is an anonymous type in your schema, the migration makes this only visible and you should set 15 | a meaningful name for this type. 16 |

    17 |
    18 |
    19 |
    20 |
    21 |
    22 | 23 | 24 |
    25 | 26 |
    27 |
    28 |
    29 |
    30 |
    31 |
    32 |
    33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /www/resources/template/tools/openapi.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 |
    11 |

    OpenAPI

    12 |

    13 | Through this form you can migrate an existing OpenAPI specification to TypeSchema. This allows you to use the OpenAPI 14 | spec to generate a client SDK. 15 |

    16 |
    17 |
    18 |
    19 |
    20 |
    21 | 22 | 23 |
    24 | 25 |
    26 |
    27 |
    28 |
    29 |
    30 |
    31 |
    32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /www/resources/typeschema.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": { 3 | "Generate": { 4 | "type": "struct", 5 | "properties": { 6 | "namespace": { 7 | "type": "string" 8 | }, 9 | "schema": { 10 | "type": "string" 11 | }, 12 | "g-recaptcha-response": { 13 | "type": "string" 14 | } 15 | } 16 | }, 17 | "Diff": { 18 | "type": "struct", 19 | "properties": { 20 | "left": { 21 | "type": "string" 22 | }, 23 | "right": { 24 | "type": "string" 25 | } 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /www/src/Controller/Developer.php: -------------------------------------------------------------------------------- 1 | 'Developer | TypeSchema', 23 | 'method' => explode('::', __METHOD__), 24 | ]; 25 | 26 | $templateFile = __DIR__ . '/../../resources/template/developer.php'; 27 | return new Template($data, $templateFile, $this->reverseRouter); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /www/src/Controller/Ecosystem.php: -------------------------------------------------------------------------------- 1 | 'Ecosystem | TypeSchema', 23 | 'method' => explode('::', __METHOD__), 24 | ]; 25 | 26 | $templateFile = __DIR__ . '/../../resources/template/ecosystem.php'; 27 | return new Template($data, $templateFile, $this->reverseRouter); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /www/src/Controller/Example.php: -------------------------------------------------------------------------------- 1 | cache->getItem('example-cache-' . $type); 35 | if (!$item->isHit()) { 36 | $examples = $this->getExamples(); 37 | foreach ($examples as $key => $example) { 38 | $examples[$key]['schema'] = file_get_contents($example['file']); 39 | $config = $example['config'][$type] ?? []; 40 | $examples[$key]['types'][$type] = $this->convert($type, $example['file'], $config['namespace'] ?? null, $config['mapping'] ?? null); 41 | } 42 | 43 | $item->expiresAfter(null); 44 | $item->set($examples); 45 | $this->cache->save($item); 46 | } else { 47 | $examples = $item->get(); 48 | } 49 | 50 | $data = [ 51 | 'title' => TypeName::getDisplayName($type) . ' Example | TypeSchema', 52 | 'method' => explode('::', __METHOD__), 53 | 'parameters' => ['type' => $type], 54 | 'type' => TypeName::getDisplayName($type), 55 | 'examples' => $examples, 56 | ]; 57 | 58 | $templateFile = __DIR__ . '/../../resources/template/example.php'; 59 | return new Template($data, $templateFile, $this->reverseRouter); 60 | } 61 | 62 | private function convert(string $type, string $file, ?string $namespace, ?array $mapping): string|array 63 | { 64 | $config = new Config(); 65 | $config->put(Config::NAMESPACE, $namespace); 66 | $config->put(Config::MAPPING, $mapping); 67 | 68 | $context = new FilesystemContext(__DIR__ . '/../../resources/examples'); 69 | $schema = $this->schemaManager->getSchema($file, $context); 70 | 71 | $factory = new GeneratorFactory(); 72 | $generator = $factory->getGenerator($type, $config); 73 | 74 | $result = $generator->generate($schema); 75 | if ($result instanceof Chunks && $generator instanceof FileAwareInterface) { 76 | $chunks = []; 77 | foreach ($result->getChunks() as $fileName => $code) { 78 | if (is_string($code)) { 79 | $chunks[$generator->getFileName($fileName)] = $generator->getFileContent($code); 80 | } 81 | } 82 | return $chunks; 83 | } else { 84 | return (string) $result; 85 | } 86 | } 87 | 88 | private function getExamples(): array 89 | { 90 | $examples = []; 91 | $examples[] = [ 92 | 'title' => 'Simple model', 93 | 'description' => 'A simple model with some scalar properties.', 94 | 'file' => __DIR__ . '/../../resources/examples/simple.json', 95 | 'config' => [ 96 | 'csharp' => ['namespace' => 'TypeSchema.DTO'], 97 | 'go' => ['namespace' => 'TypeSchema'], 98 | 'java' => ['namespace' => 'org.typeschema.dto'], 99 | 'php' => ['namespace' => 'TypeSchema\\DTO'], 100 | 'ruby' => ['namespace' => 'TypeSchema'], 101 | ], 102 | ]; 103 | 104 | $examples[] = [ 105 | 'title' => 'Model with inheritance', 106 | 'description' => 'A student struct which extends from a different struct.', 107 | 'file' => __DIR__ . '/../../resources/examples/inheritance.json', 108 | 'config' => [ 109 | 'csharp' => ['namespace' => 'TypeSchema.DTO'], 110 | 'go' => ['namespace' => 'TypeSchema'], 111 | 'java' => ['namespace' => 'org.typeschema.dto'], 112 | 'php' => ['namespace' => 'TypeSchema\\DTO'], 113 | 'ruby' => ['namespace' => 'TypeSchema'], 114 | ], 115 | ]; 116 | 117 | $examples[] = [ 118 | 'title' => 'Model with reference', 119 | 'description' => 'A student struct which reference a faculty struct.', 120 | 'file' => __DIR__ . '/../../resources/examples/reference.json', 121 | 'config' => [ 122 | 'csharp' => ['namespace' => 'TypeSchema.DTO'], 123 | 'go' => ['namespace' => 'TypeSchema'], 124 | 'java' => ['namespace' => 'org.typeschema.dto'], 125 | 'php' => ['namespace' => 'TypeSchema\\DTO'], 126 | 'ruby' => ['namespace' => 'TypeSchema'], 127 | ], 128 | ]; 129 | 130 | $examples[] = [ 131 | 'title' => 'Map with string values', 132 | 'description' => 'A student struct which contains a map with arbitrary string values.', 133 | 'file' => __DIR__ . '/../../resources/examples/map_inline.json', 134 | 'config' => [ 135 | 'csharp' => ['namespace' => 'TypeSchema.DTO'], 136 | 'go' => ['namespace' => 'TypeSchema'], 137 | 'java' => ['namespace' => 'org.typeschema.dto'], 138 | 'php' => ['namespace' => 'TypeSchema\\DTO'], 139 | 'ruby' => ['namespace' => 'TypeSchema'], 140 | ], 141 | ]; 142 | 143 | $examples[] = [ 144 | 'title' => 'Model with discriminator', 145 | 'description' => 'A struct which uses a discriminator mapping.', 146 | 'file' => __DIR__ . '/../../resources/examples/discriminator.json', 147 | 'config' => [ 148 | 'csharp' => ['namespace' => 'TypeSchema.DTO'], 149 | 'go' => ['namespace' => 'TypeSchema'], 150 | 'java' => ['namespace' => 'org.typeschema.dto'], 151 | 'php' => ['namespace' => 'TypeSchema\\DTO'], 152 | 'ruby' => ['namespace' => 'TypeSchema'], 153 | ], 154 | ]; 155 | 156 | $examples[] = [ 157 | 'title' => 'Model with generics', 158 | 'description' => 'A struct which uses generics.', 159 | 'file' => __DIR__ . '/../../resources/examples/generic.json', 160 | 'config' => [ 161 | 'csharp' => ['namespace' => 'TypeSchema.DTO'], 162 | 'go' => ['namespace' => 'TypeSchema'], 163 | 'java' => ['namespace' => 'org.typeschema.dto'], 164 | 'php' => ['namespace' => 'TypeSchema\\DTO'], 165 | 'ruby' => ['namespace' => 'TypeSchema'], 166 | ], 167 | ]; 168 | 169 | $examples[] = [ 170 | 'title' => 'Import other TypeSchema specification', 171 | 'description' => 'A struct which references an external TypeSchema.', 172 | 'file' => __DIR__ . '/../../resources/examples/import.json', 173 | 'config' => [ 174 | 'csharp' => ['namespace' => 'TypeSchema.DTO'], 175 | 'go' => ['namespace' => 'TypeSchema'], 176 | 'java' => ['namespace' => 'org.typeschema.dto'], 177 | 'php' => ['namespace' => 'TypeSchema\\DTO'], 178 | 'ruby' => ['namespace' => 'TypeSchema'], 179 | 'typescript' => ['mapping' => ['my_ns' => './']] 180 | ], 181 | ]; 182 | 183 | return $examples; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /www/src/Controller/Generator.php: -------------------------------------------------------------------------------- 1 | 'DTO Generator | TypeSchema', 47 | 'method' => explode('::', __METHOD__), 48 | 'types' => array_chunk($types, (int) ceil(count($types) / 2), true), 49 | ]; 50 | 51 | $templateFile = __DIR__ . '/../../resources/template/generator.php'; 52 | return new Template($data, $templateFile, $this->reverseRouter); 53 | } 54 | 55 | #[Get] 56 | #[Path('/generator/:type')] 57 | public function showType(string $type): mixed 58 | { 59 | if (!in_array($type, GeneratorFactory::getPossibleTypes())) { 60 | throw new BadRequestException('Provided an invalid type'); 61 | } 62 | 63 | $data = [ 64 | 'title' => TypeName::getDisplayName($type) . ' DTO Generator | TypeSchema', 65 | 'method' => explode('::', __METHOD__), 66 | 'parameters' => ['type' => $type], 67 | 'schema' => $this->getSchema(), 68 | 'type' => $type, 69 | 'typeName' => TypeName::getDisplayName($type), 70 | 'js' => ['https://www.google.com/recaptcha/api.js'], 71 | 'recaptcha_key' => $this->config->get('recaptcha_key'), 72 | ]; 73 | 74 | $templateFile = __DIR__ . '/../../resources/template/generator/form.php'; 75 | return new Template($data, $templateFile, $this->reverseRouter); 76 | } 77 | 78 | #[Post] 79 | #[Path('/generator/:type')] 80 | public function generate(string $type, Generate $generate): mixed 81 | { 82 | [$namespace, $schema, $config, $parsedSchema] = $this->parse($type, $generate); 83 | 84 | try { 85 | $generator = (new GeneratorFactory())->getGenerator($type, $config); 86 | $result = $generator->generate($parsedSchema); 87 | 88 | if ($result instanceof Chunks && $generator instanceof FileAwareInterface) { 89 | $chunks = []; 90 | foreach ($result->getChunks() as $fileName => $code) { 91 | if (is_string($code)) { 92 | $chunks[$generator->getFileName($fileName)] = $generator->getFileContent($code); 93 | } 94 | } 95 | $output = $chunks; 96 | } else { 97 | $output = (string) $result; 98 | } 99 | } catch (\Throwable $e) { 100 | $output = $e->getMessage(); 101 | } 102 | 103 | $data = [ 104 | 'title' => TypeName::getDisplayName($type) . ' DTO Generator | TypeSchema', 105 | 'method' => explode('::', __METHOD__), 106 | 'parameters' => ['type' => $type], 107 | 'namespace' => $namespace, 108 | 'schema' => $schema, 109 | 'type' => $type, 110 | 'typeName' => TypeName::getDisplayName($type), 111 | 'output' => $output, 112 | 'js' => ['https://www.google.com/recaptcha/api.js'], 113 | 'recaptcha_key' => $this->config->get('recaptcha_key'), 114 | ]; 115 | 116 | $templateFile = __DIR__ . '/../../resources/template/generator/form.php'; 117 | return new Template($data, $templateFile, $this->reverseRouter); 118 | } 119 | 120 | #[Post] 121 | #[Path('/generator/:type/download')] 122 | public function download(#[Param] string $type, #[Body] Generate $generate): mixed 123 | { 124 | [$namespace, $schema, $config, $parsedSchema] = $this->parse($type, $generate); 125 | 126 | try { 127 | $zipFile = $this->config->get('psx_path_cache') . '/typeschema_' . $type . '_' . sha1($schema) . '.zip'; 128 | if (is_file($zipFile)) { 129 | return new File($zipFile, 'typeschema_' . $type . '.zip', 'application/zip'); 130 | } 131 | 132 | $generator = (new GeneratorFactory())->getGenerator($type, $config); 133 | $result = $generator->generate($parsedSchema); 134 | 135 | if ($result instanceof Chunks && $generator instanceof FileAwareInterface) { 136 | $output = new Chunks(); 137 | $this->appendOutput($result, $output, $generator); 138 | 139 | $output->writeToZip($zipFile); 140 | 141 | return new File($zipFile, 'typeschema_' . $type . '.zip', 'application/zip'); 142 | } else { 143 | return new HttpResponse(200, ['Content-Type' => 'text/plain'], (string) $result); 144 | } 145 | } catch (\Throwable $e) { 146 | return new HttpResponse(500, ['Content-Type' => 'text/plain'], $e->getMessage()); 147 | } 148 | } 149 | 150 | private function appendOutput(Chunks $result, Chunks $output, FileAwareInterface $generator): void 151 | { 152 | foreach ($result->getChunks() as $identifier => $code) { 153 | if (is_string($code)) { 154 | $output->append($generator->getFileName($identifier), $generator->getFileContent($code)); 155 | } 156 | } 157 | } 158 | 159 | /** 160 | * @return array{string|null, string, Config, SchemaInterface} 161 | */ 162 | private function parse(string $type, Generate $generate): array 163 | { 164 | $recaptchaSecret = $this->config->get('recaptcha_secret'); 165 | if (!empty($recaptchaSecret) && !$this->captchaVerifier->verify($generate->getGRecaptchaResponse())) { 166 | throw new BadRequestException('Invalid captcha'); 167 | } 168 | 169 | $namespace = $generate->getNamespace(); 170 | $schema = $generate->getSchema() ?? throw new \RuntimeException('Provided no schema'); 171 | 172 | if (strlen($schema) > self::MAX_SCHEMA_LENGTH) { 173 | throw new BadRequestException('Provided schema is too large, allowed max ' . self::MAX_SCHEMA_LENGTH . ' characters'); 174 | } 175 | 176 | $config = new Config(); 177 | if ($namespace !== null && $namespace !== '') { 178 | $config->put(Config::NAMESPACE, $namespace); 179 | } 180 | 181 | if (!in_array($type, GeneratorFactory::getPossibleTypes())) { 182 | throw new BadRequestException('Provided an invalid type'); 183 | } 184 | 185 | $result = (new TypeSchema($this->schemaManager))->parse($schema); 186 | 187 | return [$namespace, $schema, $config, $result]; 188 | } 189 | 190 | private function getSchema(): string 191 | { 192 | return <<<'JSON' 193 | { 194 | "definitions": { 195 | "Student": { 196 | "type": "struct", 197 | "properties": { 198 | "firstName": { 199 | "type": "string" 200 | }, 201 | "lastName": { 202 | "type": "string" 203 | }, 204 | "age": { 205 | "type": "integer" 206 | } 207 | } 208 | } 209 | }, 210 | "root": "Student" 211 | } 212 | JSON; 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /www/src/Controller/History.php: -------------------------------------------------------------------------------- 1 | 'History | TypeSchema', 23 | 'method' => explode('::', __METHOD__), 24 | ]; 25 | 26 | $templateFile = __DIR__ . '/../../resources/template/history.php'; 27 | return new Template($data, $templateFile, $this->reverseRouter); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /www/src/Controller/Index.php: -------------------------------------------------------------------------------- 1 | explode('::', __METHOD__), 38 | 'types' => array_chunk($types, (int) ceil(count($types) / 2), true), 39 | ]; 40 | 41 | $templateFile = __DIR__ . '/../../resources/template/index.php'; 42 | return new Template($data, $templateFile, $this->reverseRouter); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /www/src/Controller/Integration.php: -------------------------------------------------------------------------------- 1 | 'Integration | TypeSchema', 38 | 'method' => explode('::', __METHOD__), 39 | 'types' => array_chunk($types, (int) ceil(count($types) / 2), true), 40 | ]; 41 | 42 | $templateFile = __DIR__ . '/../../resources/template/integration.php'; 43 | return new Template($data, $templateFile, $this->reverseRouter); 44 | } 45 | 46 | #[Get] 47 | #[Path('/integration/:type')] 48 | public function showType(string $type): mixed 49 | { 50 | if (!in_array($type, GeneratorFactory::getPossibleTypes())) { 51 | throw new BadRequestException('Provided an invalid type'); 52 | } 53 | 54 | $dtoFile = __DIR__ . '/../../resources/template/integration/' . $type . '_dto.txt'; 55 | $integrationFile = __DIR__ . '/../../resources/template/integration/' . $type . '_integration.txt'; 56 | $descriptionFile = __DIR__ . '/../../resources/template/integration/' . $type . '_description.html'; 57 | 58 | if (!is_file($dtoFile) || !is_file($integrationFile)) { 59 | throw new BadRequestException('No integration available for the provided type'); 60 | } 61 | 62 | $data = [ 63 | 'title' => TypeName::getDisplayName($type) . ' Integration | TypeSchema', 64 | 'method' => explode('::', __METHOD__), 65 | 'parameters' => ['type' => $type], 66 | 'dto' => file_get_contents($dtoFile), 67 | 'integration' => file_get_contents($integrationFile), 68 | 'description' => is_file($descriptionFile) ? file_get_contents($descriptionFile) : '', 69 | 'type' => $type, 70 | 'typeName' => TypeName::getDisplayName($type), 71 | ]; 72 | 73 | $templateFile = __DIR__ . '/../../resources/template/integration/detail.php'; 74 | return new Template($data, $templateFile, $this->reverseRouter); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /www/src/Controller/Specification.php: -------------------------------------------------------------------------------- 1 | 'Specification | TypeSchema', 23 | 'method' => explode('::', __METHOD__), 24 | ]; 25 | 26 | $templateFile = __DIR__ . '/../../resources/template/specification.php'; 27 | return new Template($data, $templateFile, $this->reverseRouter); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /www/src/Controller/Tools.php: -------------------------------------------------------------------------------- 1 | 'Tools | TypeSchema', 23 | 'method' => explode('::', __METHOD__), 24 | ]; 25 | 26 | $templateFile = __DIR__ . '/../../resources/template/tools.php'; 27 | return new Template($data, $templateFile, $this->reverseRouter); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /www/src/Controller/Tools/Changelog.php: -------------------------------------------------------------------------------- 1 | 'Changelog | TypeSchema', 29 | 'method' => explode('::', __METHOD__), 30 | 'left' => $this->getLeft(), 31 | 'right' => $this->getRight(), 32 | 'messages' => [] 33 | ]; 34 | 35 | $templateFile = __DIR__ . '/../../../resources/template/tools/changelog.php'; 36 | return new Template($data, $templateFile, $this->reverseRouter); 37 | } 38 | 39 | #[Post] 40 | #[Path('/tools/changelog')] 41 | public function generate(Diff $diff): mixed 42 | { 43 | $left = $diff->getLeft() ?? throw new \RuntimeException('Provided no left'); 44 | $right = $diff->getRight() ?? throw new \RuntimeException('Provided no right'); 45 | $messages = []; 46 | try { 47 | $defLeft = (new TypeSchema($this->schemaManager))->parse($left)->getDefinitions(); 48 | $defRight = (new TypeSchema($this->schemaManager))->parse($right)->getDefinitions(); 49 | 50 | foreach ((new ChangelogGenerator())->generate($defLeft, $defRight) as $type => $message) { 51 | $messages[] = [$type, $message]; 52 | } 53 | } catch (\Throwable $e) { 54 | $messages[] = [SemVer::MAJOR, $e->getMessage()]; 55 | } 56 | 57 | $data = [ 58 | 'title' => 'Changelog | TypeSchema', 59 | 'method' => explode('::', __METHOD__), 60 | 'left' => $left, 61 | 'right' => $right, 62 | 'messages' => $messages 63 | ]; 64 | 65 | $templateFile = __DIR__ . '/../../../resources/template/tools/changelog.php'; 66 | return new Template($data, $templateFile, $this->reverseRouter); 67 | } 68 | 69 | private function getLeft(): string 70 | { 71 | return <<<'JSON' 72 | { 73 | "definitions": { 74 | "Student": { 75 | "type": "struct", 76 | "properties": { 77 | "firstName": { 78 | "type": "string" 79 | }, 80 | "lastName": { 81 | "type": "string" 82 | }, 83 | "age": { 84 | "type": "integer" 85 | } 86 | } 87 | } 88 | }, 89 | "root": "Student" 90 | } 91 | JSON; 92 | } 93 | 94 | private function getRight(): string 95 | { 96 | return <<<'JSON' 97 | { 98 | "definitions": { 99 | "Student": { 100 | "description": "Represents a student", 101 | "type": "struct", 102 | "properties": { 103 | "firstName": { 104 | "type": "string" 105 | }, 106 | "lastName": { 107 | "type": "string" 108 | }, 109 | "gender": { 110 | "type": "string" 111 | }, 112 | "age": { 113 | "type": "string" 114 | } 115 | } 116 | } 117 | }, 118 | "root": "Student" 119 | } 120 | JSON; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /www/src/Controller/Tools/Json.php: -------------------------------------------------------------------------------- 1 | 'JSON migration | TypeSchema', 27 | 'method' => explode('::', __METHOD__), 28 | 'schema' => $this->getSchema() 29 | ]; 30 | 31 | $templateFile = __DIR__ . '/../../../resources/template/tools/json.php'; 32 | return new Template($data, $templateFile, $this->reverseRouter); 33 | } 34 | 35 | #[Post] 36 | #[Path('/tools/json')] 37 | public function migrate(Generate $generate): mixed 38 | { 39 | $schema = $generate->getSchema() ?? throw new \RuntimeException('Provided no schema'); 40 | 41 | try { 42 | $definitions = []; 43 | $root = new \stdClass(); 44 | $schema = $this->transform(json_decode($schema), $definitions); 45 | 46 | if (count($definitions) > 0) { 47 | $root->definitions = (object) $definitions; 48 | } 49 | 50 | if (isset($schema->target)) { 51 | $root->root = $schema->target; 52 | } 53 | 54 | $output = $root; 55 | } catch (\Throwable $e) { 56 | $output = $e->getMessage(); 57 | } 58 | 59 | $data = [ 60 | 'title' => 'JSON migration | TypeSchema', 61 | 'method' => explode('::', __METHOD__), 62 | 'schema' => $this->getSchema(), 63 | 'output' => json_encode($output, JSON_PRETTY_PRINT) 64 | ]; 65 | 66 | $templateFile = __DIR__ . '/../../../resources/template/tools/json.php'; 67 | return new Template($data, $templateFile, $this->reverseRouter); 68 | } 69 | 70 | private function transform(mixed $schema, array &$definitions): object 71 | { 72 | if ($schema instanceof \stdClass) { 73 | $properties = []; 74 | foreach (get_object_vars($schema) as $key => $value) { 75 | $properties[$key] = $this->transform($value, $definitions); 76 | } 77 | 78 | $name = 'Schema' . substr(sha1((string) json_encode($properties)), 0, 8); 79 | 80 | $definitions[$name] = (object) [ 81 | 'type' => 'struct', 82 | 'properties' => $properties, 83 | ]; 84 | 85 | return (object) [ 86 | 'type' => 'reference', 87 | 'target' => $name, 88 | ]; 89 | } elseif (is_array($schema)) { 90 | if (count($schema) === 0) { 91 | throw new \RuntimeException('Array must contain a value otherwise we cant inspect the type of the array'); 92 | } 93 | 94 | $items = $this->transform($schema[0] ?? null, $definitions); 95 | 96 | return (object) [ 97 | 'type' => 'array', 98 | 'schema' => $items, 99 | ]; 100 | } elseif (is_string($schema)) { 101 | return (object) [ 102 | 'type' => 'string', 103 | ]; 104 | } elseif (is_bool($schema)) { 105 | return (object) [ 106 | 'type' => 'boolean', 107 | ]; 108 | } elseif (is_int($schema)) { 109 | return (object) [ 110 | 'type' => 'integer', 111 | ]; 112 | } elseif (is_float($schema)) { 113 | return (object) [ 114 | 'type' => 'number', 115 | ]; 116 | } else { 117 | return (object) [ 118 | 'type' => 'any', 119 | ]; 120 | } 121 | } 122 | 123 | private function getSchema(): string 124 | { 125 | return <<<'JSON' 126 | { 127 | "productId": 1, 128 | "productName": "foobar", 129 | "price": 12.99, 130 | "tags": ["foo", "bar"], 131 | "persons": [{ 132 | "firstName": "foo", 133 | "lastName": "bar" 134 | }], 135 | "dimensions": { 136 | "length": 0, 137 | "width": 0, 138 | "height": 0 139 | } 140 | } 141 | JSON; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /www/src/Controller/Tools/JsonSchema.php: -------------------------------------------------------------------------------- 1 | 'JSON Schema migration | TypeSchema', 27 | 'method' => explode('::', __METHOD__), 28 | 'schema' => $this->getSchema() 29 | ]; 30 | 31 | $templateFile = __DIR__ . '/../../../resources/template/tools/jsonschema.php'; 32 | return new Template($data, $templateFile, $this->reverseRouter); 33 | } 34 | 35 | #[Post] 36 | #[Path('/tools/jsonschema')] 37 | public function migrate(Generate $generate): mixed 38 | { 39 | $schema = $generate->getSchema() ?? throw new \RuntimeException('Provided no schema'); 40 | 41 | try { 42 | $output = (new \PSX\Schema\Transformer\JsonSchema())->transform(json_decode($schema)); 43 | } catch (\Throwable $e) { 44 | $output = $e->getMessage(); 45 | } 46 | 47 | $data = [ 48 | 'title' => 'JSON Schema migration | TypeSchema', 49 | 'method' => explode('::', __METHOD__), 50 | 'schema' => $this->getSchema(), 51 | 'output' => json_encode($output, JSON_PRETTY_PRINT) 52 | ]; 53 | 54 | $templateFile = __DIR__ . '/../../../resources/template/tools/jsonschema.php'; 55 | return new Template($data, $templateFile, $this->reverseRouter); 56 | } 57 | 58 | private function getSchema(): string 59 | { 60 | return <<<'JSON' 61 | { 62 | "$schema": "https://json-schema.org/draft/2020-12/schema", 63 | "$id": "https://example.com/product.schema.json", 64 | "title": "Product", 65 | "description": "A product from Acme's catalog", 66 | "type": "object", 67 | "properties": { 68 | "productId": { 69 | "description": "The unique identifier for a product", 70 | "type": "integer" 71 | }, 72 | "productName": { 73 | "description": "Name of the product", 74 | "type": "string" 75 | }, 76 | "price": { 77 | "description": "The price of the product", 78 | "type": "number", 79 | "exclusiveMinimum": 0 80 | }, 81 | "tags": { 82 | "description": "Tags for the product", 83 | "type": "array", 84 | "items": { 85 | "type": "string" 86 | }, 87 | "minItems": 1, 88 | "uniqueItems": true 89 | }, 90 | "dimensions": { 91 | "type": "object", 92 | "properties": { 93 | "length": { 94 | "type": "number" 95 | }, 96 | "width": { 97 | "type": "number" 98 | }, 99 | "height": { 100 | "type": "number" 101 | } 102 | }, 103 | "required": [ "length", "width", "height" ] 104 | } 105 | }, 106 | "required": [ "productId", "productName", "price" ] 107 | } 108 | JSON; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /www/src/Controller/Tools/OpenAPI.php: -------------------------------------------------------------------------------- 1 | 'OpenAPI migration | TypeSchema', 27 | 'method' => explode('::', __METHOD__), 28 | 'schema' => $this->getSchema() 29 | ]; 30 | 31 | $templateFile = __DIR__ . '/../../../resources/template/tools/openapi.php'; 32 | return new Template($data, $templateFile, $this->reverseRouter); 33 | } 34 | 35 | #[Post] 36 | #[Path('/tools/openapi')] 37 | public function migrate(Generate $generate): mixed 38 | { 39 | $schema = $generate->getSchema() ?? throw new \RuntimeException('Provided no schema'); 40 | 41 | try { 42 | $output = (new \PSX\Api\Transformer\OpenAPI())->transform(json_decode($schema)); 43 | } catch (\Throwable $e) { 44 | $output = $e->getMessage(); 45 | } 46 | 47 | $data = [ 48 | 'title' => 'OpenAPI migration | TypeSchema', 49 | 'method' => explode('::', __METHOD__), 50 | 'schema' => $this->getSchema(), 51 | 'output' => json_encode($output, JSON_PRETTY_PRINT), 52 | ]; 53 | 54 | $templateFile = __DIR__ . '/../../../resources/template/tools/openapi.php'; 55 | return new Template($data, $templateFile, $this->reverseRouter); 56 | } 57 | 58 | private function getSchema(): string 59 | { 60 | return <<<'JSON' 61 | { 62 | "openapi": "3.0.0", 63 | "info": { 64 | "version": "1.0.0", 65 | "title": "Swagger Petstore", 66 | "license": { 67 | "name": "MIT" 68 | } 69 | }, 70 | "servers": [ 71 | { 72 | "url": "http://petstore.swagger.io/v1" 73 | } 74 | ], 75 | "paths": { 76 | "/pets": { 77 | "get": { 78 | "summary": "List all pets", 79 | "operationId": "listPets", 80 | "tags": [ 81 | "pets" 82 | ], 83 | "parameters": [ 84 | { 85 | "name": "limit", 86 | "in": "query", 87 | "description": "How many items to return at one time (max 100)", 88 | "required": false, 89 | "schema": { 90 | "type": "integer", 91 | "format": "int32" 92 | } 93 | } 94 | ], 95 | "responses": { 96 | "200": { 97 | "description": "A paged array of pets", 98 | "headers": { 99 | "x-next": { 100 | "description": "A link to the next page of responses", 101 | "schema": { 102 | "type": "string" 103 | } 104 | } 105 | }, 106 | "content": { 107 | "application/json": { 108 | "schema": { 109 | "$ref": "#/components/schemas/Pets" 110 | } 111 | } 112 | } 113 | }, 114 | "default": { 115 | "description": "unexpected error", 116 | "content": { 117 | "application/json": { 118 | "schema": { 119 | "$ref": "#/components/schemas/Error" 120 | } 121 | } 122 | } 123 | } 124 | } 125 | }, 126 | "post": { 127 | "summary": "Create a pet", 128 | "operationId": "createPets", 129 | "tags": [ 130 | "pets" 131 | ], 132 | "responses": { 133 | "201": { 134 | "description": "Null response" 135 | }, 136 | "default": { 137 | "description": "unexpected error", 138 | "content": { 139 | "application/json": { 140 | "schema": { 141 | "$ref": "#/components/schemas/Error" 142 | } 143 | } 144 | } 145 | } 146 | } 147 | } 148 | }, 149 | "/pets/{petId}": { 150 | "get": { 151 | "summary": "Info for a specific pet", 152 | "operationId": "showPetById", 153 | "tags": [ 154 | "pets" 155 | ], 156 | "parameters": [ 157 | { 158 | "name": "petId", 159 | "in": "path", 160 | "required": true, 161 | "description": "The id of the pet to retrieve", 162 | "schema": { 163 | "type": "string" 164 | } 165 | } 166 | ], 167 | "responses": { 168 | "200": { 169 | "description": "Expected response to a valid request", 170 | "content": { 171 | "application/json": { 172 | "schema": { 173 | "$ref": "#/components/schemas/Pet" 174 | } 175 | } 176 | } 177 | }, 178 | "default": { 179 | "description": "unexpected error", 180 | "content": { 181 | "application/json": { 182 | "schema": { 183 | "$ref": "#/components/schemas/Error" 184 | } 185 | } 186 | } 187 | } 188 | } 189 | } 190 | } 191 | }, 192 | "components": { 193 | "schemas": { 194 | "Pet": { 195 | "type": "object", 196 | "required": [ 197 | "id", 198 | "name" 199 | ], 200 | "properties": { 201 | "id": { 202 | "type": "integer", 203 | "format": "int64" 204 | }, 205 | "name": { 206 | "type": "string" 207 | }, 208 | "tag": { 209 | "type": "string" 210 | } 211 | } 212 | }, 213 | "Pets": { 214 | "type": "array", 215 | "items": { 216 | "$ref": "#/components/schemas/Pet" 217 | } 218 | }, 219 | "Error": { 220 | "type": "object", 221 | "required": [ 222 | "code", 223 | "message" 224 | ], 225 | "properties": { 226 | "code": { 227 | "type": "integer", 228 | "format": "int32" 229 | }, 230 | "message": { 231 | "type": "string" 232 | } 233 | } 234 | } 235 | } 236 | } 237 | } 238 | JSON; 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /www/src/Model/Diff.php: -------------------------------------------------------------------------------- 1 | left = $left; 15 | } 16 | public function getLeft(): ?string 17 | { 18 | return $this->left; 19 | } 20 | public function setRight(?string $right): void 21 | { 22 | $this->right = $right; 23 | } 24 | public function getRight(): ?string 25 | { 26 | return $this->right; 27 | } 28 | public function toRecord(): \PSX\Record\RecordInterface 29 | { 30 | /** @var \PSX\Record\Record $record */ 31 | $record = new \PSX\Record\Record(); 32 | $record->put('left', $this->left); 33 | $record->put('right', $this->right); 34 | return $record; 35 | } 36 | public function jsonSerialize(): object 37 | { 38 | return (object) $this->toRecord()->getAll(); 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /www/src/Model/Generate.php: -------------------------------------------------------------------------------- 1 | namespace = $namespace; 18 | } 19 | public function getNamespace(): ?string 20 | { 21 | return $this->namespace; 22 | } 23 | public function setSchema(?string $schema): void 24 | { 25 | $this->schema = $schema; 26 | } 27 | public function getSchema(): ?string 28 | { 29 | return $this->schema; 30 | } 31 | public function setGRecaptchaResponse(?string $gRecaptchaResponse): void 32 | { 33 | $this->gRecaptchaResponse = $gRecaptchaResponse; 34 | } 35 | public function getGRecaptchaResponse(): ?string 36 | { 37 | return $this->gRecaptchaResponse; 38 | } 39 | public function toRecord(): \PSX\Record\RecordInterface 40 | { 41 | /** @var \PSX\Record\Record $record */ 42 | $record = new \PSX\Record\Record(); 43 | $record->put('namespace', $this->namespace); 44 | $record->put('schema', $this->schema); 45 | $record->put('g-recaptcha-response', $this->gRecaptchaResponse); 46 | return $record; 47 | } 48 | public function jsonSerialize(): object 49 | { 50 | return (object) $this->toRecord()->getAll(); 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /www/src/Service/CaptchaVerifier.php: -------------------------------------------------------------------------------- 1 | httpClient = new Client(); 17 | $this->secret = $config->get('recaptcha_secret'); 18 | } 19 | 20 | public function verify(?string $recaptchaResponse): bool 21 | { 22 | if ($recaptchaResponse === null) { 23 | return false; 24 | } 25 | 26 | $response = $this->httpClient->post('https://www.google.com/recaptcha/api/siteverify', [ 27 | 'headers' => [ 28 | 'User-Agent' => 'typeschema.org' 29 | ], 30 | 'form_params' => [ 31 | 'secret' => $this->secret, 32 | 'response' => $recaptchaResponse, 33 | 'remoteip' => $_SERVER['REMOTE_ADDR'] ?? '', 34 | ], 35 | 'verify' => false 36 | ]); 37 | 38 | if ($response->getStatusCode() == 200) { 39 | $data = Json\Parser::decode((string) $response->getBody()); 40 | if (isset($data->success) && $data->success === true) { 41 | return true; 42 | } 43 | } 44 | 45 | return false; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /www/src/Service/TypeName.php: -------------------------------------------------------------------------------- 1 | 'C#', 15 | GeneratorFactory::TYPE_GRAPHQL => 'GraphQL', 16 | GeneratorFactory::TYPE_HTML => 'HTML', 17 | GeneratorFactory::TYPE_JSONSCHEMA => 'JsonSchema', 18 | GeneratorFactory::TYPE_PHP => 'PHP', 19 | GeneratorFactory::TYPE_TYPESCHEMA => 'TypeSchema', 20 | GeneratorFactory::TYPE_TYPESCRIPT => 'TypeScript', 21 | GeneratorFactory::TYPE_VISUALBASIC => 'VisualBasic', 22 | default => ucfirst($type), 23 | }; 24 | } 25 | } 26 | --------------------------------------------------------------------------------