├── .gitattributes ├── .gitignore ├── .gitmodules ├── Demos ├── Data │ └── samples │ │ ├── 1password.json │ │ ├── ablyio.json │ │ ├── authentiqio.json │ │ ├── ipgeolocation.json │ │ └── petstore.json └── OpenAPI │ ├── Demo.Form.Main.dfm │ ├── Demo.Form.Main.pas │ ├── DemoOpenAPI.dpr │ ├── DemoOpenAPI.dproj │ └── DemoOpenAPI.res ├── LICENSE ├── Packages ├── 10.2Tokyo │ ├── OpenAPI.dpk │ ├── OpenAPI.dproj │ └── OpenAPI.res ├── 10.3Rio │ ├── OpenAPI.dpk │ ├── OpenAPI.dproj │ └── OpenAPI.res ├── 10.4Sydney │ ├── OpenAPI.dpk │ ├── OpenAPI.dproj │ └── OpenAPI.res ├── 11.0Alexandria │ ├── OpenAPI.dpk │ ├── OpenAPI.dproj │ └── OpenAPI.res └── 12.0Athens │ ├── OpenAPI.dpk │ ├── OpenAPI.dproj │ └── OpenAPI.res ├── README.md ├── Source ├── OpenAPI.Core.Exceptions.pas ├── OpenAPI.Core.Interfaces.pas ├── OpenAPI.Model.Any.pas ├── OpenAPI.Model.Base.pas ├── OpenAPI.Model.Classes.pas ├── OpenAPI.Model.Expressions.pas ├── OpenAPI.Model.JsonPointer.pas ├── OpenAPI.Model.Reference.pas ├── OpenAPI.Model.Schema.pas └── OpenAPI.Neon.Serializers.pas └── openapi-delphi.png /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text=auto 3 | 4 | # Explicitly declare text files you want to always be normalized and converted 5 | # to native line endings on checkout. 6 | *.pas text 7 | *.dfm text 8 | 9 | # Declare files that will always have CRLF line endings on checkout. 10 | 11 | # Denote all files that are truly binary and should not be modified. 12 | *.exe binary 13 | *.res binary -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Uncomment these types if you want even more clean repository. But be careful. 2 | # It can make harm to an existing project source. Read explanations below. 3 | # 4 | # Resource files are binaries containing manifest, project icon and version info. 5 | # They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files. 6 | #*.res 7 | # 8 | # Type library file (binary). In old Delphi versions it should be stored. 9 | # Since Delphi 2009 it is produced from .ridl file and can safely be ignored. 10 | #*.tlb 11 | # 12 | # Diagram Portfolio file. Used by the diagram editor up to Delphi 7. 13 | # Uncomment this if you are not using diagrams or use newer Delphi version. 14 | #*.ddp 15 | # 16 | # Visual LiveBindings file. Added in Delphi XE2. 17 | # Uncomment this if you are not using LiveBindings Designer. 18 | #*.vlb 19 | # 20 | # Deployment Manager configuration file for your project. Added in Delphi XE2. 21 | # Uncomment this if it is not mobile development and you do not use remote debug feature. 22 | #*.deployproj 23 | # 24 | # C++ object files produced when C/C++ Output file generation is configured. 25 | # Uncomment this if you are not using external objects (zlib library for example). 26 | #*.obj 27 | # 28 | 29 | # Delphi compiler-generated binaries (safe to delete) 30 | *.exe 31 | *.dll 32 | *.bpl 33 | *.bpi 34 | *.dcp 35 | *.so 36 | *.apk 37 | *.drc 38 | *.map 39 | *.dres 40 | *.rsm 41 | *.tds 42 | *.dcu 43 | *.lib 44 | *.a 45 | *.o 46 | *.ocx 47 | 48 | # Delphi autogenerated files (duplicated info) 49 | *.cfg 50 | *.hpp 51 | *Resource.rc 52 | 53 | # Delphi local files (user-specific info) 54 | *.local 55 | *.identcache 56 | *.projdata 57 | *.tvsconfig 58 | *.dsk 59 | 60 | # Delphi history and backups 61 | __history/ 62 | __recovery/ 63 | *.~* 64 | 65 | # Castalia statistics file (since XE7 Castalia is distributed with Delphi) 66 | *.stat 67 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Libs/Neon"] 2 | path = Libs/Neon 3 | url = https://github.com/paolo-rossi/delphi-neon 4 | -------------------------------------------------------------------------------- /Demos/Data/samples/1password.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.0", 3 | "info": { 4 | "title": "Events API", 5 | "description": "1Password Events API Specification.", 6 | "version": "1.0.0", 7 | "x-apisguru-categories": [ 8 | "security" 9 | ], 10 | "x-logo": { 11 | "url": "https://api.apis.guru/v2/cache/logo/https_upload.wikimedia.org_wikipedia_commons_thumb_e_e3_1password-logo.svg_1280px-1password-logo.svg.png" 12 | }, 13 | "x-origin": [ 14 | { 15 | "format": "openapi", 16 | "url": "https://i.1password.com/media/1password-events-reporting/1password-events-api.yaml", 17 | "version": "3.0" 18 | } 19 | ], 20 | "x-providerName": "1password.com", 21 | "x-serviceName": "events" 22 | }, 23 | "servers": [ 24 | { 25 | "description": "1Password", 26 | "url": "https://events.1password.com" 27 | }, 28 | { 29 | "description": "1Password CA", 30 | "url": "https://events.1password.ca" 31 | }, 32 | { 33 | "description": "1Password EU", 34 | "url": "https://events.1password.eu" 35 | }, 36 | { 37 | "description": "1Password Enterprise", 38 | "url": "https://events.ent.1password.com" 39 | } 40 | ], 41 | "paths": { 42 | "/api/auth/introspect": { 43 | "get": { 44 | "summary": "Performs introspection of the provided Bearer JWT token", 45 | "operationId": "getAuthIntrospect", 46 | "responses": { 47 | "200": { 48 | "$ref": "#/components/responses/IntrospectResponse" 49 | }, 50 | "401": { 51 | "$ref": "#/components/responses/UnauthorizedErrorResponse" 52 | }, 53 | "default": { 54 | "$ref": "#/components/responses/GenericErrorResponse" 55 | } 56 | }, 57 | "security": [ 58 | { 59 | "jwtsa": [] 60 | } 61 | ], 62 | "tags": [ 63 | "auth" 64 | ] 65 | } 66 | }, 67 | "/api/v1/itemusages": { 68 | "post": { 69 | "description": "This endpoint requires your JSON Web Token to have the *itemusages* feature.", 70 | "operationId": "getItemUsages", 71 | "requestBody": { 72 | "$ref": "#/components/requestBodies/ItemUsagesRequest" 73 | }, 74 | "responses": { 75 | "200": { 76 | "$ref": "#/components/responses/ItemUsagesResponse" 77 | }, 78 | "401": { 79 | "$ref": "#/components/responses/UnauthorizedErrorResponse" 80 | }, 81 | "default": { 82 | "$ref": "#/components/responses/GenericErrorResponse" 83 | } 84 | }, 85 | "security": [ 86 | { 87 | "jwtsa": [] 88 | } 89 | ], 90 | "summary": "Retrieves item usages", 91 | "tags": [ 92 | "api-v1" 93 | ] 94 | } 95 | }, 96 | "/api/v1/signinattempts": { 97 | "post": { 98 | "description": "This endpoint requires your JSON Web Token to have the *signinattempts* feature.", 99 | "operationId": "getSignInAttempts", 100 | "requestBody": { 101 | "$ref": "#/components/requestBodies/SignInAttemptsRequest" 102 | }, 103 | "responses": { 104 | "200": { 105 | "$ref": "#/components/responses/SignInAttemptsResponse" 106 | }, 107 | "401": { 108 | "$ref": "#/components/responses/UnauthorizedErrorResponse" 109 | }, 110 | "default": { 111 | "$ref": "#/components/responses/GenericErrorResponse" 112 | } 113 | }, 114 | "security": [ 115 | { 116 | "jwtsa": [] 117 | } 118 | ], 119 | "summary": "Retrieves sign-in attempts", 120 | "tags": [ 121 | "api-v1" 122 | ] 123 | } 124 | } 125 | }, 126 | "components": { 127 | "examples": { 128 | "Cursor": { 129 | "summary": "Used for continued calling with a cursor", 130 | "value": { 131 | "cursor": "aGVsbG8hIGlzIGl0IG1lIHlvdSBhcmUgbG9va2luZyBmb3IK" 132 | } 133 | }, 134 | "ResetCursor": { 135 | "summary": "Used for reseting the cursor", 136 | "value": { 137 | "limit": 100, 138 | "start_time": "2021-06-11T16:32:50-03:00" 139 | } 140 | } 141 | }, 142 | "requestBodies": { 143 | "CursorRequest": { 144 | "content": { 145 | "application/json": { 146 | "examples": { 147 | "Continuing cursor": { 148 | "$ref": "#/components/examples/Cursor" 149 | }, 150 | "Resetting cursor": { 151 | "$ref": "#/components/examples/ResetCursor" 152 | } 153 | }, 154 | "schema": { 155 | "oneOf": [ 156 | { 157 | "$ref": "#/components/schemas/Cursor" 158 | }, 159 | { 160 | "$ref": "#/components/schemas/ResetCursor" 161 | } 162 | ] 163 | } 164 | } 165 | } 166 | }, 167 | "ItemUsagesRequest": { 168 | "$ref": "#/components/requestBodies/CursorRequest" 169 | }, 170 | "SignInAttemptsRequest": { 171 | "$ref": "#/components/requestBodies/CursorRequest" 172 | } 173 | }, 174 | "responses": { 175 | "GenericErrorResponse": { 176 | "content": { 177 | "application/json": { 178 | "schema": { 179 | "$ref": "#/components/schemas/Error" 180 | } 181 | } 182 | }, 183 | "description": "Generic error" 184 | }, 185 | "IntrospectResponse": { 186 | "content": { 187 | "application/json": { 188 | "schema": { 189 | "$ref": "#/components/schemas/Introspection" 190 | } 191 | } 192 | }, 193 | "description": "Introspection object" 194 | }, 195 | "ItemUsagesResponse": { 196 | "content": { 197 | "application/json": { 198 | "schema": { 199 | "$ref": "#/components/schemas/ItemUsageItems" 200 | } 201 | } 202 | }, 203 | "description": "Item usages response object" 204 | }, 205 | "SignInAttemptsResponse": { 206 | "content": { 207 | "application/json": { 208 | "schema": { 209 | "$ref": "#/components/schemas/SignInAttemptItems" 210 | } 211 | } 212 | }, 213 | "description": "Sign-in attempts response object" 214 | }, 215 | "UnauthorizedErrorResponse": { 216 | "content": { 217 | "application/json": { 218 | "schema": { 219 | "$ref": "#/components/schemas/Error" 220 | } 221 | } 222 | }, 223 | "description": "Unauthorized" 224 | } 225 | }, 226 | "schemas": { 227 | "Client": { 228 | "description": "Metadata gathered about the client", 229 | "properties": { 230 | "app_name": { 231 | "example": "1Password Extension", 232 | "type": "string" 233 | }, 234 | "app_version": { 235 | "example": "20127", 236 | "type": "string" 237 | }, 238 | "ip_address": { 239 | "example": "13.227.95.22", 240 | "type": "string" 241 | }, 242 | "os_name": { 243 | "example": "MacOSX", 244 | "type": "string" 245 | }, 246 | "os_version": { 247 | "example": "10.15.6", 248 | "type": "string" 249 | }, 250 | "platform_name": { 251 | "example": "Chrome", 252 | "type": "string" 253 | }, 254 | "platform_version": { 255 | "description": "Depending on the platform used, this can be the version of the browser that the client extension is installed, the model of computer that the native application is installed or the machine's CPU version that the CLI was installed", 256 | "type": "string" 257 | } 258 | } 259 | }, 260 | "Cursor": { 261 | "description": "Cursor", 262 | "properties": { 263 | "cursor": { 264 | "description": "Cursor to fetch more data if available or continue the polling process if required", 265 | "example": "aGVsbG8hIGlzIGl0IG1lIHlvdSBhcmUgbG9va2luZyBmb3IK", 266 | "type": "string" 267 | } 268 | } 269 | }, 270 | "CursorCollection": { 271 | "allOf": [ 272 | { 273 | "$ref": "#/components/schemas/Cursor" 274 | }, 275 | { 276 | "properties": { 277 | "has_more": { 278 | "description": "Whether there may still be more data to fetch using the returned cursor. If true, the subsequent request could still be empty.", 279 | "type": "boolean" 280 | } 281 | } 282 | } 283 | ], 284 | "description": "Common cursor properties for collection responses" 285 | }, 286 | "DateTimeRFC3339": { 287 | "example": "2020-06-11T16:32:50-03:00", 288 | "format": "date-time", 289 | "type": "string" 290 | }, 291 | "Details": { 292 | "description": "Additional information about the sign-in attempt", 293 | "properties": { 294 | "value": { 295 | "description": "For firewall prevented sign-ins, the value is the chosen continent, country, etc. that blocked the sign-in attempt", 296 | "example": "Europe", 297 | "type": "string" 298 | } 299 | } 300 | }, 301 | "Error": { 302 | "properties": { 303 | "Error": { 304 | "properties": { 305 | "Message": { 306 | "description": "The error message.", 307 | "type": "string" 308 | } 309 | }, 310 | "type": "object" 311 | } 312 | }, 313 | "type": "object" 314 | }, 315 | "Introspection": { 316 | "properties": { 317 | "Features": { 318 | "example": [ 319 | "itemusages", 320 | "signinattempts" 321 | ], 322 | "items": { 323 | "type": "string" 324 | }, 325 | "type": "array" 326 | }, 327 | "IssuedAt": { 328 | "$ref": "#/components/schemas/DateTimeRFC3339" 329 | }, 330 | "UUID": { 331 | "type": "string" 332 | } 333 | }, 334 | "type": "object" 335 | }, 336 | "ItemUsage": { 337 | "description": "A single item usage object", 338 | "properties": { 339 | "client": { 340 | "$ref": "#/components/schemas/Client" 341 | }, 342 | "item_uuid": { 343 | "$ref": "#/components/schemas/UUID" 344 | }, 345 | "timestamp": { 346 | "$ref": "#/components/schemas/DateTimeRFC3339" 347 | }, 348 | "used_version": { 349 | "type": "integer" 350 | }, 351 | "user": { 352 | "$ref": "#/components/schemas/User" 353 | }, 354 | "uuid": { 355 | "$ref": "#/components/schemas/UUID" 356 | }, 357 | "vault_uuid": { 358 | "$ref": "#/components/schemas/UUID" 359 | } 360 | } 361 | }, 362 | "ItemUsageItems": { 363 | "allOf": [ 364 | { 365 | "properties": { 366 | "items": { 367 | "items": { 368 | "$ref": "#/components/schemas/ItemUsage" 369 | }, 370 | "type": "array" 371 | } 372 | } 373 | }, 374 | { 375 | "$ref": "#/components/schemas/CursorCollection" 376 | } 377 | ], 378 | "description": "An object wrapping cursor properties and a list of items usages" 379 | }, 380 | "ResetCursor": { 381 | "description": "Reset cursor", 382 | "properties": { 383 | "end_time": { 384 | "$ref": "#/components/schemas/DateTimeRFC3339" 385 | }, 386 | "limit": { 387 | "maximum": 1000, 388 | "minimum": 1, 389 | "type": "number" 390 | }, 391 | "start_time": { 392 | "$ref": "#/components/schemas/DateTimeRFC3339" 393 | } 394 | } 395 | }, 396 | "SignInAttempt": { 397 | "description": "A single sign-in attempt object", 398 | "properties": { 399 | "category": { 400 | "enum": [ 401 | "success", 402 | "credentials_failed", 403 | "mfa_failed", 404 | "modern_version_failed", 405 | "firewall_failed", 406 | "firewall_reported_success" 407 | ], 408 | "example": "firewall_failed", 409 | "type": "string" 410 | }, 411 | "client": { 412 | "$ref": "#/components/schemas/Client" 413 | }, 414 | "country": { 415 | "description": "Country ISO Code", 416 | "example": "France", 417 | "type": "string" 418 | }, 419 | "details": { 420 | "$ref": "#/components/schemas/Details" 421 | }, 422 | "session_uuid": { 423 | "$ref": "#/components/schemas/UUID" 424 | }, 425 | "target_user": { 426 | "$ref": "#/components/schemas/User" 427 | }, 428 | "timestamp": { 429 | "$ref": "#/components/schemas/DateTimeRFC3339" 430 | }, 431 | "type": { 432 | "enum": [ 433 | "credentials_ok", 434 | "mfa_ok", 435 | "password_secret_bad", 436 | "mfa_missing", 437 | "totp_disabled", 438 | "totp_bad", 439 | "totp_timeout", 440 | "u2f_disabled", 441 | "u2f_bad", 442 | "u2f_timout", 443 | "duo_disabled", 444 | "duo_bad", 445 | "duo_timeout", 446 | "duo_native_bad", 447 | "platform_secret_disabled", 448 | "platform_secret_bad", 449 | "platform_secret_proxy", 450 | "code_disabled", 451 | "code_bad", 452 | "code_timeout", 453 | "ip_blocked", 454 | "continent_blocked", 455 | "country_blocked", 456 | "anonymous_blocked", 457 | "all_blocked", 458 | "modern_version_missing", 459 | "modern_version_old" 460 | ], 461 | "example": "continent_blocked", 462 | "type": "string" 463 | }, 464 | "uuid": { 465 | "$ref": "#/components/schemas/UUID" 466 | } 467 | } 468 | }, 469 | "SignInAttemptItems": { 470 | "allOf": [ 471 | { 472 | "properties": { 473 | "items": { 474 | "items": { 475 | "$ref": "#/components/schemas/SignInAttempt" 476 | }, 477 | "type": "array" 478 | } 479 | } 480 | }, 481 | { 482 | "$ref": "#/components/schemas/CursorCollection" 483 | } 484 | ], 485 | "description": "An object wrapping cursor properties and a list of sign-in attempts" 486 | }, 487 | "UUID": { 488 | "example": "56YE2TYN2VFYRLNSHKPW5NVT5E", 489 | "type": "string" 490 | }, 491 | "User": { 492 | "description": "User object", 493 | "properties": { 494 | "email": { 495 | "format": "email", 496 | "type": "string" 497 | }, 498 | "name": { 499 | "description": "Full name", 500 | "example": "Jack O'Neill", 501 | "type": "string" 502 | }, 503 | "uuid": { 504 | "$ref": "#/components/schemas/UUID" 505 | } 506 | } 507 | } 508 | }, 509 | "securitySchemes": { 510 | "jwtsa": { 511 | "bearerFormat": "JWT-SA", 512 | "description": "A JWT SA token issued to this service", 513 | "scheme": "bearer", 514 | "type": "http" 515 | } 516 | } 517 | } 518 | } -------------------------------------------------------------------------------- /Demos/Data/samples/ipgeolocation.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.1", 3 | "servers": [ 4 | { 5 | "url": "https://ipgeolocation.abstractapi.com" 6 | } 7 | ], 8 | "info": { 9 | "description": "Abstract IP geolocation API allows developers to retrieve the region, country and city behind any IP worldwide. The API covers the geolocation of IPv4 and IPv6 addresses in 180+ countries worldwide. Extra information can be retrieved like the currency, flag or language associated to an IP.", 10 | "title": "IP geolocation API", 11 | "version": "1.0.0", 12 | "x-apisguru-categories": [ 13 | "location" 14 | ], 15 | "x-logo": { 16 | "url": "https://api.apis.guru/v2/cache/logo/https_global-uploads.webflow.com_5ebbd0a566a3996636e55959_5ec2ba29feeeb05d69160e7b_webclip.png" 17 | }, 18 | "x-origin": [ 19 | { 20 | "format": "openapi", 21 | "url": "https://documentation.abstractapi.com/ip-geolocation-openapi.json", 22 | "version": "3.0" 23 | } 24 | ], 25 | "x-providerName": "abstractapi.com", 26 | "x-serviceName": "geolocation" 27 | }, 28 | "externalDocs": { 29 | "description": "API Documentation", 30 | "url": "https://www.abstractapi.com/ip-geolocation-api#docs" 31 | }, 32 | "paths": { 33 | "/v1/": { 34 | "get": { 35 | "description": "Retrieve the location of an IP address", 36 | "parameters": [ 37 | { 38 | "explode": true, 39 | "in": "query", 40 | "name": "api_key", 41 | "required": true, 42 | "schema": { 43 | "type": "string" 44 | }, 45 | "style": "form" 46 | }, 47 | { 48 | "explode": true, 49 | "in": "query", 50 | "name": "ip_address", 51 | "required": false, 52 | "schema": { 53 | "example": "195.154.25.40", 54 | "type": "string" 55 | }, 56 | "style": "form" 57 | }, 58 | { 59 | "explode": true, 60 | "in": "query", 61 | "name": "fields", 62 | "required": false, 63 | "schema": { 64 | "example": "country,city,timezone", 65 | "type": "string" 66 | }, 67 | "style": "form" 68 | } 69 | ], 70 | "responses": { 71 | "200": { 72 | "content": { 73 | "application/json": { 74 | "examples": { 75 | "0": { 76 | "value": "{\"ip_address\":\"195.154.25.40\",\"city\":\"Paris\",\"city_geoname_id\":2988507,\"region\":\"Île-de-France\",\"region_iso_code\":\"IDF\",\"region_geoname_id\":3012874,\"postal_code\":\"75008\",\"country\":\"France\",\"country_code\":\"FR\",\"country_geoname_id\":3017382,\"country_is_eu\":true,\"continent\":\"Europe\",\"continent_code\":\"EU\",\"continent_geoname_id\":6255148,\"longitude\":2.4075,\"latitude\":48.8323,\"security\":{\"is_vpn\":false},\"timezone\":{\"name\":\"Europe/Paris\",\"abbreviation\":\"CEST\",\"gmt_offset\":2,\"current_time\":\"15:42:18\",\"is_dst\":true},\"flag\":{\"emoji\":\"<ë<÷\",\"unicode\":\"U+1F1EB U+1F1F7\",\"png\":\"https://static.abstractapi.com/country-flags/FR_flag.png\",\"svg\":\"https://static.abstractapi.com/country-flags/FR_flag.svg\"},\"currency\":{\"currency_name\":\"Euros\",\"currency_code\":\"EUR\"},\"connection\":{\"autonomous_system_number\":12876,\"autonomous_system_organization\":\"Online S.a.s.\",\"connection_type\":\"Corporate\",\"isp_name\":\"Online S.A.S.\",\"organization_name\":\"ONLINE\"}}" 77 | } 78 | }, 79 | "schema": { 80 | "$ref": "#/components/schemas/inline_response_200" 81 | } 82 | } 83 | }, 84 | "description": "Location of geolocated IP" 85 | } 86 | }, 87 | "servers": [ 88 | { 89 | "url": "https://ipgeolocation.abstractapi.com" 90 | } 91 | ] 92 | }, 93 | "servers": [ 94 | { 95 | "url": "https://ipgeolocation.abstractapi.com" 96 | } 97 | ] 98 | } 99 | }, 100 | "components": { 101 | "schemas": { 102 | "inline_response_200": { 103 | "properties": { 104 | "city": { 105 | "type": "string" 106 | }, 107 | "city_geoname_id": { 108 | "type": "integer" 109 | }, 110 | "connection": { 111 | "properties": { 112 | "autonomous_system_number": { 113 | "type": "integer" 114 | }, 115 | "autonomous_system_organization": { 116 | "type": "string" 117 | }, 118 | "connection_type": { 119 | "type": "string" 120 | }, 121 | "isp_name": { 122 | "type": "string" 123 | }, 124 | "organization_name": { 125 | "type": "string" 126 | } 127 | }, 128 | "type": "object" 129 | }, 130 | "continent": { 131 | "type": "string" 132 | }, 133 | "continent_code": { 134 | "type": "string" 135 | }, 136 | "continent_geoname_id": { 137 | "type": "integer" 138 | }, 139 | "country": { 140 | "type": "string" 141 | }, 142 | "country_code": { 143 | "type": "string" 144 | }, 145 | "country_geoname_id": { 146 | "type": "integer" 147 | }, 148 | "country_is_eu": { 149 | "type": "boolean" 150 | }, 151 | "currency": { 152 | "properties": { 153 | "currency_code": { 154 | "type": "string" 155 | }, 156 | "currency_name": { 157 | "type": "string" 158 | } 159 | }, 160 | "type": "object" 161 | }, 162 | "flag": { 163 | "properties": { 164 | "emoji": { 165 | "type": "string" 166 | }, 167 | "png": { 168 | "type": "string" 169 | }, 170 | "svg": { 171 | "type": "string" 172 | }, 173 | "unicode": { 174 | "type": "string" 175 | } 176 | }, 177 | "type": "object" 178 | }, 179 | "ip_address": { 180 | "type": "string" 181 | }, 182 | "latitude": { 183 | "type": "number" 184 | }, 185 | "longitude": { 186 | "type": "number" 187 | }, 188 | "postal_code": { 189 | "type": "string" 190 | }, 191 | "region": { 192 | "type": "string" 193 | }, 194 | "region_geoname_id": { 195 | "type": "integer" 196 | }, 197 | "region_iso_code": { 198 | "type": "string" 199 | }, 200 | "security": { 201 | "properties": { 202 | "is_vpn": { 203 | "type": "boolean" 204 | } 205 | }, 206 | "type": "object" 207 | }, 208 | "timezone": { 209 | "properties": { 210 | "abbreviation": { 211 | "type": "string" 212 | }, 213 | "current_time": { 214 | "type": "string" 215 | }, 216 | "gmt_offset": { 217 | "type": "integer" 218 | }, 219 | "is_dst": { 220 | "type": "boolean" 221 | }, 222 | "name": { 223 | "type": "string" 224 | } 225 | }, 226 | "type": "object" 227 | } 228 | }, 229 | "type": "object" 230 | } 231 | } 232 | } 233 | } -------------------------------------------------------------------------------- /Demos/Data/samples/petstore.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.0", 3 | "info": { 4 | "version": "1.0.0", 5 | "title": "Swagger Petstore", 6 | "license": { 7 | "name": "MIT" 8 | } 9 | }, 10 | "servers": [ 11 | { 12 | "url": "http://petstore.swagger.io/v1" 13 | } 14 | ], 15 | "paths": { 16 | "/pets": { 17 | "get": { 18 | "summary": "List all pets", 19 | "operationId": "listPets", 20 | "tags": [ 21 | "pets" 22 | ], 23 | "parameters": [ 24 | { 25 | "name": "limit", 26 | "in": "query", 27 | "description": "How many items to return at one time (max 100)", 28 | "required": false, 29 | "schema": { 30 | "type": "integer", 31 | "format": "int32" 32 | } 33 | } 34 | ], 35 | "responses": { 36 | "200": { 37 | "description": "An paged array of pets", 38 | "headers": { 39 | "x-next": { 40 | "description": "A link to the next page of responses", 41 | "schema": { 42 | "type": "string" 43 | } 44 | } 45 | }, 46 | "content": { 47 | "application/json": { 48 | "schema": { 49 | "$ref": "#/components/schemas/Pets" 50 | } 51 | } 52 | } 53 | }, 54 | "default": { 55 | "description": "unexpected error", 56 | "content": { 57 | "application/json": { 58 | "schema": { 59 | "$ref": "#/components/schemas/Error" 60 | } 61 | } 62 | } 63 | } 64 | } 65 | }, 66 | "post": { 67 | "summary": "Create a pet", 68 | "operationId": "createPets", 69 | "tags": [ 70 | "pets" 71 | ], 72 | "responses": { 73 | "201": { 74 | "description": "Null response" 75 | }, 76 | "default": { 77 | "description": "unexpected error", 78 | "content": { 79 | "application/json": { 80 | "schema": { 81 | "$ref": "#/components/schemas/Error" 82 | } 83 | } 84 | } 85 | } 86 | } 87 | } 88 | }, 89 | "/pets/{petId}": { 90 | "get": { 91 | "summary": "Info for a specific pet", 92 | "operationId": "showPetById", 93 | "tags": [ 94 | "pets" 95 | ], 96 | "parameters": [ 97 | { 98 | "name": "petId", 99 | "in": "path", 100 | "required": true, 101 | "description": "The id of the pet to retrieve", 102 | "schema": { 103 | "type": "string" 104 | } 105 | } 106 | ], 107 | "responses": { 108 | "200": { 109 | "description": "Expected response to a valid request", 110 | "content": { 111 | "application/json": { 112 | "schema": { 113 | "$ref": "#/components/schemas/Pets" 114 | } 115 | } 116 | } 117 | }, 118 | "default": { 119 | "description": "unexpected error", 120 | "content": { 121 | "application/json": { 122 | "schema": { 123 | "$ref": "#/components/schemas/Error" 124 | } 125 | } 126 | } 127 | } 128 | } 129 | } 130 | } 131 | }, 132 | "components": { 133 | "schemas": { 134 | "Pet": { 135 | "required": [ 136 | "id", 137 | "name" 138 | ], 139 | "properties": { 140 | "id": { 141 | "type": "integer", 142 | "format": "int64" 143 | }, 144 | "name": { 145 | "type": "string" 146 | }, 147 | "tag": { 148 | "type": "string" 149 | } 150 | } 151 | }, 152 | "Pets": { 153 | "type": "array", 154 | "items": { 155 | "$ref": "#/components/schemas/Pet" 156 | } 157 | }, 158 | "Error": { 159 | "required": [ 160 | "code", 161 | "message" 162 | ], 163 | "properties": { 164 | "code": { 165 | "type": "integer", 166 | "format": "int32" 167 | }, 168 | "message": { 169 | "type": "string" 170 | } 171 | } 172 | } 173 | } 174 | } 175 | } -------------------------------------------------------------------------------- /Demos/OpenAPI/Demo.Form.Main.dfm: -------------------------------------------------------------------------------- 1 | object frmMain: TfrmMain 2 | Left = 0 3 | Top = 0 4 | Caption = 'OpenAPI Demo' 5 | ClientHeight = 580 6 | ClientWidth = 902 7 | Color = clBtnFace 8 | Font.Charset = DEFAULT_CHARSET 9 | Font.Color = clWindowText 10 | Font.Height = -11 11 | Font.Name = 'Tahoma' 12 | Font.Style = [] 13 | OnCreate = FormCreate 14 | OnDestroy = FormDestroy 15 | TextHeight = 13 16 | object memoDocument: TMemo 17 | Left = 200 18 | Top = 0 19 | Width = 702 20 | Height = 580 21 | Align = alClient 22 | Font.Charset = ANSI_CHARSET 23 | Font.Color = clWindowText 24 | Font.Height = -16 25 | Font.Name = 'Consolas' 26 | Font.Style = [] 27 | ParentFont = False 28 | ScrollBars = ssVertical 29 | TabOrder = 0 30 | ExplicitLeft = 203 31 | end 32 | object catMenu: TCategoryPanelGroup 33 | Left = 0 34 | Top = 0 35 | Height = 580 36 | VertScrollBar.Tracking = True 37 | HeaderFont.Charset = DEFAULT_CHARSET 38 | HeaderFont.Color = clWindowText 39 | HeaderFont.Height = -11 40 | HeaderFont.Name = 'Tahoma' 41 | HeaderFont.Style = [] 42 | TabOrder = 1 43 | object pnlSections: TCategoryPanel 44 | Top = 192 45 | Height = 369 46 | Caption = 'Fill Document Sections' 47 | TabOrder = 0 48 | Visible = False 49 | object CategoryButtons1: TCategoryButtons 50 | Left = 0 51 | Top = 0 52 | Width = 194 53 | Height = 343 54 | Align = alClient 55 | ButtonFlow = cbfVertical 56 | ButtonOptions = [boFullSize, boGradientFill, boShowCaptions, boUsePlusMinus] 57 | Categories = < 58 | item 59 | Caption = 'General' 60 | Color = 15466474 61 | Collapsed = False 62 | Items = < 63 | item 64 | Action = actAddInfo 65 | end 66 | item 67 | Action = actAddInfoExtensions 68 | end 69 | item 70 | Action = actAddServers 71 | end> 72 | end 73 | item 74 | Caption = 'Components' 75 | Color = 16771818 76 | Collapsed = False 77 | Items = < 78 | item 79 | Action = actCompAddSchemas 80 | end 81 | item 82 | Action = actCompAddResponses 83 | end 84 | item 85 | Action = actCompAddSecurityDefs 86 | end 87 | item 88 | Action = actCompAddParameters 89 | end> 90 | end 91 | item 92 | Caption = 'Optional Sections' 93 | Color = 16771839 94 | Collapsed = False 95 | Items = < 96 | item 97 | Action = actAddPaths 98 | end 99 | item 100 | Action = actAddSecurity 101 | end> 102 | end 103 | item 104 | Caption = 'Serialization' 105 | Color = 16777194 106 | Collapsed = False 107 | Items = < 108 | item 109 | Action = actJSONGenerate 110 | end> 111 | end> 112 | RegularButtonColor = clWhite 113 | SelectedButtonColor = 15132390 114 | TabOrder = 0 115 | ExplicitHeight = 327 116 | end 117 | end 118 | object pnlDocument: TCategoryPanel 119 | Top = 0 120 | Height = 192 121 | Caption = 'OpenAPI Document' 122 | TabOrder = 1 123 | object CategoryButtons2: TCategoryButtons 124 | Left = 0 125 | Top = 0 126 | Width = 194 127 | Height = 166 128 | Align = alClient 129 | ButtonFlow = cbfVertical 130 | Categories = <> 131 | RegularButtonColor = clWhite 132 | SelectedButtonColor = 15132390 133 | TabOrder = 0 134 | end 135 | object catJSON: TCategoryButtons 136 | Left = 0 137 | Top = 0 138 | Width = 194 139 | Height = 166 140 | Align = alClient 141 | ButtonFlow = cbfVertical 142 | ButtonOptions = [boFullSize, boGradientFill, boShowCaptions, boUsePlusMinus] 143 | Categories = < 144 | item 145 | Caption = 'Document' 146 | Color = 15466474 147 | Collapsed = False 148 | Items = < 149 | item 150 | Action = actDocumentNew 151 | end 152 | item 153 | Action = actDocumentClose 154 | end 155 | item 156 | Action = actDocumentOpen 157 | end 158 | item 159 | Action = actDocumentSave 160 | end> 161 | end 162 | item 163 | Caption = 'General' 164 | Color = 16771818 165 | Collapsed = False 166 | Items = < 167 | item 168 | Action = actJSONReplace 169 | end> 170 | end> 171 | RegularButtonColor = clWhite 172 | SelectedButtonColor = 15132390 173 | TabOrder = 1 174 | end 175 | end 176 | end 177 | object aclCommands: TActionList 178 | Images = imgCommands 179 | Left = 176 180 | Top = 168 181 | object actAddInfo: TAction 182 | Caption = 'Add Info Object' 183 | OnExecute = actAddInfoExecute 184 | end 185 | object actAddInfoExtensions: TAction 186 | Caption = 'Add Info Object Extensions' 187 | OnExecute = actAddInfoExtensionsExecute 188 | end 189 | object actAddServers: TAction 190 | Caption = 'Add Servers' 191 | OnExecute = actAddServersExecute 192 | end 193 | object actAddPaths: TAction 194 | Caption = 'Add Paths && Params' 195 | OnExecute = actAddPathsExecute 196 | end 197 | object actAddSecurity: TAction 198 | Caption = 'Add Security' 199 | OnExecute = actAddSecurityExecute 200 | end 201 | object actCompAddSchemas: TAction 202 | Caption = 'Add Schemas' 203 | OnExecute = actCompAddSchemasExecute 204 | end 205 | object actCompAddResponses: TAction 206 | Caption = 'Add Responses' 207 | OnExecute = actCompAddResponsesExecute 208 | end 209 | object actCompAddSecurityDefs: TAction 210 | Caption = 'Add SecurityDefs' 211 | OnExecute = actCompAddSecurityDefsExecute 212 | end 213 | object actCompAddParameters: TAction 214 | Caption = 'Add Parameters' 215 | OnExecute = actCompAddParametersExecute 216 | end 217 | object actCompAddRequestBodies: TAction 218 | Caption = 'Add RequestBodies' 219 | end 220 | object actJSONGenerate: TAction 221 | Caption = 'Generate JSON Document' 222 | OnExecute = actJSONGenerateExecute 223 | end 224 | object actJSONReplace: TAction 225 | Caption = 'Replace Escaped Slash "/"' 226 | OnExecute = actJSONReplaceExecute 227 | end 228 | object actDocumentOpen: TAction 229 | Caption = 'Load Document (JSON)' 230 | OnExecute = actDocumentOpenExecute 231 | end 232 | object actDocumentSave: TAction 233 | Caption = 'Save Document (JSON)' 234 | OnExecute = actDocumentSaveExecute 235 | end 236 | object actDocumentNew: TAction 237 | Caption = 'New Document' 238 | OnExecute = actDocumentNewExecute 239 | end 240 | object actDocumentClose: TAction 241 | Caption = 'Close Document' 242 | OnExecute = actDocumentCloseExecute 243 | end 244 | end 245 | object imgCommands: TImageList 246 | Left = 184 247 | Top = 216 248 | end 249 | object dlgOpenJSON: TOpenDialog 250 | Filter = 'Schema Documents|*.json|All Files|*.*' 251 | Left = 440 252 | Top = 296 253 | end 254 | object dlgSaveDocument: TSaveDialog 255 | DefaultExt = 'json' 256 | Filter = 'OpenAPI Document|*.json' 257 | Left = 440 258 | Top = 360 259 | end 260 | end 261 | -------------------------------------------------------------------------------- /Demos/OpenAPI/Demo.Form.Main.pas: -------------------------------------------------------------------------------- 1 | unit Demo.Form.Main; 2 | 3 | interface 4 | 5 | uses 6 | Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, 7 | Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.CategoryButtons, 8 | Vcl.ExtCtrls, System.Actions, Vcl.ActnList, System.ImageList, Vcl.ImgList, 9 | System.Rtti, System.JSON, 10 | 11 | Neon.Core.Types, 12 | Neon.Core.Persistence, 13 | Neon.Core.Persistence.JSON, 14 | Neon.Core.Persistence.JSON.Schema, 15 | OpenAPI.Model.Classes, 16 | OpenAPI.Model.Schema, 17 | OpenAPI.Neon.Serializers; 18 | 19 | type 20 | TPerson = class 21 | private 22 | FAge: Integer; 23 | FName: string; 24 | public 25 | property Age: Integer read FAge write FAge; 26 | property Name: string read FName write FName; 27 | end; 28 | 29 | TfrmMain = class(TForm) 30 | memoDocument: TMemo; 31 | catMenu: TCategoryPanelGroup; 32 | pnlSections: TCategoryPanel; 33 | CategoryButtons1: TCategoryButtons; 34 | pnlDocument: TCategoryPanel; 35 | CategoryButtons2: TCategoryButtons; 36 | catJSON: TCategoryButtons; 37 | aclCommands: TActionList; 38 | imgCommands: TImageList; 39 | actAddInfo: TAction; 40 | actAddServers: TAction; 41 | actAddPaths: TAction; 42 | actAddSecurity: TAction; 43 | actCompAddSchemas: TAction; 44 | actCompAddResponses: TAction; 45 | actCompAddSecurityDefs: TAction; 46 | actJSONGenerate: TAction; 47 | actJSONReplace: TAction; 48 | actCompAddParameters: TAction; 49 | actCompAddRequestBodies: TAction; 50 | actAddInfoExtensions: TAction; 51 | actDocumentOpen: TAction; 52 | actDocumentSave: TAction; 53 | dlgOpenJSON: TOpenDialog; 54 | actDocumentNew: TAction; 55 | actDocumentClose: TAction; 56 | dlgSaveDocument: TSaveDialog; 57 | procedure FormCreate(Sender: TObject); 58 | procedure FormDestroy(Sender: TObject); 59 | procedure actAddInfoExecute(Sender: TObject); 60 | procedure actAddInfoExtensionsExecute(Sender: TObject); 61 | procedure actAddServersExecute(Sender: TObject); 62 | procedure actAddPathsExecute(Sender: TObject); 63 | procedure actCompAddResponsesExecute(Sender: TObject); 64 | procedure actCompAddSchemasExecute(Sender: TObject); 65 | procedure actCompAddSecurityDefsExecute(Sender: TObject); 66 | procedure actAddSecurityExecute(Sender: TObject); 67 | procedure actCompAddParametersExecute(Sender: TObject); 68 | procedure actDocumentCloseExecute(Sender: TObject); 69 | procedure actDocumentNewExecute(Sender: TObject); 70 | procedure actJSONGenerateExecute(Sender: TObject); 71 | procedure actDocumentOpenExecute(Sender: TObject); 72 | procedure actDocumentSaveExecute(Sender: TObject); 73 | procedure actJSONReplaceExecute(Sender: TObject); 74 | private 75 | FDocumentName: string; 76 | FDocument: TOpenAPIDocument; 77 | public 78 | { Public declarations } 79 | end; 80 | 81 | var 82 | frmMain: TfrmMain; 83 | 84 | implementation 85 | 86 | uses 87 | System.IOUtils; 88 | 89 | {$R *.dfm} 90 | 91 | procedure TfrmMain.FormCreate(Sender: TObject); 92 | begin 93 | dlgOpenJSON.InitialDir := TPath.GetDirectoryName(TPath.GetDirectoryName(Application.ExeName)) + '\Data\samples\'; 94 | end; 95 | 96 | procedure TfrmMain.FormDestroy(Sender: TObject); 97 | begin 98 | FDocument.Free; 99 | end; 100 | 101 | procedure TfrmMain.actAddInfoExecute(Sender: TObject); 102 | begin 103 | FDocument.Info.Title := 'OpenAPI Demo'; 104 | FDocument.Info.Version := '1.0.2'; 105 | FDocument.Info.Description := 'OpenAPI Demo Description'; 106 | FDocument.Info.Contact.Name := 'Paolo Rossi'; 107 | FDocument.Info.Contact.URL := 'https://github.com/paolo-rossi'; 108 | FDocument.Info.License.Name := 'Apache-2.0'; 109 | FDocument.Info.License.URL := 'http://www.apache.org/licenses/'; 110 | end; 111 | 112 | procedure TfrmMain.actAddInfoExtensionsExecute(Sender: TObject); 113 | var 114 | LJSON: TJSONObject; 115 | begin 116 | LJSON := TJSONObject.Create; 117 | LJSON.AddPair('url', '/images/wirl.png'); 118 | LJSON.AddPair('backgroundColor', '#000000'); 119 | LJSON.AddPair('altText', 'WiRL Logo'); 120 | 121 | FDocument.Info.Extensions.Add('x-logo', LJSON); 122 | 123 | // Testing the Dictionary serialization for Extensions 124 | //FDocument.Info.Ext.Add('test', 'prova'); 125 | //FDocument.Info.Ext.Add('xyz', TStringList.Create.Add('Prova Item 1')); 126 | 127 | end; 128 | 129 | procedure TfrmMain.actAddServersExecute(Sender: TObject); 130 | begin 131 | FDocument.Servers.Add(TOpenAPIServer.Create('https://api.wirl.com/rest/app/', 'Production Server')); 132 | FDocument.Servers.Add(TOpenAPIServer.Create('https://beta.wirl.com/rest/app/', 'Beta Server API v2')); 133 | FDocument.Servers.Add(TOpenAPIServer.Create('https://test.wirl.com/rest/app/', 'Testing Server')); 134 | end; 135 | 136 | procedure TfrmMain.actAddPathsExecute(Sender: TObject); 137 | var 138 | LPath: TOpenAPIPathItem; 139 | LOperation: TOpenAPIOperation; 140 | LParameter: TOpenAPIParameter; 141 | begin 142 | LPath := FDocument.AddPath('/customers'); 143 | LPath.Description := 'Customers resource'; 144 | 145 | LOperation := LPath.AddOperation(TOperationType.Get); 146 | LOperation.Summary := 'Get all customers'; 147 | LOperation.OperationId := 'CustomerList'; 148 | 149 | LParameter := LOperation.AddParameter('id', 'query'); 150 | LParameter.Description := 'Customer ID'; 151 | LParameter.Schema.Type_ := 'string'; 152 | 153 | LParameter := LOperation.AddParameter('country', 'query'); 154 | LParameter.Description := 'Country Code'; 155 | LParameter.Schema.Type_ := 'string'; 156 | LParameter.Schema.AddEnum('it'); 157 | LParameter.Schema.AddEnum('br'); 158 | LParameter.Schema.AddEnum('us'); 159 | LParameter.Schema.AddEnum('uk'); 160 | 161 | LParameter := LOperation.AddParameter('date', 'query'); 162 | LParameter.Description := 'Date'; 163 | LParameter.Schema.Type_ := 'string'; 164 | LParameter.Schema.Format := 'date-time'; 165 | 166 | // Uses a JSON schema already existing as a TJSONObject 167 | LParameter := LOperation.AddParameter('person', 'query'); 168 | LParameter.Description := 'Person Entity'; 169 | LParameter.Schema.SetJSONObject(TNeonSchemaGenerator.ClassToJSONSchema(TPerson)); 170 | 171 | // Uses #ref 172 | LParameter := LOperation.AddParameter('order', 'query'); 173 | LParameter.Description := 'Order Entity'; 174 | LParameter.Schema.Reference.Ref := '#components/schemas/order'; 175 | end; 176 | 177 | procedure TfrmMain.actCompAddResponsesExecute(Sender: TObject); 178 | var 179 | LResponse: TOpenAPIResponse; 180 | LMediaType: TOpenAPIMediaType; 181 | begin 182 | LResponse := FDocument.Components.AddResponse('200', 'Successful Response'); 183 | LMediaType := LResponse.AddMediaType('application/json'); 184 | LMediaType.Schema.Reference.Ref := '#components/schemas/country'; 185 | end; 186 | 187 | procedure TfrmMain.actCompAddSchemasExecute(Sender: TObject); 188 | var 189 | LSchema: TOpenAPISchema; 190 | LProperty: TOpenAPISchema; 191 | begin 192 | LSchema := FDocument.Components.AddSchema('Person'); 193 | LSchema.Type_ := 'object'; 194 | 195 | LProperty := LSchema.AddProperty('id'); 196 | LProperty.Title := 'ID Value'; 197 | LProperty.Description := 'AutoInc **ID** value'; 198 | LProperty.Type_ := 'integer'; 199 | LProperty.Format := 'int64'; 200 | 201 | LProperty := LSchema.AddProperty('firstname'); 202 | LProperty.Type_ := 'string'; 203 | 204 | LProperty := LSchema.AddProperty('lastname'); 205 | LProperty.Type_ := 'string'; 206 | 207 | LProperty := LSchema.AddProperty('birthdate'); 208 | LProperty.Title := 'Birth Date'; 209 | LProperty.Description := 'Birth Date'; 210 | LProperty.Type_ := 'string'; 211 | LProperty.Format := 'date-time'; 212 | end; 213 | 214 | procedure TfrmMain.actCompAddSecurityDefsExecute(Sender: TObject); 215 | begin 216 | FDocument.Components.AddSecurityApiKey('key_auth', 'Key Standard Authentication', 'X-ApiKey', tapikeylocation.Header); 217 | FDocument.Components.AddSecurityHttp('basic_auth', 'Basic Authentication', 'Basic', ''); 218 | FDocument.Components.AddSecurityHttp('jwt_auth', 'JWT (Bearer) Authentication', 'Bearer', 'JWT'); 219 | end; 220 | 221 | procedure TfrmMain.actAddSecurityExecute(Sender: TObject); 222 | begin 223 | FDocument.AddSecurity('basic_auth', []); 224 | FDocument.AddSecurity('jwt_auth', []); 225 | end; 226 | 227 | procedure TfrmMain.actCompAddParametersExecute(Sender: TObject); 228 | var 229 | LParameter: TOpenAPIParameter; 230 | begin 231 | LParameter := FDocument.Components.AddParameter('idParam', 'id', 'query'); 232 | LParameter.Description := 'Customer ID'; 233 | LParameter.Schema.Type_ := 'string'; 234 | LParameter.Schema.MaxLength := 20; 235 | end; 236 | 237 | procedure TfrmMain.actDocumentCloseExecute(Sender: TObject); 238 | begin 239 | FreeAndNil(FDocument); 240 | memoDocument.Clear; 241 | pnlSections.Visible := False; 242 | end; 243 | 244 | procedure TfrmMain.actDocumentNewExecute(Sender: TObject); 245 | begin 246 | FDocument := TOpenAPIDocument.Create(TOpenAPIVersion.v303); 247 | pnlSections.Visible := True; 248 | FDocumentName := 'MyAPI'; 249 | end; 250 | 251 | procedure TfrmMain.actJSONGenerateExecute(Sender: TObject); 252 | begin 253 | memoDocument.Lines.Text := 254 | TNeon.ObjectToJSONString(FDocument, TOpenAPISerializer.GetNeonConfig); 255 | end; 256 | 257 | procedure TfrmMain.actDocumentOpenExecute(Sender: TObject); 258 | var 259 | LDocument: TOpenAPIDocument; 260 | LJSON: TJSONObject; 261 | LConfig: INeonConfiguration; 262 | begin 263 | if not dlgOpenJSON.Execute() then 264 | Exit; 265 | 266 | LDocument := TOpenAPIDocument.Create(TOpenAPIVersion.v303); 267 | try 268 | LJSON := TJSONObject.ParseJSONValue(TFile.ReadAllText(dlgOpenJSON.FileName)) as TJSONObject; 269 | try 270 | LConfig := TOpenAPISerializer.GetNeonConfig; 271 | 272 | TNeon.JSONToObject(LDocument, LJSON, LConfig); 273 | finally 274 | LJSON.Free; 275 | end; 276 | 277 | // Serialization in order to see the document loaded 278 | memoDocument.Text := TNeon.ObjectToJSONString(LDocument, LConfig); 279 | finally 280 | LDocument.Free; 281 | end; 282 | FDocumentName := TPath.GetFileNameWithoutExtension(dlgOpenJSON.FileName); 283 | end; 284 | 285 | procedure TfrmMain.actDocumentSaveExecute(Sender: TObject); 286 | begin 287 | dlgSaveDocument.FileName := FDocumentName + '.json'; 288 | if not dlgSaveDocument.Execute then 289 | Exit; 290 | 291 | memoDocument.Lines.SaveToFile(dlgSaveDocument.FileName, TEncoding.UTF8); 292 | end; 293 | 294 | procedure TfrmMain.actJSONReplaceExecute(Sender: TObject); 295 | begin 296 | memoDocument.Lines.Text := 297 | StringReplace(memoDocument.Lines.Text, '\/', '/', [rfReplaceAll]); 298 | end; 299 | 300 | end. 301 | -------------------------------------------------------------------------------- /Demos/OpenAPI/DemoOpenAPI.dpr: -------------------------------------------------------------------------------- 1 | program DemoOpenAPI; 2 | 3 | uses 4 | Vcl.Forms, 5 | Demo.Form.Main in 'Demo.Form.Main.pas' {frmMain}, 6 | OpenAPI.Core.Interfaces in '..\..\Source\OpenAPI.Core.Interfaces.pas', 7 | OpenAPI.Core.Exceptions in '..\..\Source\OpenAPI.Core.Exceptions.pas', 8 | OpenAPI.Model.Any in '..\..\Source\OpenAPI.Model.Any.pas', 9 | OpenAPI.Model.Schema in '..\..\Source\OpenAPI.Model.Schema.pas', 10 | OpenAPI.Model.Classes in '..\..\Source\OpenAPI.Model.Classes.pas', 11 | OpenAPI.Model.Reference in '..\..\Source\OpenAPI.Model.Reference.pas', 12 | OpenAPI.Model.Expressions in '..\..\Source\OpenAPI.Model.Expressions.pas', 13 | OpenAPI.Model.JsonPointer in '..\..\Source\OpenAPI.Model.JsonPointer.pas', 14 | OpenAPI.Neon.Serializers in '..\..\Source\OpenAPI.Neon.Serializers.pas', 15 | OpenAPI.Model.Base in '..\..\Source\OpenAPI.Model.Base.pas'; 16 | 17 | {$R *.res} 18 | 19 | begin 20 | ReportMemoryLeaksOnShutdown := True; 21 | Application.Initialize; 22 | Application.MainFormOnTaskbar := True; 23 | Application.CreateForm(TfrmMain, frmMain); 24 | Application.Run; 25 | end. 26 | -------------------------------------------------------------------------------- /Demos/OpenAPI/DemoOpenAPI.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paolo-rossi/OpenAPI-Delphi/02c7fbeb4256e1606fbbc5b39e95629c59423df3/Demos/OpenAPI/DemoOpenAPI.res -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Packages/10.2Tokyo/OpenAPI.dpk: -------------------------------------------------------------------------------- 1 | {******************************************************************************} 2 | { } 3 | { Delphi OpenAPI 3.0 Generator } 4 | { Copyright (c) 2018-2021 Paolo Rossi } 5 | { https://github.com/paolo-rossi/delphi-openapi } 6 | { } 7 | {******************************************************************************} 8 | { } 9 | { Licensed under the Apache License, Version 2.0 (the 'License"); } 10 | { you may not use this file except in compliance with the License. } 11 | { You may obtain a copy of the License at } 12 | { } 13 | { http://www.apache.org/licenses/LICENSE-2.0 } 14 | { } 15 | { Unless required by applicable law or agreed to in writing, software } 16 | { distributed under the License is distributed on an "AS IS" BASIS, } 17 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } 18 | { See the License for the specific language governing permissions and } 19 | { limitations under the License. } 20 | { } 21 | {******************************************************************************} 22 | package OpenAPI; 23 | 24 | {$R *.res} 25 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} 26 | {$ALIGN 8} 27 | {$ASSERTIONS ON} 28 | {$BOOLEVAL OFF} 29 | {$DEBUGINFO OFF} 30 | {$EXTENDEDSYNTAX ON} 31 | {$IMPORTEDDATA ON} 32 | {$IOCHECKS ON} 33 | {$LOCALSYMBOLS ON} 34 | {$LONGSTRINGS ON} 35 | {$OPENSTRINGS ON} 36 | {$OPTIMIZATION OFF} 37 | {$OVERFLOWCHECKS OFF} 38 | {$RANGECHECKS OFF} 39 | {$REFERENCEINFO ON} 40 | {$SAFEDIVIDE OFF} 41 | {$STACKFRAMES ON} 42 | {$TYPEDADDRESS OFF} 43 | {$VARSTRINGCHECKS ON} 44 | {$WRITEABLECONST OFF} 45 | {$MINENUMSIZE 1} 46 | {$IMAGEBASE $400000} 47 | {$DEFINE DEBUG} 48 | {$ENDIF IMPLICITBUILDING} 49 | {$DESCRIPTION 'Delphi OpenAPI 3 Library'} 50 | {$LIBSUFFIX '250'} 51 | {$RUNONLY} 52 | {$IMPLICITBUILD OFF} 53 | 54 | requires 55 | rtl, 56 | Neon; 57 | 58 | contains 59 | OpenAPI.Core.Exceptions in '..\..\Source\OpenAPI.Core.Exceptions.pas', 60 | OpenAPI.Core.Interfaces in '..\..\Source\OpenAPI.Core.Interfaces.pas', 61 | OpenAPI.Model.Any in '..\..\Source\OpenAPI.Model.Any.pas', 62 | OpenAPI.Model.Base in '..\..\Source\OpenAPI.Model.Base.pas', 63 | OpenAPI.Model.Classes in '..\..\Source\OpenAPI.Model.Classes.pas', 64 | OpenAPI.Model.Expressions in '..\..\Source\OpenAPI.Model.Expressions.pas', 65 | OpenAPI.Model.JsonPointer in '..\..\Source\OpenAPI.Model.JsonPointer.pas', 66 | OpenAPI.Model.Reference in '..\..\Source\OpenAPI.Model.Reference.pas', 67 | OpenAPI.Model.Schema in '..\..\Source\OpenAPI.Model.Schema.pas', 68 | OpenAPI.Neon.Serializers in '..\..\Source\OpenAPI.Neon.Serializers.pas'; 69 | 70 | end. 71 | 72 | -------------------------------------------------------------------------------- /Packages/10.2Tokyo/OpenAPI.dproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | {511659C1-0C7F-4B9E-BEDF-0B30D8057D8D} 4 | OpenAPI.dpk 5 | True 6 | Debug 7 | 131 8 | Package 9 | None 10 | 19.2 11 | Win32 12 | 13 | 14 | true 15 | 16 | 17 | true 18 | Base 19 | true 20 | 21 | 22 | true 23 | Base 24 | true 25 | 26 | 27 | true 28 | Base 29 | true 30 | 31 | 32 | true 33 | Cfg_1 34 | true 35 | true 36 | 37 | 38 | true 39 | Cfg_1 40 | true 41 | true 42 | 43 | 44 | true 45 | Base 46 | true 47 | 48 | 49 | true 50 | Cfg_2 51 | true 52 | true 53 | 54 | 55 | true 56 | Cfg_2 57 | true 58 | true 59 | 60 | 61 | true 62 | ..\..\Source;$(DCC_UnitSearchPath) 63 | OpenAPI 64 | 1040 65 | Delphi OpenAPI 3 Library 66 | true 67 | false 68 | false 69 | false 70 | 250 71 | CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= 72 | true 73 | false 74 | 00400000 75 | true 76 | false 77 | true 78 | ..\..\Lib\$(DLLSuffix)\$(Platform)\$(Config) 79 | 80 | 81 | /usr/bin/xterm -e "%debuggee%" 82 | CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=;CFBundleVersion=1.0.0.0;CFBundleShortVersionString=1.0.0.0 83 | 84 | 85 | rtl;dbrtl;inet;IndySystem;IndyProtocols;IndyCore;FireDAC;FireDACCommonDriver;FireDACCommon;bindengine;bindcomp;WiRLClient;$(DCC_UsePackage) 86 | 1033 87 | Debug 88 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) 89 | 90 | 91 | 0 92 | 0 93 | false 94 | RELEASE;$(DCC_Define) 95 | 96 | 97 | CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=;CFBundleVersion=1.0.0.0;CFBundleShortVersionString=1.0.0.0 98 | 99 | 100 | 1033 101 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) 102 | 103 | 104 | DEBUG;$(DCC_Define) 105 | false 106 | true 107 | 108 | 109 | CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=;CFBundleVersion=1.0.0.0;CFBundleShortVersionString=1.0.0.0 110 | 111 | 112 | 1033 113 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) 114 | 115 | 116 | 117 | MainSource 118 | 119 | 120 | 121 | 122 | Cfg_2 123 | Base 124 | 125 | 126 | Base 127 | 128 | 129 | Cfg_1 130 | Base 131 | 132 | 133 | 134 | Delphi.Personality.12 135 | Package 136 | 137 | 138 | 139 | OpenAPI.dpk 140 | 141 | 142 | 143 | 144 | True 145 | True 146 | True 147 | 148 | 149 | 12 150 | 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /Packages/10.2Tokyo/OpenAPI.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paolo-rossi/OpenAPI-Delphi/02c7fbeb4256e1606fbbc5b39e95629c59423df3/Packages/10.2Tokyo/OpenAPI.res -------------------------------------------------------------------------------- /Packages/10.3Rio/OpenAPI.dpk: -------------------------------------------------------------------------------- 1 | {******************************************************************************} 2 | { } 3 | { Delphi OpenAPI 3.0 Generator } 4 | { Copyright (c) 2018-2021 Paolo Rossi } 5 | { https://github.com/paolo-rossi/delphi-openapi } 6 | { } 7 | {******************************************************************************} 8 | { } 9 | { Licensed under the Apache License, Version 2.0 (the 'License"); } 10 | { you may not use this file except in compliance with the License. } 11 | { You may obtain a copy of the License at } 12 | { } 13 | { http://www.apache.org/licenses/LICENSE-2.0 } 14 | { } 15 | { Unless required by applicable law or agreed to in writing, software } 16 | { distributed under the License is distributed on an "AS IS" BASIS, } 17 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } 18 | { See the License for the specific language governing permissions and } 19 | { limitations under the License. } 20 | { } 21 | {******************************************************************************} 22 | package OpenAPI; 23 | 24 | {$R *.res} 25 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} 26 | {$ALIGN 8} 27 | {$ASSERTIONS ON} 28 | {$BOOLEVAL OFF} 29 | {$DEBUGINFO OFF} 30 | {$EXTENDEDSYNTAX ON} 31 | {$IMPORTEDDATA ON} 32 | {$IOCHECKS ON} 33 | {$LOCALSYMBOLS ON} 34 | {$LONGSTRINGS ON} 35 | {$OPENSTRINGS ON} 36 | {$OPTIMIZATION OFF} 37 | {$OVERFLOWCHECKS OFF} 38 | {$RANGECHECKS OFF} 39 | {$REFERENCEINFO ON} 40 | {$SAFEDIVIDE OFF} 41 | {$STACKFRAMES ON} 42 | {$TYPEDADDRESS OFF} 43 | {$VARSTRINGCHECKS ON} 44 | {$WRITEABLECONST OFF} 45 | {$MINENUMSIZE 1} 46 | {$IMAGEBASE $400000} 47 | {$DEFINE DEBUG} 48 | {$ENDIF IMPLICITBUILDING} 49 | {$DESCRIPTION 'Delphi OpenAPI 3 Library'} 50 | {$LIBSUFFIX '260'} 51 | {$RUNONLY} 52 | {$IMPLICITBUILD OFF} 53 | 54 | requires 55 | rtl, 56 | Neon; 57 | 58 | contains 59 | OpenAPI.Core.Exceptions in '..\..\Source\OpenAPI.Core.Exceptions.pas', 60 | OpenAPI.Core.Interfaces in '..\..\Source\OpenAPI.Core.Interfaces.pas', 61 | OpenAPI.Model.Any in '..\..\Source\OpenAPI.Model.Any.pas', 62 | OpenAPI.Model.Base in '..\..\Source\OpenAPI.Model.Base.pas', 63 | OpenAPI.Model.Classes in '..\..\Source\OpenAPI.Model.Classes.pas', 64 | OpenAPI.Model.Expressions in '..\..\Source\OpenAPI.Model.Expressions.pas', 65 | OpenAPI.Model.JsonPointer in '..\..\Source\OpenAPI.Model.JsonPointer.pas', 66 | OpenAPI.Model.Reference in '..\..\Source\OpenAPI.Model.Reference.pas', 67 | OpenAPI.Model.Schema in '..\..\Source\OpenAPI.Model.Schema.pas', 68 | OpenAPI.Neon.Serializers in '..\..\Source\OpenAPI.Neon.Serializers.pas'; 69 | 70 | end. 71 | 72 | -------------------------------------------------------------------------------- /Packages/10.3Rio/OpenAPI.dproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | {511659C1-0C7F-4B9E-BEDF-0B30D8057D8D} 4 | OpenAPI.dpk 5 | True 6 | Debug 7 | 131 8 | Package 9 | None 10 | 19.2 11 | Win32 12 | 13 | 14 | true 15 | 16 | 17 | true 18 | Base 19 | true 20 | 21 | 22 | true 23 | Base 24 | true 25 | 26 | 27 | true 28 | Base 29 | true 30 | 31 | 32 | true 33 | Cfg_1 34 | true 35 | true 36 | 37 | 38 | true 39 | Cfg_1 40 | true 41 | true 42 | 43 | 44 | true 45 | Base 46 | true 47 | 48 | 49 | true 50 | Cfg_2 51 | true 52 | true 53 | 54 | 55 | true 56 | Cfg_2 57 | true 58 | true 59 | 60 | 61 | true 62 | ..\..\Source;$(DCC_UnitSearchPath) 63 | OpenAPI 64 | 1040 65 | Delphi OpenAPI 3 Library 66 | true 67 | false 68 | false 69 | false 70 | 260 71 | CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= 72 | true 73 | false 74 | 00400000 75 | true 76 | false 77 | true 78 | ..\..\Lib\$(DLLSuffix)\$(Platform)\$(Config) 79 | 80 | 81 | /usr/bin/xterm -e "%debuggee%" 82 | CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=;CFBundleVersion=1.0.0.0;CFBundleShortVersionString=1.0.0.0 83 | 84 | 85 | rtl;dbrtl;inet;IndySystem;IndyProtocols;IndyCore;FireDAC;FireDACCommonDriver;FireDACCommon;bindengine;bindcomp;WiRLClient;$(DCC_UsePackage) 86 | 1033 87 | Debug 88 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) 89 | 90 | 91 | 0 92 | 0 93 | false 94 | RELEASE;$(DCC_Define) 95 | 96 | 97 | CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=;CFBundleVersion=1.0.0.0;CFBundleShortVersionString=1.0.0.0 98 | 99 | 100 | 1033 101 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) 102 | 103 | 104 | DEBUG;$(DCC_Define) 105 | false 106 | true 107 | 108 | 109 | CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=;CFBundleVersion=1.0.0.0;CFBundleShortVersionString=1.0.0.0 110 | 111 | 112 | 1033 113 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) 114 | 115 | 116 | 117 | MainSource 118 | 119 | 120 | 121 | 122 | Cfg_2 123 | Base 124 | 125 | 126 | Base 127 | 128 | 129 | Cfg_1 130 | Base 131 | 132 | 133 | 134 | Delphi.Personality.12 135 | Package 136 | 137 | 138 | 139 | OpenAPI.dpk 140 | 141 | 142 | 143 | 144 | True 145 | True 146 | True 147 | 148 | 149 | 12 150 | 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /Packages/10.3Rio/OpenAPI.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paolo-rossi/OpenAPI-Delphi/02c7fbeb4256e1606fbbc5b39e95629c59423df3/Packages/10.3Rio/OpenAPI.res -------------------------------------------------------------------------------- /Packages/10.4Sydney/OpenAPI.dpk: -------------------------------------------------------------------------------- 1 | {******************************************************************************} 2 | { } 3 | { Delphi OpenAPI 3.0 Generator } 4 | { Copyright (c) 2018-2021 Paolo Rossi } 5 | { https://github.com/paolo-rossi/delphi-openapi } 6 | { } 7 | {******************************************************************************} 8 | { } 9 | { Licensed under the Apache License, Version 2.0 (the 'License"); } 10 | { you may not use this file except in compliance with the License. } 11 | { You may obtain a copy of the License at } 12 | { } 13 | { http://www.apache.org/licenses/LICENSE-2.0 } 14 | { } 15 | { Unless required by applicable law or agreed to in writing, software } 16 | { distributed under the License is distributed on an "AS IS" BASIS, } 17 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } 18 | { See the License for the specific language governing permissions and } 19 | { limitations under the License. } 20 | { } 21 | {******************************************************************************} 22 | package OpenAPI; 23 | 24 | {$R *.res} 25 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} 26 | {$ALIGN 8} 27 | {$ASSERTIONS ON} 28 | {$BOOLEVAL OFF} 29 | {$DEBUGINFO OFF} 30 | {$EXTENDEDSYNTAX ON} 31 | {$IMPORTEDDATA ON} 32 | {$IOCHECKS ON} 33 | {$LOCALSYMBOLS ON} 34 | {$LONGSTRINGS ON} 35 | {$OPENSTRINGS ON} 36 | {$OPTIMIZATION OFF} 37 | {$OVERFLOWCHECKS OFF} 38 | {$RANGECHECKS OFF} 39 | {$REFERENCEINFO ON} 40 | {$SAFEDIVIDE OFF} 41 | {$STACKFRAMES ON} 42 | {$TYPEDADDRESS OFF} 43 | {$VARSTRINGCHECKS ON} 44 | {$WRITEABLECONST OFF} 45 | {$MINENUMSIZE 1} 46 | {$IMAGEBASE $400000} 47 | {$DEFINE DEBUG} 48 | {$ENDIF IMPLICITBUILDING} 49 | {$DESCRIPTION 'Delphi OpenAPI 3 Library'} 50 | {$LIBSUFFIX '270'} 51 | {$RUNONLY} 52 | {$IMPLICITBUILD OFF} 53 | 54 | requires 55 | rtl, 56 | Neon; 57 | 58 | contains 59 | OpenAPI.Core.Exceptions in '..\..\Source\OpenAPI.Core.Exceptions.pas', 60 | OpenAPI.Core.Interfaces in '..\..\Source\OpenAPI.Core.Interfaces.pas', 61 | OpenAPI.Model.Any in '..\..\Source\OpenAPI.Model.Any.pas', 62 | OpenAPI.Model.Base in '..\..\Source\OpenAPI.Model.Base.pas', 63 | OpenAPI.Model.Classes in '..\..\Source\OpenAPI.Model.Classes.pas', 64 | OpenAPI.Model.Expressions in '..\..\Source\OpenAPI.Model.Expressions.pas', 65 | OpenAPI.Model.JsonPointer in '..\..\Source\OpenAPI.Model.JsonPointer.pas', 66 | OpenAPI.Model.Reference in '..\..\Source\OpenAPI.Model.Reference.pas', 67 | OpenAPI.Model.Schema in '..\..\Source\OpenAPI.Model.Schema.pas', 68 | OpenAPI.Neon.Serializers in '..\..\Source\OpenAPI.Neon.Serializers.pas'; 69 | 70 | end. 71 | 72 | -------------------------------------------------------------------------------- /Packages/10.4Sydney/OpenAPI.dproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | {511659C1-0C7F-4B9E-BEDF-0B30D8057D8D} 4 | OpenAPI.dpk 5 | True 6 | Debug 7 | 131 8 | Package 9 | None 10 | 19.2 11 | Win32 12 | 13 | 14 | true 15 | 16 | 17 | true 18 | Base 19 | true 20 | 21 | 22 | true 23 | Base 24 | true 25 | 26 | 27 | true 28 | Base 29 | true 30 | 31 | 32 | true 33 | Cfg_1 34 | true 35 | true 36 | 37 | 38 | true 39 | Cfg_1 40 | true 41 | true 42 | 43 | 44 | true 45 | Base 46 | true 47 | 48 | 49 | true 50 | Cfg_2 51 | true 52 | true 53 | 54 | 55 | true 56 | Cfg_2 57 | true 58 | true 59 | 60 | 61 | true 62 | ..\..\Source;$(DCC_UnitSearchPath) 63 | OpenAPI 64 | 1040 65 | Delphi OpenAPI 3 Library 66 | true 67 | false 68 | false 69 | false 70 | 270 71 | CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName= 72 | true 73 | false 74 | 00400000 75 | true 76 | false 77 | true 78 | ..\..\Lib\$(DLLSuffix)\$(Platform)\$(Config) 79 | 80 | 81 | /usr/bin/xterm -e "%debuggee%" 82 | CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=;CFBundleVersion=1.0.0.0;CFBundleShortVersionString=1.0.0.0 83 | 84 | 85 | rtl;dbrtl;inet;IndySystem;IndyProtocols;IndyCore;FireDAC;FireDACCommonDriver;FireDACCommon;bindengine;bindcomp;WiRLClient;$(DCC_UsePackage) 86 | 1033 87 | Debug 88 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) 89 | 90 | 91 | 0 92 | 0 93 | false 94 | RELEASE;$(DCC_Define) 95 | 96 | 97 | CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=;CFBundleVersion=1.0.0.0;CFBundleShortVersionString=1.0.0.0 98 | 99 | 100 | 1033 101 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) 102 | 103 | 104 | DEBUG;$(DCC_Define) 105 | false 106 | true 107 | 108 | 109 | CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=;CFBundleVersion=1.0.0.0;CFBundleShortVersionString=1.0.0.0 110 | 111 | 112 | 1033 113 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) 114 | 115 | 116 | 117 | MainSource 118 | 119 | 120 | 121 | 122 | Cfg_2 123 | Base 124 | 125 | 126 | Base 127 | 128 | 129 | Cfg_1 130 | Base 131 | 132 | 133 | 134 | Delphi.Personality.12 135 | Package 136 | 137 | 138 | 139 | OpenAPI.dpk 140 | 141 | 142 | 143 | 144 | True 145 | True 146 | True 147 | 148 | 149 | 12 150 | 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /Packages/10.4Sydney/OpenAPI.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paolo-rossi/OpenAPI-Delphi/02c7fbeb4256e1606fbbc5b39e95629c59423df3/Packages/10.4Sydney/OpenAPI.res -------------------------------------------------------------------------------- /Packages/11.0Alexandria/OpenAPI.dpk: -------------------------------------------------------------------------------- 1 | {******************************************************************************} 2 | { } 3 | { Delphi OpenAPI 3.0 Generator } 4 | { Copyright (c) 2018-2023 Paolo Rossi } 5 | { https://github.com/paolo-rossi/delphi-openapi } 6 | { } 7 | {******************************************************************************} 8 | { } 9 | { Licensed under the Apache License, Version 2.0 (the 'License"); } 10 | { you may not use this file except in compliance with the License. } 11 | { You may obtain a copy of the License at } 12 | { } 13 | { http://www.apache.org/licenses/LICENSE-2.0 } 14 | { } 15 | { Unless required by applicable law or agreed to in writing, software } 16 | { distributed under the License is distributed on an "AS IS" BASIS, } 17 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } 18 | { See the License for the specific language governing permissions and } 19 | { limitations under the License. } 20 | { } 21 | {******************************************************************************} 22 | package OpenAPI; 23 | 24 | {$R *.res} 25 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} 26 | {$ALIGN 8} 27 | {$ASSERTIONS ON} 28 | {$BOOLEVAL OFF} 29 | {$DEBUGINFO OFF} 30 | {$EXTENDEDSYNTAX ON} 31 | {$IMPORTEDDATA ON} 32 | {$IOCHECKS ON} 33 | {$LOCALSYMBOLS ON} 34 | {$LONGSTRINGS ON} 35 | {$OPENSTRINGS ON} 36 | {$OPTIMIZATION OFF} 37 | {$OVERFLOWCHECKS OFF} 38 | {$RANGECHECKS OFF} 39 | {$REFERENCEINFO ON} 40 | {$SAFEDIVIDE OFF} 41 | {$STACKFRAMES ON} 42 | {$TYPEDADDRESS OFF} 43 | {$VARSTRINGCHECKS ON} 44 | {$WRITEABLECONST OFF} 45 | {$MINENUMSIZE 1} 46 | {$IMAGEBASE $400000} 47 | {$DEFINE DEBUG} 48 | {$ENDIF IMPLICITBUILDING} 49 | {$DESCRIPTION 'Delphi OpenAPI 3 Library'} 50 | {$LIBSUFFIX AUTO} 51 | {$RUNONLY} 52 | {$IMPLICITBUILD OFF} 53 | 54 | requires 55 | rtl, 56 | Neon; 57 | 58 | contains 59 | OpenAPI.Core.Exceptions in '..\..\Source\OpenAPI.Core.Exceptions.pas', 60 | OpenAPI.Core.Interfaces in '..\..\Source\OpenAPI.Core.Interfaces.pas', 61 | OpenAPI.Model.Any in '..\..\Source\OpenAPI.Model.Any.pas', 62 | OpenAPI.Model.Base in '..\..\Source\OpenAPI.Model.Base.pas', 63 | OpenAPI.Model.Classes in '..\..\Source\OpenAPI.Model.Classes.pas', 64 | OpenAPI.Model.Expressions in '..\..\Source\OpenAPI.Model.Expressions.pas', 65 | OpenAPI.Model.JsonPointer in '..\..\Source\OpenAPI.Model.JsonPointer.pas', 66 | OpenAPI.Model.Reference in '..\..\Source\OpenAPI.Model.Reference.pas', 67 | OpenAPI.Model.Schema in '..\..\Source\OpenAPI.Model.Schema.pas', 68 | OpenAPI.Neon.Serializers in '..\..\Source\OpenAPI.Neon.Serializers.pas'; 69 | 70 | end. 71 | -------------------------------------------------------------------------------- /Packages/11.0Alexandria/OpenAPI.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paolo-rossi/OpenAPI-Delphi/02c7fbeb4256e1606fbbc5b39e95629c59423df3/Packages/11.0Alexandria/OpenAPI.res -------------------------------------------------------------------------------- /Packages/12.0Athens/OpenAPI.dpk: -------------------------------------------------------------------------------- 1 | {******************************************************************************} 2 | { } 3 | { Delphi OpenAPI 3.0 Generator } 4 | { Copyright (c) 2018-2023 Paolo Rossi } 5 | { https://github.com/paolo-rossi/delphi-openapi } 6 | { } 7 | {******************************************************************************} 8 | { } 9 | { Licensed under the Apache License, Version 2.0 (the 'License"); } 10 | { you may not use this file except in compliance with the License. } 11 | { You may obtain a copy of the License at } 12 | { } 13 | { http://www.apache.org/licenses/LICENSE-2.0 } 14 | { } 15 | { Unless required by applicable law or agreed to in writing, software } 16 | { distributed under the License is distributed on an "AS IS" BASIS, } 17 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } 18 | { See the License for the specific language governing permissions and } 19 | { limitations under the License. } 20 | { } 21 | {******************************************************************************} 22 | package OpenAPI; 23 | 24 | {$R *.res} 25 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} 26 | {$ALIGN 8} 27 | {$ASSERTIONS ON} 28 | {$BOOLEVAL OFF} 29 | {$DEBUGINFO OFF} 30 | {$EXTENDEDSYNTAX ON} 31 | {$IMPORTEDDATA ON} 32 | {$IOCHECKS ON} 33 | {$LOCALSYMBOLS ON} 34 | {$LONGSTRINGS ON} 35 | {$OPENSTRINGS ON} 36 | {$OPTIMIZATION OFF} 37 | {$OVERFLOWCHECKS OFF} 38 | {$RANGECHECKS OFF} 39 | {$REFERENCEINFO ON} 40 | {$SAFEDIVIDE OFF} 41 | {$STACKFRAMES ON} 42 | {$TYPEDADDRESS OFF} 43 | {$VARSTRINGCHECKS ON} 44 | {$WRITEABLECONST OFF} 45 | {$MINENUMSIZE 1} 46 | {$IMAGEBASE $400000} 47 | {$DEFINE DEBUG} 48 | {$ENDIF IMPLICITBUILDING} 49 | {$DESCRIPTION 'Delphi OpenAPI 3 Library'} 50 | {$LIBSUFFIX AUTO} 51 | {$RUNONLY} 52 | {$IMPLICITBUILD OFF} 53 | 54 | requires 55 | rtl, 56 | Neon; 57 | 58 | contains 59 | OpenAPI.Core.Exceptions in '..\..\Source\OpenAPI.Core.Exceptions.pas', 60 | OpenAPI.Core.Interfaces in '..\..\Source\OpenAPI.Core.Interfaces.pas', 61 | OpenAPI.Model.Any in '..\..\Source\OpenAPI.Model.Any.pas', 62 | OpenAPI.Model.Base in '..\..\Source\OpenAPI.Model.Base.pas', 63 | OpenAPI.Model.Classes in '..\..\Source\OpenAPI.Model.Classes.pas', 64 | OpenAPI.Model.Expressions in '..\..\Source\OpenAPI.Model.Expressions.pas', 65 | OpenAPI.Model.JsonPointer in '..\..\Source\OpenAPI.Model.JsonPointer.pas', 66 | OpenAPI.Model.Reference in '..\..\Source\OpenAPI.Model.Reference.pas', 67 | OpenAPI.Model.Schema in '..\..\Source\OpenAPI.Model.Schema.pas', 68 | OpenAPI.Neon.Serializers in '..\..\Source\OpenAPI.Neon.Serializers.pas'; 69 | 70 | end. 71 | -------------------------------------------------------------------------------- /Packages/12.0Athens/OpenAPI.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paolo-rossi/OpenAPI-Delphi/02c7fbeb4256e1606fbbc5b39e95629c59423df3/Packages/12.0Athens/OpenAPI.res -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenAPI for Delphi - OpenAPI 3.0 for Delphi 2 | 3 |
4 | 5 |

6 | OpenAPI Delphi Library 7 |

8 | 9 | ## What is OpenAPI-Delphi 10 | 11 | **OpenAPI-Delphi** is an OpenAPI 3.0 library for [Delphi](https://www.embarcadero.com/products/delphi) that helps you to generate (and load) OpenAPI 3.0 documentation (in JSON) starting from plain Delphi classes. Delphi-OpenAPI uses the [Neon](https://github.com/paolo-rossi/delphi-neon) serialization library to transform the OpenAPI models from Delphi classes to JSON and to load a OpenAPI document into a Delphi (OpenAPI) object. Please take a look at the Demo to see OpenAPI-Delphi in action. 12 | 13 | ## General Features 14 | 15 | - OpenAPI document generation (JSON) from a Delphi (OpenAPI) object 16 | - OpenAPI loading and parsing into a Delphi (OpenAPI) object (:star2: new in 2.0) 17 | - Use plain Delphi classes to set the OpenAPI specification sections & fields 18 | - Support for JSON Schema (the OpenAPI version) 19 | - Support for Schema field recursion (:star2: new in 2.0) 20 | - Full Support for enum of any type (:star2: new in 2.0) 21 | - Use 1-line code (using the [Neon](https://github.com/paolo-rossi/delphi-neon) library) to transform from and to JSON documents 22 | 23 | ## Delphi Compatibility 24 | This library has been tested with **Delphi 12 Athens**, **Delphi 11 Alexandria**, **Delphi 10.4 Sydney**, **Delphi 10.3 Rio**, **Delphi 10.2 Tokyo**. 25 | 26 | 27 | ## Todo 28 | - Full validation for the OpenAPI models 29 | 30 | -------------------------------------------------------------------------------- /Source/OpenAPI.Core.Exceptions.pas: -------------------------------------------------------------------------------- 1 | {******************************************************************************} 2 | { } 3 | { Delphi OpenAPI 3.0 Generator } 4 | { Copyright (c) 2018-2021 Paolo Rossi } 5 | { https://github.com/paolo-rossi/delphi-openapi } 6 | { } 7 | {******************************************************************************} 8 | { } 9 | { Licensed under the Apache License, Version 2.0 (the 'License"); } 10 | { you may not use this file except in compliance with the License. } 11 | { You may obtain a copy of the License at } 12 | { } 13 | { http://www.apache.org/licenses/LICENSE-2.0 } 14 | { } 15 | { Unless required by applicable law or agreed to in writing, software } 16 | { distributed under the License is distributed on an "AS IS" BASIS, } 17 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } 18 | { See the License for the specific language governing permissions and } 19 | { limitations under the License. } 20 | { } 21 | {******************************************************************************} 22 | unit OpenAPI.Core.Exceptions; 23 | 24 | interface 25 | 26 | uses 27 | System.SysUtils, System.Classes; 28 | 29 | type 30 | EOpenAPIException = class(Exception); 31 | 32 | implementation 33 | 34 | end. 35 | -------------------------------------------------------------------------------- /Source/OpenAPI.Core.Interfaces.pas: -------------------------------------------------------------------------------- 1 | {******************************************************************************} 2 | { } 3 | { Delphi OpenAPI 3.0 Generator } 4 | { Copyright (c) 2018-2021 Paolo Rossi } 5 | { https://github.com/paolo-rossi/delphi-openapi } 6 | { } 7 | {******************************************************************************} 8 | { } 9 | { Licensed under the Apache License, Version 2.0 (the 'License"); } 10 | { you may not use this file except in compliance with the License. } 11 | { You may obtain a copy of the License at } 12 | { } 13 | { http://www.apache.org/licenses/LICENSE-2.0 } 14 | { } 15 | { Unless required by applicable law or agreed to in writing, software } 16 | { distributed under the License is distributed on an "AS IS" BASIS, } 17 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } 18 | { See the License for the specific language governing permissions and } 19 | { limitations under the License. } 20 | { } 21 | {******************************************************************************} 22 | unit OpenAPI.Core.Interfaces; 23 | 24 | interface 25 | 26 | implementation 27 | 28 | type 29 | IOpenAPIElement = interface 30 | ['{F7230DE3-B52E-4A2A-8A32-FF409E4ADD49}'] 31 | end; 32 | 33 | IOpenAPICheckable = interface 34 | ['{0B22E054-370D-46AF-BB53-3A8827EE6907}'] 35 | function CheckModel: Boolean; 36 | end; 37 | 38 | end. 39 | -------------------------------------------------------------------------------- /Source/OpenAPI.Model.Any.pas: -------------------------------------------------------------------------------- 1 | {******************************************************************************} 2 | { } 3 | { Delphi OpenAPI 3.0 Generator } 4 | { Copyright (c) 2018-2023 Paolo Rossi } 5 | { https://github.com/paolo-rossi/delphi-openapi } 6 | { } 7 | {******************************************************************************} 8 | { } 9 | { Licensed under the Apache License, Version 2.0 (the 'License"); } 10 | { you may not use this file except in compliance with the License. } 11 | { You may obtain a copy of the License at } 12 | { } 13 | { http://www.apache.org/licenses/LICENSE-2.0 } 14 | { } 15 | { Unless required by applicable law or agreed to in writing, software } 16 | { distributed under the License is distributed on an "AS IS" BASIS, } 17 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } 18 | { See the License for the specific language governing permissions and } 19 | { limitations under the License. } 20 | { } 21 | {******************************************************************************} 22 | unit OpenAPI.Model.Any; 23 | 24 | interface 25 | 26 | uses 27 | System.Rtti, 28 | 29 | Neon.Core.Attributes, 30 | Neon.Core.Nullables, 31 | OpenAPI.Model.Base; 32 | 33 | {$SCOPEDENUMS ON} 34 | 35 | type 36 | 37 | {$REGION 'Enum Types'} 38 | /// 39 | /// Type of an 40 | /// 41 | TAny = ( 42 | TypePrimitive, 43 | TypeNull, 44 | TypeArray, 45 | TypeObject 46 | ); 47 | 48 | /// 49 | /// Primitive type. 50 | /// 51 | TPrimitive = ( 52 | TypeInteger, 53 | TypeLong, 54 | TypeFloat, 55 | TypeDouble, 56 | TypeString, 57 | TypeBytes, 58 | TypeBinary, 59 | TypeBoolean, 60 | TypeDate, 61 | TypeDateTime, 62 | TypePassword 63 | ); 64 | 65 | {$ENDREGION} 66 | 67 | 68 | /// 69 | /// Represents an Open API element. 70 | /// 71 | IOpenApiElement = interface 72 | ['{F7230DE3-B52E-4A2A-8A32-FF409E4ADD49}'] 73 | end; 74 | 75 | /// 76 | /// Base interface for all the types that represent Open API Any. 77 | /// 78 | IOpenApiAny = interface(IOpenApiElement) 79 | ['{AB82F994-F48C-4D24-89A7-7EDC2854ED62}'] 80 | /// 81 | /// Type of an . 82 | /// 83 | function GetAny: TAny; 84 | property Any: TAny read GetAny; 85 | end; 86 | 87 | /// 88 | /// Base interface for the Primitive type. 89 | /// 90 | IOpenApiPrimitive = interface(IOpenApiAny) 91 | ['{193D437C-682E-457D-B24F-C1C15EDFAD67}'] 92 | /// 93 | /// Primitive type. 94 | /// 95 | function GetPrimitive: TPrimitive; 96 | property Primitive: TPrimitive read GetPrimitive; 97 | end; 98 | 99 | /// 100 | /// Open API primitive class. 101 | /// 102 | /// 103 | TOpenAPIAnyValue = class(TInterfacedObject, IOpenApiPrimitive) 104 | private 105 | FValue: T; 106 | function GetAny: TAny; 107 | protected 108 | function GetPrimitive: TPrimitive; virtual; abstract; 109 | public 110 | /// 111 | /// Initializes the class with the given value. 112 | /// 113 | /// 114 | constructor Create(const AValue: T); 115 | 116 | /// 117 | /// The kind of . 118 | /// 119 | property Any: TAny read GetAny; 120 | 121 | /// 122 | /// The primitive class this object represents. 123 | /// 124 | property PrimitiveType: TPrimitive read GetPrimitive; 125 | 126 | /// 127 | /// Value of this 128 | /// 129 | property Value: T read FValue; 130 | end; 131 | 132 | /// 133 | /// Open API boolean. 134 | /// 135 | TOpenAPIInteger = class(TOpenAPIAnyValue) 136 | protected 137 | /// 138 | /// Primitive type this object represents. 139 | /// 140 | function GetPrimitive: TPrimitive; override; 141 | public 142 | /// 143 | /// Initializes the class. 144 | /// 145 | /// 146 | constructor Create(const AValue: Integer); 147 | end; 148 | 149 | /// 150 | /// Open API boolean. 151 | /// 152 | TOpenAPIBoolean = class(TOpenAPIAnyValue) 153 | protected 154 | /// 155 | /// Primitive type this object represents. 156 | /// 157 | function GetPrimitive: TPrimitive; override; 158 | public 159 | /// 160 | /// Initializes the class. 161 | /// 162 | /// 163 | constructor Create(const AValue: Boolean); 164 | end; 165 | 166 | /// 167 | /// Open API boolean. 168 | /// 169 | TOpenAPIString = class(TOpenAPIAnyValue) 170 | protected 171 | /// 172 | /// Primitive type this object represents. 173 | /// 174 | function GetPrimitive: TPrimitive; override; 175 | public 176 | /// 177 | /// Initializes the class. 178 | /// 179 | /// 180 | constructor Create(const AValue: string); 181 | end; 182 | 183 | /// 184 | /// Class for representing... any value 185 | /// 186 | TOpenAPIAny = class 187 | private 188 | FValue: TValue; 189 | public 190 | procedure ValueFrom(const Value: T); 191 | 192 | [NeonInclude(IncludeIf.NotEmpty)] 193 | property Value: TValue read FValue write FValue; 194 | end; 195 | 196 | TOpenAPIAnyValue = TValue; 197 | TOpenAPIAnyValues = TOpenAPIList; 198 | 199 | implementation 200 | 201 | { TOpenAPIAnyValue } 202 | 203 | constructor TOpenAPIAnyValue.Create(const AValue: T); 204 | begin 205 | FValue := AValue; 206 | end; 207 | 208 | function TOpenAPIAnyValue.GetAny: TAny; 209 | begin 210 | Result := TAny.TypePrimitive; 211 | end; 212 | 213 | { TOpenAPIBoolean } 214 | 215 | constructor TOpenAPIBoolean.Create(const AValue: Boolean); 216 | begin 217 | inherited Create(AValue); 218 | end; 219 | 220 | function TOpenAPIBoolean.GetPrimitive: TPrimitive; 221 | begin 222 | Result := TPrimitive.TypeBoolean; 223 | end; 224 | 225 | { TOpenAPIString } 226 | 227 | constructor TOpenAPIString.Create(const AValue: string); 228 | begin 229 | FValue := AValue; 230 | end; 231 | 232 | function TOpenAPIString.GetPrimitive: TPrimitive; 233 | begin 234 | Result := TPrimitive.TypeString; 235 | end; 236 | 237 | { TOpenAPIInteger } 238 | 239 | constructor TOpenAPIInteger.Create(const AValue: Integer); 240 | begin 241 | FValue := AValue; 242 | end; 243 | 244 | function TOpenAPIInteger.GetPrimitive: TPrimitive; 245 | begin 246 | Result := TPrimitive.TypeInteger; 247 | end; 248 | 249 | { TOpenAPIAny } 250 | 251 | procedure TOpenAPIAny.ValueFrom(const Value: T); 252 | begin 253 | FValue := TValue.From(Value); 254 | end; 255 | 256 | end. 257 | -------------------------------------------------------------------------------- /Source/OpenAPI.Model.Base.pas: -------------------------------------------------------------------------------- 1 | {******************************************************************************} 2 | { } 3 | { Delphi OpenAPI 3.0 Generator } 4 | { Copyright (c) 2018-2023 Paolo Rossi } 5 | { https://github.com/paolo-rossi/delphi-openapi } 6 | { } 7 | {******************************************************************************} 8 | { } 9 | { Licensed under the Apache License, Version 2.0 (the 'License"); } 10 | { you may not use this file except in compliance with the License. } 11 | { You may obtain a copy of the License at } 12 | { } 13 | { http://www.apache.org/licenses/LICENSE-2.0 } 14 | { } 15 | { Unless required by applicable law or agreed to in writing, software } 16 | { distributed under the License is distributed on an "AS IS" BASIS, } 17 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } 18 | { See the License for the specific language governing permissions and } 19 | { limitations under the License. } 20 | { } 21 | {******************************************************************************} 22 | unit OpenAPI.Model.Base; 23 | 24 | interface 25 | 26 | uses 27 | System.SysUtils, System.Classes, System.Rtti, System.Generics.Collections, 28 | System.JSON, 29 | 30 | Neon.Core.Types, 31 | Neon.Core.Nullables, 32 | Neon.Core.Attributes, 33 | OpenAPI.Core.Exceptions, 34 | OpenAPI.Model.Reference; 35 | 36 | type 37 | /// 38 | /// Base class for OpenAPI model classes 39 | /// 40 | TOpenAPIModel = class 41 | protected 42 | FSubObjects: TObjectList; 43 | function InternalCheckModel: Boolean; virtual; 44 | public 45 | constructor Create; 46 | destructor Destroy; override; 47 | function CreateSubObject: T; 48 | function AddSubObject(Value: T): T; 49 | function RemoveSubObject(const Value: T): Integer; 50 | public 51 | function CheckModel: Boolean; inline; 52 | end; 53 | 54 | /// 55 | /// Class for OpenAPI Extensions. Extensions are custom properties 56 | /// and they can be used to describe extra functionality that is not 57 | /// covered by the standard OpenAPI Specification. 58 | /// 59 | TOpenAPIExtensions = class(TOpenAPIModel) 60 | private 61 | FValues: TJSONObject; 62 | procedure CheckName(const AName: string); 63 | public 64 | constructor Create; 65 | 66 | procedure Add(const AName: string; const AValue: NullString); overload; 67 | procedure Add(const AName: string; const AValue: NullDouble); overload; 68 | procedure Add(const AName: string; const AValue: NullInteger); overload; 69 | procedure Add(const AName: string; const AValue: NullBoolean); overload; 70 | procedure Add(const AName: string; const AValue: TJSONValue; AOWned: Boolean = True); overload; 71 | public 72 | [NeonUnwrapped] [NeonInclude(IncludeIf.NotEmpty)] 73 | property Values: TJSONObject read FValues write FValues; 74 | end; 75 | 76 | /// 77 | /// Class for a property extension 78 | /// 79 | /// 80 | /// Deprecated, use TOpenAPIExtensions 81 | /// 82 | TOpenAPIExtension = class(TObjectDictionary) 83 | public 84 | constructor Create; 85 | end; 86 | 87 | /// 88 | /// Base class for OpenAPI classes that are Extensible ( OpenAPIInfo, 89 | /// OpenAPIPaths, OpenAPIPathItem, OpenAPIOperation, OpenAPIParameter, 90 | /// OpenAPIResponses, OpenAPITag, OpenAPISecurityScheme) 91 | /// 92 | TOpenAPIExtensible = class(TOpenAPIModel) 93 | protected 94 | FExtensions: TOpenAPIExtensions; 95 | public 96 | constructor Create; 97 | 98 | /// 99 | /// This object MAY be extended with Specification Extensions. 100 | /// 101 | [NeonUnwrapped] [NeonInclude(IncludeIf.NotEmpty)] 102 | property Extensions: TOpenAPIExtensions read FExtensions write FExtensions; 103 | end; 104 | 105 | /// 106 | /// Class for the Reference Object 107 | /// 108 | TOpenAPIModelReference = class(TOpenAPIExtensible) 109 | protected 110 | FUnresolvedReference: NullBoolean; 111 | FReference: TOpenAPIReference; 112 | public 113 | constructor Create; 114 | 115 | function IsReference: Boolean; 116 | 117 | /// 118 | /// Indicates object is a placeholder reference to an actual object and does not contain valid data. 119 | /// 120 | [NeonIgnore] 121 | property UnresolvedReference: NullBoolean read FUnresolvedReference write FUnresolvedReference; 122 | 123 | /// 124 | /// Reference object. 125 | /// 126 | [NeonProperty('$ref')][NeonInclude(IncludeIf.NotEmpty)] 127 | property Reference: TOpenAPIReference read FReference write FReference; 128 | end; 129 | 130 | 131 | /// 132 | /// Base class for the value-based lists 133 | /// 134 | TOpenAPIList = class(TList) 135 | public 136 | function IsEmpty: Boolean; 137 | end; 138 | 139 | /// 140 | /// Base class for the OpenAPI lists 141 | /// 142 | TOpenAPIModelList = class(TObjectList) 143 | public 144 | constructor Create; 145 | function IsEmpty: Boolean; 146 | end; 147 | 148 | /// 149 | /// Value-owned Map 150 | /// 151 | TOpenAPIOwnedMap = class(TObjectDictionary) 152 | public 153 | constructor Create; 154 | end; 155 | 156 | /// 157 | /// Base class for the OpenAPI Maps 158 | /// 159 | TOpenAPIModelMap = class(TOpenAPIOwnedMap) 160 | public 161 | function IsEmpty: Boolean; 162 | end; 163 | 164 | /// 165 | /// Base class for the OpenAPI maps extensible with the Extension 166 | /// Specifications 167 | /// 168 | TOpenAPIModelExtensibleMap = class(TOpenAPIModelMap) 169 | private 170 | FExtensions: TOpenAPIExtensions; 171 | public 172 | constructor Create; 173 | destructor Destroy; override; 174 | 175 | /// 176 | /// This object MAY be extended with Specification Extensions. 177 | /// 178 | [NeonUnwrapped] [NeonInclude(IncludeIf.NotEmpty)] 179 | property Extensions: TOpenAPIExtensions read FExtensions write FExtensions; 180 | end; 181 | 182 | implementation 183 | 184 | { TOpenAPIModel } 185 | 186 | function TOpenAPIModel.AddSubObject(Value: T): T; 187 | begin 188 | FSubObjects.Add(Value); 189 | Result := Value; 190 | end; 191 | 192 | function TOpenAPIModel.CheckModel: Boolean; 193 | begin 194 | Result := InternalCheckModel; 195 | end; 196 | 197 | constructor TOpenAPIModel.Create; 198 | begin 199 | FSubObjects := TObjectList.Create(True); 200 | end; 201 | 202 | function TOpenAPIModel.CreateSubObject: T; 203 | begin 204 | Result := T.Create; 205 | FSubObjects.Add(Result); 206 | end; 207 | 208 | destructor TOpenAPIModel.Destroy; 209 | begin 210 | FSubObjects.Free; 211 | inherited; 212 | end; 213 | 214 | function TOpenAPIModel.InternalCheckModel: Boolean; 215 | begin 216 | Result := True; 217 | end; 218 | 219 | function TOpenAPIModel.RemoveSubObject(const Value: T): Integer; 220 | begin 221 | Result := FSubObjects.Remove(Value); 222 | end; 223 | 224 | { TOpenAPIExtension } 225 | 226 | constructor TOpenAPIExtension.Create; 227 | begin 228 | inherited Create(); 229 | end; 230 | 231 | procedure TOpenAPIExtensions.Add(const AName: string; const AValue: NullString); 232 | begin 233 | CheckName(AName); 234 | FValues.AddPair(AName, AValue.Value); 235 | end; 236 | 237 | procedure TOpenAPIExtensions.Add(const AName: string; const AValue: NullDouble); 238 | begin 239 | CheckName(AName); 240 | FValues.AddPair(AName, TJSONNumber.Create(AValue.Value)); 241 | end; 242 | 243 | procedure TOpenAPIExtensions.Add(const AName: string; const AValue: NullInteger); 244 | begin 245 | CheckName(AName); 246 | FValues.AddPair(AName, TJSONNumber.Create(AValue.Value)); 247 | end; 248 | 249 | procedure TOpenAPIExtensions.Add(const AName: string; const AValue: NullBoolean); 250 | begin 251 | CheckName(AName); 252 | FValues.AddPair(AName, TJSONBool.Create(AValue.Value)); 253 | end; 254 | 255 | procedure TOpenAPIExtensions.CheckName(const AName: string); 256 | begin 257 | if not AName.StartsWith('x-') then 258 | raise EOpenAPIException.Create('An extension must start with "x-"'); 259 | end; 260 | 261 | constructor TOpenAPIExtensions.Create; 262 | begin 263 | inherited Create; 264 | FValues := CreateSubObject; 265 | end; 266 | 267 | procedure TOpenAPIExtensions.Add(const AName: string; const AValue: TJSONValue; AOWned: Boolean); 268 | begin 269 | CheckName(AName); 270 | if AOWned then 271 | FValues.AddPair(AName, AValue) 272 | else 273 | FValues.AddPair(AName, AValue.Clone as TJSONValue); 274 | end; 275 | 276 | { TOpenAPIExtensible } 277 | 278 | constructor TOpenAPIExtensible.Create; 279 | begin 280 | inherited Create; 281 | FExtensions := CreateSubObject; 282 | end; 283 | 284 | { TOpenAPIModelReference } 285 | 286 | constructor TOpenAPIModelReference.Create; 287 | begin 288 | inherited Create; 289 | FReference := CreateSubObject; 290 | end; 291 | 292 | function TOpenAPIModelReference.IsReference: Boolean; 293 | begin 294 | Result := not FReference.Ref.IsEmpty; 295 | end; 296 | 297 | { TOpenAPIModelExtensibleMap } 298 | 299 | constructor TOpenAPIModelExtensibleMap.Create; 300 | begin 301 | inherited Create; 302 | FExtensions := TOpenAPIExtensions.Create; 303 | end; 304 | 305 | destructor TOpenAPIModelExtensibleMap.Destroy; 306 | begin 307 | FExtensions.Free; 308 | inherited; 309 | end; 310 | 311 | { TOpenAPIModelList } 312 | 313 | constructor TOpenAPIModelList.Create; 314 | begin 315 | inherited Create(True); 316 | end; 317 | 318 | function TOpenAPIModelList.IsEmpty: Boolean; 319 | begin 320 | Result := Count = 0; 321 | end; 322 | 323 | { TOpenAPIOwnedMap } 324 | 325 | constructor TOpenAPIOwnedMap.Create; 326 | begin 327 | inherited Create([doOwnsValues]); 328 | end; 329 | 330 | { TOpenAPIModelMap } 331 | 332 | function TOpenAPIModelMap.IsEmpty: Boolean; 333 | begin 334 | Result := Count = 0; 335 | end; 336 | 337 | { TOpenAPIList } 338 | 339 | function TOpenAPIList.IsEmpty: Boolean; 340 | begin 341 | Result := Count = 0; 342 | end; 343 | 344 | end. 345 | -------------------------------------------------------------------------------- /Source/OpenAPI.Model.Expressions.pas: -------------------------------------------------------------------------------- 1 | {******************************************************************************} 2 | { } 3 | { Delphi OpenAPI 3.0 Generator } 4 | { Copyright (c) 2018-2019 Paolo Rossi } 5 | { https://github.com/paolo-rossi/delphi-openapi } 6 | { } 7 | {******************************************************************************} 8 | { } 9 | { Licensed under the Apache License, Version 2.0 (the "License"); } 10 | { you may not use this file except in compliance with the License. } 11 | { You may obtain a copy of the License at } 12 | { } 13 | { http://www.apache.org/licenses/LICENSE-2.0 } 14 | { } 15 | { Unless required by applicable law or agreed to in writing, software } 16 | { distributed under the License is distributed on an "AS IS" BASIS, } 17 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } 18 | { See the License for the specific language governing permissions and } 19 | { limitations under the License. } 20 | { } 21 | {******************************************************************************} 22 | unit OpenAPI.Model.Expressions; 23 | 24 | interface 25 | 26 | uses 27 | System.SysUtils, System.Classes, System.Generics.Collections, System.RegularExpressions, 28 | OpenAPI.Model.JsonPointer, 29 | OpenAPI.Model.Reference; 30 | 31 | type 32 | /// 33 | /// Base class for the Open API runtime expression. 34 | /// 35 | TRuntimeExpression = class abstract 36 | public const 37 | PREFIX = '$'; 38 | protected 39 | FExpression: string; 40 | function GetExpression: string; virtual; abstract; 41 | public 42 | class function Build(const AExpression: string): TRuntimeExpression; virtual; 43 | 44 | function ToString: string; override; 45 | procedure FromString(const AValue: string); 46 | 47 | property Expression: string read GetExpression; 48 | end; 49 | 50 | TRuntimeExpressions = class(TObjectList) 51 | end; 52 | 53 | TRuntimeExpressionMap = class(TObjectDictionary) 54 | end; 55 | 56 | 57 | /// 58 | /// String literal with embedded expressions 59 | /// 60 | TCompositeExpression = class(TRuntimeExpression) 61 | private const 62 | REGEX_PATTERN = '@"{(?\$[^}]*)"'; 63 | private 64 | FTemplate: string; 65 | FExpressionPattern: TRegEx; 66 | FContainedExpressions: TObjectList; 67 | protected 68 | function GetExpression: string; override; 69 | public 70 | /// 71 | /// Create a composite expression from a string literal with an embedded expression 72 | /// 73 | /// 74 | constructor Create(const AExpression: string); 75 | destructor Destroy; override; 76 | 77 | /// 78 | /// Expressions embedded into string literal 79 | /// 80 | property ContainedExpressions: TObjectList read FContainedExpressions write FContainedExpressions; 81 | end; 82 | 83 | /// 84 | /// Source expression. 85 | /// 86 | TSourceExpression = class(TRuntimeExpression) 87 | protected 88 | /// 89 | /// Gets the expression string. 90 | /// 91 | FValue: string; 92 | 93 | /// 94 | /// Initializes a new instance of the class. 95 | /// 96 | /// The value string. 97 | constructor Create(const AValue: string); 98 | public 99 | /// 100 | /// Build the source expression from input string. 101 | /// 102 | /// The source expression. 103 | /// The built source expression. 104 | class function Build(const AExpression: string): TSourceExpression; reintroduce; 105 | end; 106 | 107 | /// 108 | /// URL expression. 109 | /// 110 | TURLExpression = class sealed(TRuntimeExpression) 111 | public 112 | /// 113 | /// $url string. 114 | /// 115 | const URL = '$url'; 116 | public 117 | /// 118 | /// Gets the expression string. 119 | /// 120 | function GetExpression: string; override; 121 | end; 122 | 123 | /// 124 | /// Method expression. 125 | /// 126 | TMethodExpression = class sealed(TRuntimeExpression) 127 | public 128 | /// 129 | /// $method. string 130 | /// 131 | const METHOD = '$method'; 132 | public 133 | /// 134 | /// Gets the expression string. 135 | /// 136 | function GetExpression: string; override; 137 | end; 138 | 139 | /// 140 | /// StatusCode expression. 141 | /// 142 | TStatusCodeExpression = class sealed(TRuntimeExpression) 143 | public 144 | /// 145 | /// $statusCode. string 146 | /// 147 | const STATUSCODE = '$statusCode'; 148 | public 149 | /// 150 | /// Gets the expression string. 151 | /// 152 | function GetExpression: string; override; 153 | end; 154 | 155 | /// 156 | /// Request expression. 157 | /// 158 | TRequestExpression = class sealed(TRuntimeExpression) 159 | public 160 | /// 161 | /// $request. string 162 | /// 163 | const REQUEST = '$request.'; 164 | private 165 | FSource: TSourceExpression; 166 | function GetSource: TSourceExpression; 167 | public 168 | /// 169 | /// Initializes a new instance of the class. 170 | /// 171 | /// The source of the request. 172 | constructor Create(ASource: TSourceExpression); 173 | 174 | /// 175 | /// Gets the expression string. 176 | /// 177 | function GetExpression: string; override; 178 | 179 | /// 180 | /// The expression. 181 | /// 182 | property Source: TSourceExpression read GetSource; 183 | end; 184 | 185 | TResponseExpression = class(TRuntimeExpression) 186 | /// 187 | /// $response. string 188 | /// 189 | public const RESPONSE = '$response.'; 190 | private 191 | FSource: TSourceExpression; 192 | public 193 | /// 194 | /// Create a new instance of the class. 195 | /// 196 | /// The source of the response. 197 | constructor Create(ASource: TSourceExpression); 198 | 199 | /// 200 | /// Gets the expression string. 201 | /// 202 | function GetExpression: string; override; 203 | //public override string Expression => Response + Source.Expression; 204 | 205 | /// 206 | /// The expression. 207 | /// 208 | property Source: TSourceExpression read FSource; 209 | end; 210 | 211 | /// 212 | /// Body expression. 213 | /// 214 | TBodyExpression = class sealed(TSourceExpression) 215 | public 216 | /// 217 | /// body string 218 | /// 219 | const BODY = 'body'; 220 | 221 | /// 222 | /// Prefix for a pointer 223 | /// 224 | const POINTER_PREFIX = '#'; 225 | private 226 | function GetFragment: string; 227 | protected 228 | /// 229 | /// Gets the expression string. 230 | /// 231 | function GetExpression: string; override; 232 | public 233 | /// 234 | /// Initializes a new instance of the class. 235 | /// 236 | /// a JSON Pointer [RFC 6901](https://tools.ietf.org/html/rfc6901). 237 | constructor Create(APointer: TJsonPointer); 238 | 239 | /// 240 | /// Gets the fragment string. 241 | /// 242 | property Fragment: string read GetFragment; 243 | end; 244 | 245 | /// 246 | /// Header expression, The token identifier in header is case-insensitive. 247 | /// 248 | THeaderExpression = class(TSourceExpression) 249 | /// 250 | /// header. string 251 | /// 252 | public const HEADER = 'header.'; 253 | private 254 | function GetToken: string; 255 | protected 256 | /// 257 | /// Gets the expression string. 258 | /// 259 | function GetExpression: string; override; 260 | public 261 | /// 262 | /// Initializes a new instance of the class. 263 | /// 264 | /// The token string, it's case-insensitive. 265 | constructor Create(const AToken: string); 266 | { 267 | if (string.IsNullOrWhiteSpace(token)) 268 | throw Error.ArgumentNullOrWhiteSpace(nameof(token)); 269 | } 270 | 271 | /// 272 | /// Gets the expression string. 273 | /// 274 | //public override string Expression 275 | { 276 | return Header + Value; 277 | } 278 | 279 | /// 280 | /// Gets the token string. 281 | /// 282 | property Token: string read GetToken; 283 | { 284 | return Value; 285 | } 286 | end; 287 | 288 | implementation 289 | 290 | uses 291 | OpenAPI.Core.Exceptions; 292 | 293 | { TRuntimeExpression } 294 | 295 | class function TRuntimeExpression.Build(const AExpression: string): TRuntimeExpression; 296 | var 297 | LSubString: string; 298 | LSource: TSourceExpression; 299 | begin 300 | if AExpression.IsEmpty then 301 | raise Exception.Create('Expression empty'); 302 | 303 | if not AExpression.StartsWith(PREFIX) then 304 | Exit(TCompositeExpression.Create(AExpression)); 305 | 306 | // $url 307 | if AExpression = TURLExpression.URL then 308 | Exit(TURLExpression.Create); 309 | 310 | // $method 311 | if AExpression = TMethodExpression.METHOD then 312 | Exit(TMethodExpression.Create); 313 | 314 | // $statusCode 315 | if AExpression = TStatusCodeExpression.STATUSCODE then 316 | Exit(TStatusCodeExpression.Create); 317 | 318 | // $request. 319 | if AExpression.StartsWith(TRequestExpression.REQUEST) then 320 | begin 321 | LSubString := AExpression.Substring(TRequestExpression.REQUEST.Length); 322 | LSource := TSourceExpression.Build(LSubString); 323 | Exit(TRequestExpression(LSource)); 324 | end; 325 | 326 | // $response. 327 | if AExpression.StartsWith(TResponseExpression.RESPONSE) then 328 | begin 329 | LSubString := AExpression.Substring(TResponseExpression.RESPONSE.Length); 330 | LSource := TSourceExpression.Build(LSubString); 331 | Exit(TResponseExpression(LSource)); 332 | end; 333 | 334 | raise EOpenAPIException.Create(Format('Runtime Expression Has Invalid Format: %s', [AExpression])); 335 | end; 336 | 337 | procedure TRuntimeExpression.FromString(const AValue: string); 338 | begin 339 | FExpression := AValue; 340 | end; 341 | 342 | function TRuntimeExpression.ToString: string; 343 | begin 344 | Result := Expression; 345 | end; 346 | 347 | { TCompositeExpression } 348 | 349 | constructor TCompositeExpression.Create(const AExpression: string); 350 | var 351 | LValue: string; 352 | LMatch: TMatch; 353 | LMatches: TMatchCollection; 354 | begin 355 | FContainedExpressions := TObjectList.Create(True); 356 | 357 | FTemplate := AExpression; 358 | 359 | // Extract subexpressions and convert to RuntimeExpressions 360 | LMatches := FExpressionPattern.Matches(AExpression); 361 | 362 | for LMatch in LMatches do 363 | begin 364 | if LMatch.Success then 365 | begin 366 | LValue := LMatch.Groups['exp'].Value; 367 | FContainedExpressions.Add(TRuntimeExpression.Build(LValue)); 368 | end; 369 | end; 370 | end; 371 | 372 | destructor TCompositeExpression.Destroy; 373 | begin 374 | FContainedExpressions.Free; 375 | inherited; 376 | end; 377 | 378 | function TCompositeExpression.GetExpression: string; 379 | begin 380 | Result := FTemplate; 381 | end; 382 | 383 | { TSourceExpression } 384 | 385 | class function TSourceExpression.Build(const AExpression: string): TSourceExpression; 386 | var 387 | LSubString: string; 388 | LExpressions: TArray; 389 | begin 390 | { TODO -opaolo -c : to finish 31/03/2019 11:15:25 } 391 | if not string.IsNullOrWhiteSpace(AExpression) then 392 | begin 393 | LExpressions := AExpression.Split(['.']); 394 | if Length(LExpressions) = 2 then 395 | begin 396 | if AExpression.StartsWith(THeaderExpression.HEADER) then 397 | // header. 398 | Exit(THeaderExpression(LExpressions[1])); 399 | { 400 | if AExpression.StartsWith(TQueryExpression.QUERY) then 401 | // query. 402 | Exit(TQueryExpression(LExpressions[1])); 403 | 404 | if AExpression.StartsWith(TPathExpression.PATH) then 405 | // path. 406 | Exit(PathExpression(LExpressions[1])); 407 | } 408 | end; 409 | 410 | // body 411 | if AExpression.StartsWith(TBodyExpression.BODY) then 412 | begin 413 | LSubString := AExpression.Substring(Length(TBodyExpression.BODY)); 414 | if string.IsNullOrEmpty(LSubString) then 415 | Exit(TBodyExpression.Create(nil)); 416 | 417 | Exit(TBodyExpression.Create(TJsonPointer.Create(LSubString))); 418 | end; 419 | end; 420 | raise EOpenAPIException.Create('Source Expression invalid format'); 421 | end; 422 | 423 | constructor TSourceExpression.Create(const AValue: string); 424 | begin 425 | FValue := AValue; 426 | end; 427 | 428 | { TURLExpression } 429 | 430 | function TURLExpression.GetExpression: string; 431 | begin 432 | Result := URL; 433 | end; 434 | 435 | { TRequestExpression } 436 | 437 | constructor TRequestExpression.Create(ASource: TSourceExpression); 438 | begin 439 | Assert(ASource <> nil); 440 | FSource := ASource; 441 | end; 442 | 443 | function TRequestExpression.GetExpression: string; 444 | begin 445 | Result := REQUEST + Source.Expression; 446 | end; 447 | 448 | function TRequestExpression.GetSource: TSourceExpression; 449 | begin 450 | Result := FSource; 451 | end; 452 | 453 | { TMethodExpression } 454 | 455 | function TMethodExpression.GetExpression: string; 456 | begin 457 | Result := METHOD; 458 | end; 459 | 460 | { TStatusCodeExpression } 461 | 462 | function TStatusCodeExpression.GetExpression: string; 463 | begin 464 | Result := STATUSCODE; 465 | end; 466 | 467 | { TBodyExpression } 468 | 469 | constructor TBodyExpression.Create(APointer: TJsonPointer); 470 | begin 471 | // : base(pointer?.ToString()) 472 | if not Assigned(APointer) then 473 | raise EArgumentNilException.Create('JsonPointer is null'); 474 | 475 | inherited Create(APointer.ToString); 476 | end; 477 | 478 | function TBodyExpression.GetExpression: string; 479 | begin 480 | if string.IsNullOrWhiteSpace(FValue) then 481 | Exit(BODY); 482 | 483 | Result := BODY + POINTER_PREFIX + FValue; 484 | end; 485 | 486 | function TBodyExpression.GetFragment: string; 487 | begin 488 | Result := FValue; 489 | end; 490 | 491 | { TResponseExpression } 492 | 493 | constructor TResponseExpression.Create(ASource: TSourceExpression); 494 | begin 495 | FSource := ASource; 496 | end; 497 | 498 | function TResponseExpression.GetExpression: string; 499 | begin 500 | Result := RESPONSE + Source.Expression; 501 | end; 502 | 503 | { THeaderExpression } 504 | 505 | constructor THeaderExpression.Create(const AToken: string); 506 | begin 507 | if string.IsNullOrWhiteSpace(AToken) then 508 | raise Exception.Create('Argument null or whitespace'); 509 | 510 | inherited Create(AToken); 511 | end; 512 | 513 | function THeaderExpression.GetExpression: string; 514 | begin 515 | Result := HEADER + FValue; 516 | end; 517 | 518 | function THeaderExpression.GetToken: string; 519 | begin 520 | Result := FValue; 521 | end; 522 | 523 | end. 524 | 525 | -------------------------------------------------------------------------------- /Source/OpenAPI.Model.JsonPointer.pas: -------------------------------------------------------------------------------- 1 | {******************************************************************************} 2 | { } 3 | { Delphi OpenAPI 3.0 Generator } 4 | { Copyright (c) 2018-2019 Paolo Rossi } 5 | { https://github.com/paolo-rossi/delphi-openapi } 6 | { } 7 | {******************************************************************************} 8 | { } 9 | { Licensed under the Apache License, Version 2.0 (the "License"); } 10 | { you may not use this file except in compliance with the License. } 11 | { You may obtain a copy of the License at } 12 | { } 13 | { http://www.apache.org/licenses/LICENSE-2.0 } 14 | { } 15 | { Unless required by applicable law or agreed to in writing, software } 16 | { distributed under the License is distributed on an "AS IS" BASIS, } 17 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } 18 | { See the License for the specific language governing permissions and } 19 | { limitations under the License. } 20 | { } 21 | {******************************************************************************} 22 | unit OpenAPI.Model.JsonPointer; 23 | 24 | interface 25 | 26 | uses 27 | System.SysUtils, System.Classes, System.Generics.Collections, System.NetEncoding; 28 | 29 | type 30 | /// 31 | /// JSON pointer expression 32 | /// 33 | TJSONPointer = class 34 | private 35 | FTokens: TArray; 36 | public 37 | /// 38 | /// Initializes the class. 39 | /// 40 | /// Pointer as string. 41 | constructor Create(APointer: string); overload; 42 | constructor Create(ATokens: TArray); overload; 43 | 44 | /// 45 | /// Decode the string. 46 | /// 47 | function Decode(const AToken: string): string; 48 | 49 | /// 50 | /// Gets the parent pointer. 51 | /// 52 | function ParentPointer: TJsonPointer; 53 | 54 | /// 55 | /// Gets the string representation of this JSON pointer. 56 | /// 57 | function ToString: string; override; 58 | 59 | /// 60 | /// Tokens. 61 | /// 62 | property Tokens: TArray read FTokens; 63 | end; 64 | 65 | implementation 66 | 67 | { TJSONPointer } 68 | 69 | constructor TJSONPointer.Create(APointer: string); 70 | var 71 | LArr: TArray; 72 | LIndex: Integer; 73 | begin 74 | LArr := APointer.Split(['/']); 75 | 76 | FTokens := []; 77 | for LIndex := Low(LArr) to High(LArr) do 78 | begin 79 | if LIndex = Low(LArr) then 80 | Continue; 81 | FTokens := FTokens + [LArr[LIndex]]; 82 | end; 83 | 84 | for LIndex := Low(FTokens) to High(FTokens) do 85 | FTokens[LIndex] := Decode(FTokens[LIndex]); 86 | end; 87 | 88 | constructor TJSONPointer.Create(ATokens: TArray); 89 | begin 90 | FTokens := ATokens; 91 | end; 92 | 93 | function TJSONPointer.Decode(const AToken: string): string; 94 | begin 95 | Result := TNetEncoding.URL.Decode(AToken).Replace('~1', '/').Replace('~0', '~'); 96 | end; 97 | 98 | function TJSONPointer.ParentPointer: TJsonPointer; 99 | var 100 | LParent: TArray; 101 | LIndex: Integer; 102 | begin 103 | if Length(FTokens) = 0 then 104 | Result := nil 105 | else 106 | begin 107 | for LIndex := Low(FTokens) to High(FTokens) - 1 do 108 | LParent := LParent + [FTokens[LIndex]]; 109 | 110 | Result := TJSONPointer.Create(LParent); 111 | end; 112 | end; 113 | 114 | function TJSONPointer.ToString: string; 115 | begin 116 | Result := '/' + string.join('/', FTokens); 117 | end; 118 | 119 | end. 120 | 121 | -------------------------------------------------------------------------------- /Source/OpenAPI.Model.Reference.pas: -------------------------------------------------------------------------------- 1 | {******************************************************************************} 2 | { } 3 | { Delphi OpenAPI 3.0 Generator } 4 | { Copyright (c) 2018-2019 Paolo Rossi } 5 | { https://github.com/paolo-rossi/delphi-openapi } 6 | { } 7 | {******************************************************************************} 8 | { } 9 | { Licensed under the Apache License, Version 2.0 (the "License"); } 10 | { you may not use this file except in compliance with the License. } 11 | { You may obtain a copy of the License at } 12 | { } 13 | { http://www.apache.org/licenses/LICENSE-2.0 } 14 | { } 15 | { Unless required by applicable law or agreed to in writing, software } 16 | { distributed under the License is distributed on an "AS IS" BASIS, } 17 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } 18 | { See the License for the specific language governing permissions and } 19 | { limitations under the License. } 20 | { } 21 | {******************************************************************************} 22 | unit OpenAPI.Model.Reference; 23 | 24 | interface 25 | 26 | uses 27 | System.SysUtils, System.Classes, System.Generics.Collections, System.JSON, 28 | 29 | Neon.Core.Attributes; 30 | 31 | type 32 | /// 33 | /// A simple object to allow referencing other components in the specification, internally and externally. 34 | /// 35 | TOpenAPIReference = class 36 | private 37 | FId: string; 38 | FRef: string; 39 | FRefType: string; 40 | FExternalResource: string; 41 | public 42 | /// 43 | /// Gets a flag indicating whether this reference is an external reference. 44 | /// 45 | function IsExternal: Boolean; 46 | 47 | /// 48 | /// Gets a flag indicating whether this reference is a local reference. 49 | /// 50 | function IsLocal: Boolean; 51 | public 52 | /// 53 | /// The identifier of the reusable component of one particular ReferenceType. 54 | /// If ExternalResource is present, this is the path to the component after the '#/'. 55 | /// For example, if the reference is 'example.json#/path/to/component', the Id is 'path/to/component'. 56 | /// If ExternalResource is not present, this is the name of the component without the reference type name. 57 | /// For example, if the reference is '#/components/schemas/componentName', the Id is 'componentName'. 58 | /// 59 | [NeonIgnore] 60 | property Id: string read FId write FId; 61 | 62 | /// 63 | /// The element type referenced. 64 | /// 65 | /// This must be present if is not present. 66 | [NeonIgnore] 67 | property RefType: string read FRefType write FRefType; 68 | 69 | /// 70 | /// External resource in the reference. 71 | /// It maybe: 72 | /// 1. a absolute/relative file path, for example: ../commons/pet.json 73 | /// 2. a Url, for example: http://localhost/pet.json 74 | /// 75 | [NeonIgnore] 76 | property ExternalResource: string read FExternalResource write FExternalResource; 77 | 78 | [NeonProperty('$ref')][NeonInclude(IncludeIf.NotEmpty)] 79 | property Ref: string read FRef write FRef; 80 | end; 81 | 82 | implementation 83 | 84 | { TOpenAPIReference } 85 | 86 | function TOpenAPIReference.IsExternal: Boolean; 87 | begin 88 | { TODO -opaolo -c : to finish 31/03/2019 18:47:51 } 89 | Result := not FExternalResource.IsEmpty; 90 | end; 91 | 92 | function TOpenAPIReference.IsLocal: Boolean; 93 | begin 94 | { TODO -opaolo -c : to finish 31/03/2019 18:47:51 } 95 | Result := FExternalResource.IsEmpty; 96 | end; 97 | 98 | end. 99 | -------------------------------------------------------------------------------- /Source/OpenAPI.Model.Schema.pas: -------------------------------------------------------------------------------- 1 | {******************************************************************************} 2 | { } 3 | { Delphi OpenAPI 3.0 Generator } 4 | { Copyright (c) 2018-2023 Paolo Rossi } 5 | { https://github.com/paolo-rossi/delphi-openapi } 6 | { } 7 | {******************************************************************************} 8 | { } 9 | { Licensed under the Apache License, Version 2.0 (the "License"); } 10 | { you may not use this file except in compliance with the License. } 11 | { You may obtain a copy of the License at } 12 | { } 13 | { http://www.apache.org/licenses/LICENSE-2.0 } 14 | { } 15 | { Unless required by applicable law or agreed to in writing, software } 16 | { distributed under the License is distributed on an "AS IS" BASIS, } 17 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } 18 | { See the License for the specific language governing permissions and } 19 | { limitations under the License. } 20 | { } 21 | {******************************************************************************} 22 | unit OpenAPI.Model.Schema; 23 | 24 | interface 25 | 26 | uses 27 | System.SysUtils, System.Classes, System.Generics.Collections, 28 | System.JSON, System.Rtti, System.TypInfo, 29 | 30 | Neon.Core.Types, 31 | Neon.Core.Attributes, 32 | Neon.Core.Nullables, 33 | Neon.Core.Persistence, 34 | Neon.Core.Persistence.JSON.Schema, 35 | 36 | OpenAPI.Model.Any, 37 | OpenAPI.Model.Base, 38 | OpenAPI.Model.Reference; 39 | 40 | type 41 | TOpenAPISchema = class; 42 | 43 | TOpenAPIDiscriminator = class 44 | private 45 | FPropertyName: NullString; 46 | FMapping: TDictionary; 47 | public 48 | constructor Create; 49 | destructor Destroy; override; 50 | 51 | /// 52 | /// REQUIRED. The name of the property in the payload that will hold the discriminator value. 53 | /// 54 | property PropertyName: NullString read FPropertyName write FPropertyName; 55 | 56 | /// 57 | /// An object to hold mappings between payload values and schema names or references. 58 | /// 59 | [NeonInclude(IncludeIf.NotEmpty)] 60 | property Mapping: TDictionary read FMapping write FMapping; 61 | end; 62 | 63 | TOpenAPISchemaBase = class 64 | protected 65 | FType_: NullString; 66 | FTitle: NullString; 67 | FDescription: NullString; 68 | FFormat: NullString; 69 | public 70 | /// 71 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 72 | /// While relying on JSON Schema's defined formats, 73 | /// the OAS offers a few additional predefined formats. 74 | /// 75 | property Format: NullString read FFormat write FFormat; 76 | 77 | /// 78 | /// Follow JSON Schema definition. Short text providing information about the data. 79 | /// 80 | property Title: NullString read FTitle write FTitle; 81 | 82 | /// 83 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 84 | /// Value MUST be a string. Multiple types via an array are not supported. 85 | /// 86 | [NeonProperty('type')] 87 | property Type_: NullString read FType_ write FType_; 88 | 89 | /// 90 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 91 | /// CommonMark syntax MAY be used for rich text representation. 92 | /// 93 | property Description: NullString read FDescription write FDescription; 94 | end; 95 | 96 | TOpenAPISchemas = class(TOpenAPIModelList); 97 | TOpenAPISchemaMap = class(TOpenAPIModelMap); 98 | 99 | TOpenAPIEnum = class(TOpenAPIModelList); 100 | 101 | /// 102 | /// JSON Schema Object: https://json-schema.org/ 103 | /// 104 | TOpenAPISchema = class(TOpenAPIModelReference) 105 | private 106 | FNeonConfig: INeonConfiguration; 107 | FJSONObject: TJSONObject; 108 | FJSONOwned: Boolean; 109 | private 110 | FFormat: NullString; 111 | FTitle: NullString; 112 | FType_: NullString; 113 | FDescription: NullString; 114 | FMaximum: NullDouble; 115 | FExclusiveMaximum: NullBoolean; 116 | FMinimum: NullDouble; 117 | FExclusiveMinimum: NullBoolean; 118 | FMaxLength: NullInteger; 119 | FMinLength: NullInteger; 120 | FPattern: NullString; 121 | FMultipleOf: NullDouble; 122 | FReadOnly_: NullBoolean; 123 | FWriteOnly_: NullBoolean; 124 | FAllOf: TOpenAPISchemas; 125 | FOneOf: TOpenAPISchemas; 126 | FAnyOf: TOpenAPISchemas; 127 | FNot_: TOpenAPISchema; 128 | FRequired: TArray; 129 | FItems: TOpenAPISchema; 130 | FMaxItems: NullInteger; 131 | FMinItems: NullInteger; 132 | FUniqueItems: NullBoolean; 133 | FProperties: TOpenAPISchemaMap; 134 | FMaxProperties: NullInteger; 135 | FMinProperties: NullInteger; 136 | FAdditionalPropertiesAllowed: NullBoolean; 137 | FAdditionalProperties: TOpenAPISchema; 138 | FNullable: NullBoolean; 139 | FDefault_: TOpenAPIAny; 140 | FEnum: TOpenAPIEnum; 141 | FDiscriminator: TOpenAPIDiscriminator; 142 | private 143 | function GetNeonConfig: INeonConfiguration; 144 | public 145 | constructor Create; 146 | destructor Destroy; override; 147 | public 148 | function WithNeonConfig(AConfig: INeonConfiguration): TOpenAPISchema; 149 | 150 | function AddProperty(const AKeyName: string): TOpenAPISchema; 151 | function AddEnum(const AValue: TValue): TOpenAPIAny; 152 | 153 | procedure SetJSONObject(AJSON: TJSONObject; AOwned: Boolean = True); 154 | 155 | procedure SetJSONFromType(AType: TRttiType); 156 | procedure SetJSONFromClass(AClass: TClass); 157 | 158 | procedure SetSchemaReference(const AReference: string); 159 | function IsEmpty: Boolean; 160 | 161 | [NeonIgnore] 162 | property JSONObject: TJSONObject read FJSONObject write FJSONObject; 163 | public 164 | /// 165 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 166 | /// While relying on JSON Schema's defined formats, 167 | /// the OAS offers a few additional predefined formats. 168 | /// 169 | property Format: NullString read FFormat write FFormat; 170 | 171 | /// 172 | /// Follow JSON Schema definition. Short text providing information about the data. 173 | /// 174 | property Title: NullString read FTitle write FTitle; 175 | 176 | /// 177 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 178 | /// Value MUST be a string. Multiple types via an array are not supported. 179 | /// 180 | [NeonProperty('type')] 181 | property Type_: NullString read FType_ write FType_; 182 | 183 | /// 184 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 185 | /// CommonMark syntax MAY be used for rich text representation. 186 | /// 187 | property Description: NullString read FDescription write FDescription; 188 | 189 | /// 190 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 191 | /// 192 | property Maximum: NullDouble read FMaximum write FMaximum; 193 | 194 | /// 195 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 196 | /// 197 | property ExclusiveMaximum: NullBoolean read FExclusiveMaximum write FExclusiveMaximum; 198 | 199 | /// 200 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 201 | /// 202 | property Minimum: NullDouble read FMinimum write FMinimum; 203 | 204 | /// 205 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 206 | /// 207 | property ExclusiveMinimum: NullBoolean read FExclusiveMinimum write FExclusiveMinimum; 208 | 209 | /// 210 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 211 | /// 212 | property MaxLength: NullInteger read FMaxLength write FMaxLength; 213 | 214 | /// 215 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 216 | /// 217 | property MinLength: NullInteger read FMinLength write FMinLength; 218 | 219 | /// 220 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 221 | /// This string SHOULD be a valid regular expression, according to the ECMA 262 regular expression dialect 222 | /// 223 | property Pattern: NullString read FPattern write FPattern; 224 | 225 | /// 226 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 227 | /// 228 | property MultipleOf: NullDouble read FMultipleOf write FMultipleOf; 229 | 230 | /// 231 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 232 | /// The default value represents what would be assumed by the consumer of the input as the value of the schema if one is not provided. 233 | /// Unlike JSON Schema, the value MUST conform to the defined type for the Schema Object defined at the same level. 234 | /// For example, if type is string, then default can be "foo" but cannot be 1. 235 | /// 236 | [NeonProperty('default')] [NeonInclude(IncludeIf.NotEmpty)] 237 | property Default_: TOpenAPIAny read FDefault_ write FDefault_; 238 | 239 | /// 240 | /// Relevant only for Schema "properties" definitions. Declares the property as "read only". 241 | /// This means that it MAY be sent as part of a response but SHOULD NOT be sent as part of the request. 242 | /// If the property is marked as ReadOnly_ being true and is in the required list, 243 | /// the required will take effect on the response only. 244 | /// A property MUST NOT be marked as both ReadOnly_ and WriteOnly_ being true. 245 | /// Default value is false. 246 | /// 247 | [NeonProperty('readOnly')] 248 | property ReadOnly_: NullBoolean read FReadOnly_ write FReadOnly_; 249 | 250 | /// 251 | /// Relevant only for Schema "properties" definitions. Declares the property as "write only". 252 | /// Therefore, it MAY be sent as part of a request but SHOULD NOT be sent as part of the response. 253 | /// If the property is marked as WriteOnly_ being true and is in the required list, 254 | /// the required will take effect on the request only. 255 | /// A property MUST NOT be marked as both ReadOnly_ and WriteOnly_ being true. 256 | /// Default value is false. 257 | /// 258 | [NeonProperty('writeOnly')] 259 | property WriteOnly_: NullBoolean read FWriteOnly_ write FWriteOnly_; 260 | 261 | /// 262 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 263 | /// Inline or referenced schema MUST be of a Schema Object and not a standard JSON Schema. 264 | /// 265 | [NeonInclude(IncludeIf.NotEmpty)] 266 | property AllOf: TOpenAPISchemas read FAllOf write FAllOf; 267 | 268 | /// 269 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 270 | /// Inline or referenced schema MUST be of a Schema Object and not a standard JSON Schema. 271 | /// 272 | [NeonInclude(IncludeIf.NotEmpty)] 273 | property OneOf: TOpenAPISchemas read FOneOf write FOneOf; 274 | 275 | /// 276 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 277 | /// Inline or referenced schema MUST be of a Schema Object and not a standard JSON Schema. 278 | /// 279 | [NeonInclude(IncludeIf.NotEmpty)] 280 | property AnyOf: TOpenAPISchemas read FAnyOf write FAnyOf; 281 | 282 | /// 283 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 284 | /// Inline or referenced schema MUST be of a Schema Object and not a standard JSON Schema. 285 | /// 286 | [NeonInclude(IncludeIf.NotNull)] 287 | [NeonProperty('not')] 288 | [NeonAutoCreate] 289 | property Not_: TOpenAPISchema read FNot_ write FNot_; 290 | 291 | /// 292 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 293 | /// 294 | [NeonInclude(IncludeIf.NotEmpty)] 295 | property Required: TArray read FRequired write FRequired; 296 | 297 | /// 298 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 299 | /// Value MUST be an object and not an array. Inline or referenced schema MUST be of a Schema Object 300 | /// and not a standard JSON Schema. items MUST be present if the type is array. 301 | /// 302 | [NeonInclude(IncludeIf.NotNull)] 303 | [NeonAutoCreate] 304 | property Items: TOpenAPISchema read FItems write FItems; 305 | 306 | /// 307 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 308 | /// 309 | property MaxItems: NullInteger read FMaxItems write FMaxItems; 310 | 311 | /// 312 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 313 | /// 314 | property MinItems: NullInteger read FMinItems write FMinItems; 315 | 316 | /// 317 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 318 | /// 319 | property UniqueItems: NullBoolean read FUniqueItems write FUniqueItems; 320 | 321 | /// 322 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 323 | /// Property definitions MUST be a Schema Object and not a standard JSON Schema (inline or referenced). 324 | /// 325 | [NeonInclude(IncludeIf.NotEmpty)] 326 | property Properties: TOpenAPISchemaMap read FProperties write FProperties; 327 | 328 | /// 329 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 330 | /// 331 | property MaxProperties: NullInteger read FMaxProperties write FMaxProperties; 332 | 333 | /// 334 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 335 | /// 336 | property MinProperties: NullInteger read FMinProperties write FMinProperties; 337 | 338 | /// 339 | /// Indicates if the schema can contain properties other than those defined by the properties map. 340 | /// 341 | property AdditionalPropertiesAllowed: NullBoolean read FAdditionalPropertiesAllowed write FAdditionalPropertiesAllowed; 342 | 343 | /// 344 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 345 | /// Value can be boolean or object. Inline or referenced schema 346 | /// MUST be of a Schema Object and not a standard JSON Schema. 347 | /// 348 | [NeonInclude(IncludeIf.NotNull)] 349 | [NeonAutoCreate] 350 | property AdditionalProperties: TOpenAPISchema read FAdditionalProperties write FAdditionalProperties; 351 | 352 | /// 353 | /// Adds support for polymorphism. The discriminator is an object name that is used to differentiate 354 | /// between other schemas which may satisfy the payload description. 355 | /// 356 | [NeonInclude(IncludeIf.NotEmpty)] 357 | property Discriminator: TOpenAPIDiscriminator read FDiscriminator write FDiscriminator; 358 | 359 | /// 360 | /// A free-form property to include an example of an instance for this schema. 361 | /// To represent examples that cannot be naturally represented in JSON or YAML, 362 | /// a string value can be used to contain the example with escaping where necessary. 363 | /// 364 | //property Example IOpenApiAny 365 | 366 | /// 367 | /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 368 | /// 369 | [NeonInclude(IncludeIf.NotEmpty)] 370 | //property Enum: TOpenAPIAny read FEnum write FEnum; 371 | property Enum: TOpenAPIEnum read FEnum write FEnum; 372 | 373 | /// 374 | /// Allows sending a null value for the defined schema. Default value is false. 375 | /// 376 | property Nullable: NullBoolean read FNullable write FNullable; 377 | 378 | /// 379 | /// Additional external documentation for this schema. 380 | /// 381 | //ExternalDocs: TOpenApiExternalDocs; 382 | end; 383 | 384 | implementation 385 | 386 | { TOpenAPIDiscriminator } 387 | 388 | constructor TOpenAPIDiscriminator.Create; 389 | begin 390 | FMapping := TDictionary.Create; 391 | end; 392 | 393 | destructor TOpenAPIDiscriminator.Destroy; 394 | begin 395 | FMapping.Free; 396 | inherited; 397 | end; 398 | 399 | { TOpenAPISchema } 400 | 401 | function TOpenAPISchema.AddEnum(const AValue: TValue): TOpenAPIAny; 402 | begin 403 | Result := TOpenAPIAny.Create; 404 | Result.Value := AValue; 405 | FEnum.Add(Result); 406 | end; 407 | 408 | function TOpenAPISchema.AddProperty(const AKeyName: string): TOpenAPISchema; 409 | begin 410 | if not FProperties.TryGetValue(AKeyName, Result) then 411 | begin 412 | Result := TOpenAPISchema.Create; 413 | FProperties.Add(AKeyName, Result); 414 | end; 415 | end; 416 | 417 | constructor TOpenAPISchema.Create; 418 | begin 419 | inherited Create; 420 | 421 | FAllOf := CreateSubObject; 422 | FOneOf := CreateSubObject; 423 | FAnyOf := CreateSubObject; 424 | //FNot_ := CreateSubObject; 425 | //FItems := CreateSubObject; 426 | FProperties := CreateSubObject; 427 | FDiscriminator := CreateSubObject; 428 | //FAdditionalProperties := CreateSubObject; 429 | FDefault_ := CreateSubObject; 430 | FEnum := CreateSubObject; 431 | end; 432 | 433 | destructor TOpenAPISchema.Destroy; 434 | begin 435 | // You need to destroy these in case Neon creates them 436 | FNot_.Free; 437 | FItems.Free; 438 | FAdditionalProperties.Free; 439 | if FJSONOwned then 440 | FJSONObject.Free; 441 | 442 | inherited; 443 | end; 444 | 445 | function TOpenAPISchema.GetNeonConfig: INeonConfiguration; 446 | begin 447 | if not Assigned(FNeonConfig) then 448 | FNeonConfig := TNeonConfiguration.Camel; 449 | Result := FNeonConfig; 450 | end; 451 | 452 | function TOpenAPISchema.IsEmpty: Boolean; 453 | begin 454 | Result := not Assigned(FJSONObject) and FType_.IsNull and FTitle.IsNull and FFormat.IsNull and 455 | FAllOf.IsEmpty and FAnyOf.IsEmpty and FOneOf.IsEmpty and FProperties.IsEmpty and 456 | not IsReference(); 457 | end; 458 | 459 | procedure TOpenAPISchema.SetJSONObject(AJSON: TJSONObject; AOwned: Boolean); 460 | begin 461 | if Assigned(FJSONObject) and FJSONOwned then 462 | FJSONObject.Free; 463 | 464 | FJSONObject := AJSON; 465 | FJSONOwned := AOwned; 466 | end; 467 | 468 | procedure TOpenAPISchema.SetJSONFromClass(AClass: TClass); 469 | begin 470 | SetJSONObject(TNeonSchemaGenerator.ClassToJSONSchema(AClass, GetNeonConfig)); 471 | end; 472 | 473 | procedure TOpenAPISchema.SetJSONFromType(AType: TRttiType); 474 | begin 475 | SetJSONObject(TNeonSchemaGenerator.TypeToJSONSchema(AType, GetNeonConfig)); 476 | end; 477 | 478 | procedure TOpenAPISchema.SetSchemaReference(const AReference: string); 479 | begin 480 | Reference.Ref := '#/components/schemas/' + AReference; 481 | end; 482 | 483 | function TOpenAPISchema.WithNeonConfig(AConfig: INeonConfiguration): TOpenAPISchema; 484 | begin 485 | FNeonConfig := AConfig; 486 | Result := Self; 487 | end; 488 | 489 | end. 490 | -------------------------------------------------------------------------------- /Source/OpenAPI.Neon.Serializers.pas: -------------------------------------------------------------------------------- 1 | {******************************************************************************} 2 | { } 3 | { Delphi OpenAPI 3.0 Generator } 4 | { Copyright (c) 2018-2023 Paolo Rossi } 5 | { https://github.com/paolo-rossi/delphi-openapi } 6 | { } 7 | {******************************************************************************} 8 | { } 9 | { Licensed under the Apache License, Version 2.0 (the 'License"); } 10 | { you may not use this file except in compliance with the License. } 11 | { You may obtain a copy of the License at } 12 | { } 13 | { http://www.apache.org/licenses/LICENSE-2.0 } 14 | { } 15 | { Unless required by applicable law or agreed to in writing, software } 16 | { distributed under the License is distributed on an "AS IS" BASIS, } 17 | { WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. } 18 | { See the License for the specific language governing permissions and } 19 | { limitations under the License. } 20 | { } 21 | {******************************************************************************} 22 | unit OpenAPI.Neon.Serializers; 23 | 24 | interface 25 | 26 | uses 27 | System.SysUtils, System.Generics.Defaults, System.Rtti, System.TypInfo, System.JSON, 28 | 29 | Neon.Core.Attributes, 30 | Neon.Core.Persistence, 31 | Neon.Core.Types, 32 | Neon.Core.Nullables, 33 | Neon.Core.Serializers.RTL, 34 | 35 | OpenAPI.Model.Any, 36 | OpenAPI.Model.Base, 37 | OpenAPI.Model.Classes, 38 | OpenAPI.Model.Reference, 39 | OpenAPI.Model.Schema; 40 | 41 | type 42 | TOpenAPISerializer = class 43 | class function GetNeonConfig: INeonConfiguration; static; 44 | end; 45 | 46 | TNullableStringSerializer = class(TCustomSerializer) 47 | protected 48 | class function GetTargetInfo: PTypeInfo; override; 49 | class function CanHandle(AType: PTypeInfo): Boolean; override; 50 | public 51 | function Serialize(const AValue: TValue; ANeonObject: TNeonRttiObject; AContext: ISerializerContext): TJSONValue; override; 52 | function Deserialize(AValue: TJSONValue; const AData: TValue; ANeonObject: TNeonRttiObject; AContext: IDeserializerContext): TValue; override; 53 | end; 54 | 55 | TNullableBooleanSerializer = class(TCustomSerializer) 56 | protected 57 | class function GetTargetInfo: PTypeInfo; override; 58 | class function CanHandle(AType: PTypeInfo): Boolean; override; 59 | public 60 | function Serialize(const AValue: TValue; ANeonObject: TNeonRttiObject; AContext: ISerializerContext): TJSONValue; override; 61 | function Deserialize(AValue: TJSONValue; const AData: TValue; ANeonObject: TNeonRttiObject; AContext: IDeserializerContext): TValue; override; 62 | end; 63 | 64 | TNullableIntegerSerializer = class(TCustomSerializer) 65 | protected 66 | class function GetTargetInfo: PTypeInfo; override; 67 | class function CanHandle(AType: PTypeInfo): Boolean; override; 68 | public 69 | function Serialize(const AValue: TValue; ANeonObject: TNeonRttiObject; AContext: ISerializerContext): TJSONValue; override; 70 | function Deserialize(AValue: TJSONValue; const AData: TValue; ANeonObject: TNeonRttiObject; AContext: IDeserializerContext): TValue; override; 71 | end; 72 | 73 | TNullableInt64Serializer = class(TCustomSerializer) 74 | protected 75 | class function GetTargetInfo: PTypeInfo; override; 76 | class function CanHandle(AType: PTypeInfo): Boolean; override; 77 | public 78 | function Serialize(const AValue: TValue; ANeonObject: TNeonRttiObject; AContext: ISerializerContext): TJSONValue; override; 79 | function Deserialize(AValue: TJSONValue; const AData: TValue; ANeonObject: TNeonRttiObject; AContext: IDeserializerContext): TValue; override; 80 | end; 81 | 82 | TNullableDoubleSerializer = class(TCustomSerializer) 83 | protected 84 | class function GetTargetInfo: PTypeInfo; override; 85 | class function CanHandle(AType: PTypeInfo): Boolean; override; 86 | public 87 | function Serialize(const AValue: TValue; ANeonObject: TNeonRttiObject; AContext: ISerializerContext): TJSONValue; override; 88 | function Deserialize(AValue: TJSONValue; const AData: TValue; ANeonObject: TNeonRttiObject; AContext: IDeserializerContext): TValue; override; 89 | end; 90 | 91 | TNullableTDateTimeSerializer = class(TCustomSerializer) 92 | protected 93 | class function GetTargetInfo: PTypeInfo; override; 94 | class function CanHandle(AType: PTypeInfo): Boolean; override; 95 | public 96 | function Serialize(const AValue: TValue; ANeonObject: TNeonRttiObject; AContext: ISerializerContext): TJSONValue; override; 97 | function Deserialize(AValue: TJSONValue; const AData: TValue; ANeonObject: TNeonRttiObject; AContext: IDeserializerContext): TValue; override; 98 | end; 99 | 100 | TOpenAPIAnySerializer = class(TCustomSerializer) 101 | protected 102 | class function GetTargetInfo: PTypeInfo; override; 103 | class function CanHandle(AType: PTypeInfo): Boolean; override; 104 | public 105 | function Serialize(const AValue: TValue; ANeonObject: TNeonRttiObject; AContext: ISerializerContext): TJSONValue; override; 106 | function Deserialize(AValue: TJSONValue; const AData: TValue; ANeonObject: TNeonRttiObject; AContext: IDeserializerContext): TValue; override; 107 | end; 108 | 109 | TOpenAPIReferenceSerializer = class(TCustomSerializer) 110 | protected 111 | class function GetTargetInfo: PTypeInfo; override; 112 | class function CanHandle(AType: PTypeInfo): Boolean; override; 113 | public 114 | function Serialize(const AValue: TValue; ANeonObject: TNeonRttiObject; AContext: ISerializerContext): TJSONValue; override; 115 | function Deserialize(AValue: TJSONValue; const AData: TValue; ANeonObject: TNeonRttiObject; AContext: IDeserializerContext): TValue; override; 116 | end; 117 | 118 | TOpenAPIPathItemSerializer = class(TCustomSerializer) 119 | protected 120 | class function GetTargetInfo: PTypeInfo; override; 121 | class function CanHandle(AType: PTypeInfo): Boolean; override; 122 | public 123 | function Serialize(const AValue: TValue; ANeonObject: TNeonRttiObject; AContext: ISerializerContext): TJSONValue; override; 124 | function Deserialize(AValue: TJSONValue; const AData: TValue; ANeonObject: TNeonRttiObject; AContext: IDeserializerContext): TValue; override; 125 | end; 126 | 127 | TOpenAPIExtensionsSerializer = class(TCustomSerializer) 128 | protected 129 | class function GetTargetInfo: PTypeInfo; override; 130 | class function CanHandle(AType: PTypeInfo): Boolean; override; 131 | public 132 | function Serialize(const AValue: TValue; ANeonObject: TNeonRttiObject; AContext: ISerializerContext): TJSONValue; override; 133 | function Deserialize(AValue: TJSONValue; const AData: TValue; ANeonObject: TNeonRttiObject; AContext: IDeserializerContext): TValue; override; 134 | end; 135 | 136 | TOpenAPISchemaSerializer = class(TCustomSerializer) 137 | protected 138 | class function GetTargetInfo: PTypeInfo; override; 139 | class function CanHandle(AType: PTypeInfo): Boolean; override; 140 | public 141 | function Serialize(const AValue: TValue; ANeonObject: TNeonRttiObject; AContext: ISerializerContext): TJSONValue; override; 142 | function Deserialize(AValue: TJSONValue; const AData: TValue; ANeonObject: TNeonRttiObject; AContext: IDeserializerContext): TValue; override; 143 | end; 144 | 145 | procedure RegisterOpenAPISerializers(ARegistry: TNeonSerializerRegistry); 146 | 147 | implementation 148 | 149 | uses 150 | Neon.Core.Utils; 151 | 152 | { TNullableStringSerializer } 153 | 154 | class function TNullableStringSerializer.CanHandle(AType: PTypeInfo): Boolean; 155 | begin 156 | if AType = GetTargetInfo then 157 | Result := True 158 | else 159 | Result := False; 160 | end; 161 | 162 | function TNullableStringSerializer.Deserialize(AValue: TJSONValue; const AData: 163 | TValue; ANeonObject: TNeonRttiObject; AContext: IDeserializerContext): TValue; 164 | var 165 | LNullValue: NullString; 166 | begin 167 | LNullValue := AValue.Value; 168 | Result := TValue.From(LNullValue); 169 | end; 170 | 171 | class function TNullableStringSerializer.GetTargetInfo: PTypeInfo; 172 | begin 173 | Result := TypeInfo(NullString); 174 | end; 175 | 176 | function TNullableStringSerializer.Serialize(const AValue: TValue; ANeonObject: 177 | TNeonRttiObject; AContext: ISerializerContext): TJSONValue; 178 | var 179 | LValue: NullString; 180 | begin 181 | Result := nil; 182 | LValue := AValue.AsType; 183 | if LValue.HasValue then 184 | Result := TJSONString.Create(LValue.Value); 185 | end; 186 | 187 | { TNullableBooleanSerializer } 188 | 189 | class function TNullableBooleanSerializer.CanHandle(AType: PTypeInfo): Boolean; 190 | begin 191 | if AType = GetTargetInfo then 192 | Result := True 193 | else 194 | Result := False; 195 | end; 196 | 197 | function TNullableBooleanSerializer.Deserialize(AValue: TJSONValue; const 198 | AData: TValue; ANeonObject: TNeonRttiObject; AContext: IDeserializerContext): TValue; 199 | var 200 | LNullValue: NullBoolean; 201 | begin 202 | LNullValue := (AValue as TJSONBool).AsBoolean; 203 | Result := TValue.From(LNullValue); 204 | end; 205 | 206 | class function TNullableBooleanSerializer.GetTargetInfo: PTypeInfo; 207 | begin 208 | Result := TypeInfo(NullBoolean); 209 | end; 210 | 211 | function TNullableBooleanSerializer.Serialize(const AValue: TValue; 212 | ANeonObject: TNeonRttiObject; AContext: ISerializerContext): TJSONValue; 213 | var 214 | LValue: NullBoolean; 215 | begin 216 | Result := nil; 217 | LValue := AValue.AsType; 218 | if LValue.HasValue then 219 | Result := TJSONBool.Create(LValue.Value); 220 | end; 221 | 222 | { TNullableIntegerSerializer } 223 | 224 | class function TNullableIntegerSerializer.CanHandle(AType: PTypeInfo): Boolean; 225 | begin 226 | if AType = GetTargetInfo then 227 | Result := True 228 | else 229 | Result := False; 230 | end; 231 | 232 | function TNullableIntegerSerializer.Deserialize(AValue: TJSONValue; const 233 | AData: TValue; ANeonObject: TNeonRttiObject; AContext: IDeserializerContext): TValue; 234 | var 235 | LNullValue: NullInteger; 236 | begin 237 | LNullValue := (AValue as TJSONNumber).AsInt; 238 | Result := TValue.From(LNullValue); 239 | end; 240 | 241 | class function TNullableIntegerSerializer.GetTargetInfo: PTypeInfo; 242 | begin 243 | Result := TypeInfo(NullInteger); 244 | end; 245 | 246 | function TNullableIntegerSerializer.Serialize(const AValue: TValue; 247 | ANeonObject: TNeonRttiObject; AContext: ISerializerContext): TJSONValue; 248 | var 249 | LValue: NullInteger; 250 | begin 251 | Result := nil; 252 | LValue := AValue.AsType; 253 | if LValue.HasValue then 254 | Result := TJSONNumber.Create(LValue.Value); 255 | end; 256 | 257 | { TNullableInt64Serializer } 258 | 259 | class function TNullableInt64Serializer.CanHandle(AType: PTypeInfo): Boolean; 260 | begin 261 | if AType = GetTargetInfo then 262 | Result := True 263 | else 264 | Result := False; 265 | end; 266 | 267 | function TNullableInt64Serializer.Deserialize(AValue: TJSONValue; const AData: TValue; 268 | ANeonObject: TNeonRttiObject; AContext: IDeserializerContext): TValue; 269 | var 270 | LNullValue: NullInt64; 271 | begin 272 | LNullValue := (AValue as TJSONNumber).AsInt64; 273 | Result := TValue.From(LNullValue); 274 | end; 275 | 276 | class function TNullableInt64Serializer.GetTargetInfo: PTypeInfo; 277 | begin 278 | Result := TypeInfo(NullInt64); 279 | end; 280 | 281 | function TNullableInt64Serializer.Serialize(const AValue: TValue; ANeonObject: 282 | TNeonRttiObject; AContext: ISerializerContext): TJSONValue; 283 | var 284 | LValue: NullInt64; 285 | begin 286 | Result := nil; 287 | LValue := AValue.AsType; 288 | if LValue.HasValue then 289 | Result := TJSONNumber.Create(LValue.Value); 290 | end; 291 | 292 | { TNullableDoubleSerializer } 293 | 294 | class function TNullableDoubleSerializer.CanHandle(AType: PTypeInfo): Boolean; 295 | begin 296 | if AType = GetTargetInfo then 297 | Result := True 298 | else 299 | Result := False; 300 | end; 301 | 302 | function TNullableDoubleSerializer.Deserialize(AValue: TJSONValue; const AData: TValue; 303 | ANeonObject: TNeonRttiObject; AContext: IDeserializerContext): TValue; 304 | var 305 | LNullValue: NullDouble; 306 | begin 307 | LNullValue := (AValue as TJSONNumber).AsDouble; 308 | Result := TValue.From(LNullValue); 309 | end; 310 | 311 | class function TNullableDoubleSerializer.GetTargetInfo: PTypeInfo; 312 | begin 313 | Result := TypeInfo(NullDouble); 314 | end; 315 | 316 | function TNullableDoubleSerializer.Serialize(const AValue: TValue; ANeonObject: 317 | TNeonRttiObject; AContext: ISerializerContext): TJSONValue; 318 | var 319 | LValue: NullDouble; 320 | begin 321 | Result := nil; 322 | LValue := AValue.AsType; 323 | if LValue.HasValue then 324 | Result := TJSONNumber.Create(LValue.Value); 325 | end; 326 | 327 | { TNullableTDateTimeSerializer } 328 | 329 | class function TNullableTDateTimeSerializer.CanHandle(AType: PTypeInfo): Boolean; 330 | begin 331 | if AType = GetTargetInfo then 332 | Result := True 333 | else 334 | Result := False; 335 | end; 336 | 337 | function TNullableTDateTimeSerializer.Deserialize(AValue: TJSONValue; const AData: TValue; 338 | ANeonObject: TNeonRttiObject; AContext: IDeserializerContext): TValue; 339 | var 340 | LNullValue: NullDateTime; 341 | begin 342 | LNullValue := TJSONUtils.JSONToDateTime(AValue.Value, AContext.GetConfiguration.GetUseUTCDate); 343 | Result := TValue.From(LNullValue); 344 | end; 345 | 346 | class function TNullableTDateTimeSerializer.GetTargetInfo: PTypeInfo; 347 | begin 348 | Result := TypeInfo(NullDateTime); 349 | end; 350 | 351 | function TNullableTDateTimeSerializer.Serialize(const AValue: TValue; 352 | ANeonObject: TNeonRttiObject; AContext: ISerializerContext): TJSONValue; 353 | var 354 | LValue: NullDateTime; 355 | begin 356 | Result := nil; 357 | LValue := AValue.AsType; 358 | if LValue.HasValue then 359 | Result := TJSONString.Create(TJSONUtils.DateTimeToJSON(LValue.Value, AContext.GetConfiguration.GetUseUTCDate)); 360 | end; 361 | 362 | { TOpenAPISerializer } 363 | 364 | class function TOpenAPISerializer.GetNeonConfig: INeonConfiguration; 365 | begin 366 | Result := TNeonConfiguration.Create; 367 | 368 | Result.SetMemberCase(TNeonCase.CamelCase) 369 | .SetPrettyPrint(True) 370 | .GetSerializers 371 | //Neon Serializers 372 | .RegisterSerializer(TJSONValueSerializer) 373 | //Neon Serializers 374 | .RegisterSerializer(TNullableStringSerializer) 375 | .RegisterSerializer(TNullableBooleanSerializer) 376 | .RegisterSerializer(TNullableIntegerSerializer) 377 | .RegisterSerializer(TNullableInt64Serializer) 378 | .RegisterSerializer(TNullableDoubleSerializer) 379 | .RegisterSerializer(TNullableTDateTimeSerializer) 380 | // OpenAPI Models 381 | .RegisterSerializer(TOpenAPIReferenceSerializer) 382 | .RegisterSerializer(TOpenAPISchemaSerializer) 383 | .RegisterSerializer(TOpenAPIAnySerializer) 384 | .RegisterSerializer(TOpenAPIPathItemSerializer) 385 | .RegisterSerializer(TOpenAPIExtensionsSerializer) 386 | ; 387 | end; 388 | 389 | { TOpenAPIAnySerializer } 390 | 391 | class function TOpenAPIAnySerializer.CanHandle(AType: PTypeInfo): Boolean; 392 | begin 393 | if AType = GetTargetInfo then 394 | Result := True 395 | else 396 | Result := False; 397 | end; 398 | 399 | function TOpenAPIAnySerializer.Deserialize(AValue: TJSONValue; const AData: TValue; 400 | ANeonObject: TNeonRttiObject; AContext: IDeserializerContext): TValue; 401 | var 402 | LAny: TOpenAPIAny; 403 | begin 404 | Result := AData; 405 | LAny := AData.AsObject as TOpenAPIAny; 406 | 407 | if AValue is TJSONNumber then 408 | LAny.ValueFrom((AValue as TJSONNumber).AsDouble) 409 | else if AValue is TJSONString then 410 | LAny.ValueFrom((AValue as TJSONString).Value) 411 | else if AValue is TJSONBool then 412 | LAny.ValueFrom((AValue as TJSONBool).AsBoolean); 413 | end; 414 | 415 | class function TOpenAPIAnySerializer.GetTargetInfo: PTypeInfo; 416 | begin 417 | Result := TypeInfo(TOpenAPIAny); 418 | end; 419 | 420 | function TOpenAPIAnySerializer.Serialize(const AValue: TValue; 421 | ANeonObject: TNeonRttiObject; AContext: ISerializerContext): TJSONValue; 422 | var 423 | LValue: TOpenAPIAny; 424 | begin 425 | LValue := AValue.AsType; 426 | if LValue = nil then 427 | Exit(nil); 428 | 429 | if LValue.Value.IsEmpty then 430 | Exit(nil); 431 | 432 | Result := AContext.WriteDataMember(LValue.Value); 433 | case ANeonObject.NeonInclude.Value of 434 | IncludeIf.NotEmpty, IncludeIf.NotDefault: 435 | begin 436 | if not TJSONUtils.IsNotDefault(Result) then 437 | FreeAndNil(Result); 438 | end; 439 | end; 440 | end; 441 | 442 | { TOpenAPIReferenceSerializer } 443 | 444 | class function TOpenAPIReferenceSerializer.CanHandle(AType: PTypeInfo): Boolean; 445 | begin 446 | Result := TypeInfoIs(AType); 447 | end; 448 | 449 | function TOpenAPIReferenceSerializer.Deserialize(AValue: TJSONValue; const AData: TValue; 450 | ANeonObject: TNeonRttiObject; AContext: IDeserializerContext): TValue; 451 | var 452 | LType: TRttiType; 453 | LRef: TOpenAPIModelReference; 454 | LJSON: TJSONObject; 455 | begin 456 | Result := AData; 457 | LRef := AData.AsObject as TOpenAPIModelReference; 458 | LJSON := AValue as TJSONObject; 459 | 460 | if Assigned(LJSON.Values['$ref']) then 461 | begin 462 | LType := TRttiUtils.Context.GetType(TOpenAPIReference); 463 | AContext.ReadDataMember(AValue, LType, LRef.Reference, False); 464 | end 465 | else 466 | begin 467 | LType := TRttiUtils.Context.GetType(AData.AsObject.ClassType); 468 | AContext.ReadDataMember(AValue, LType, AData, False); 469 | end; 470 | end; 471 | 472 | class function TOpenAPIReferenceSerializer.GetTargetInfo: PTypeInfo; 473 | begin 474 | Result := TOpenAPIModelReference.ClassInfo; 475 | end; 476 | 477 | function TOpenAPIReferenceSerializer.Serialize(const AValue: TValue; 478 | ANeonObject: TNeonRttiObject; AContext: ISerializerContext): TJSONValue; 479 | var 480 | LRefObj: TOpenAPIModelReference; 481 | LType: TRttiType; 482 | begin 483 | LRefObj := AValue.AsType; 484 | if LRefObj = nil then 485 | Exit(nil); 486 | 487 | if Assigned(LRefObj.Reference) and not (LRefObj.Reference.Ref.IsEmpty) then 488 | Exit(TJSONString.Create(LRefObj.Reference.Ref)) 489 | else 490 | begin 491 | LType := TRttiUtils.Context.GetType(AValue.TypeInfo); 492 | Result := TJSONObject.Create; 493 | AContext.WriteMembers(LType, AValue.AsObject, Result); 494 | end; 495 | 496 | case ANeonObject.NeonInclude.Value of 497 | IncludeIf.NotEmpty, IncludeIf.NotDefault: 498 | begin 499 | if (Result as TJSONObject).Count = 0 then 500 | FreeAndNil(Result); 501 | end; 502 | end; 503 | 504 | end; 505 | 506 | { TOpenAPISchemaSerializer } 507 | 508 | class function TOpenAPISchemaSerializer.CanHandle(AType: PTypeInfo): Boolean; 509 | begin 510 | Result := TypeInfoIs(AType); 511 | end; 512 | 513 | function TOpenAPISchemaSerializer.Deserialize(AValue: TJSONValue; 514 | const AData: TValue; ANeonObject: TNeonRttiObject; 515 | AContext: IDeserializerContext): TValue; 516 | var 517 | LType: TRttiType; 518 | LSchema: TOpenAPISchema; 519 | LJSONSchema: TJSONObject; 520 | begin 521 | Result := AData; 522 | 523 | LSchema := Result.AsObject as TOpenAPISchema; 524 | LJSONSchema := AValue as TJSONObject; 525 | 526 | if Assigned(LJSONSchema.Values['$ref']) then 527 | begin 528 | LType := TRttiUtils.Context.GetType(TOpenAPIReference); 529 | AContext.ReadDataMember(AValue, LType, LSchema.Reference, False); 530 | end 531 | else 532 | begin 533 | LType := TRttiUtils.Context.GetType(TOpenAPISchema); 534 | //AContext.ReadDataMember(AValue, LType, Result, True); 535 | AContext.ReadMembers(LType, LSchema, LJSONSchema); 536 | end; 537 | end; 538 | 539 | class function TOpenAPISchemaSerializer.GetTargetInfo: PTypeInfo; 540 | begin 541 | Result := TOpenAPISchema.ClassInfo; 542 | end; 543 | 544 | function TOpenAPISchemaSerializer.Serialize(const AValue: TValue; 545 | ANeonObject: TNeonRttiObject; AContext: ISerializerContext): TJSONValue; 546 | var 547 | LSchema: TOpenAPISchema; 548 | LType: TRttiType; 549 | begin 550 | LSchema := AValue.AsType; 551 | 552 | if LSchema = nil then 553 | Exit(nil); 554 | 555 | if LSchema.IsEmpty then 556 | Exit(nil); 557 | 558 | // The Schema has a reference 559 | if LSchema.IsReference then 560 | Exit(AContext.WriteDataMember(LSchema.Reference, False)); 561 | 562 | if Assigned(LSchema.JSONObject) then 563 | Result := LSchema.JSONObject.Clone as TJSONObject 564 | else 565 | begin 566 | LType := TRttiUtils.Context.GetType(AValue.TypeInfo); 567 | Result := TJSONObject.Create; 568 | AContext.WriteMembers(LType, AValue.AsObject, Result); 569 | end; 570 | 571 | case ANeonObject.NeonInclude.Value of 572 | IncludeIf.NotEmpty, IncludeIf.NotDefault: 573 | begin 574 | if (Result as TJSONObject).Count = 0 then 575 | FreeAndNil(Result); 576 | end; 577 | end; 578 | end; 579 | 580 | { TOpenAPIPathItemSerializer } 581 | 582 | class function TOpenAPIPathItemSerializer.CanHandle(AType: PTypeInfo): Boolean; 583 | begin 584 | Result := TypeInfoIs(AType); 585 | end; 586 | 587 | function TOpenAPIPathItemSerializer.Deserialize(AValue: TJSONValue; 588 | const AData: TValue; ANeonObject: TNeonRttiObject; 589 | AContext: IDeserializerContext): TValue; 590 | var 591 | LPath: TOpenAPIPathItem; 592 | LOperation: TOpenAPIOperation; 593 | LJSONPath: TJSONObject; 594 | LOpType: TOperationType; 595 | LType: TRttiType; 596 | LJSONVal: TJSONValue; 597 | begin 598 | Result := AData; 599 | LPath := AData.AsObject as TOpenAPIPathItem; 600 | LJSONPath := AValue as TJSONObject; 601 | LType := TRttiUtils.Context.GetType(TOpenAPIOperation); 602 | 603 | for LOpType := Low(TOperationType) to High(TOperationType) do 604 | begin 605 | LJSONVal := LJSONPath.Values[LOpType.ToString]; 606 | if Assigned(LJSONVal) then 607 | begin 608 | LOperation := LPath.AddOperation(LOpType); 609 | AContext.ReadMembers(LType, LOperation, LJSONVal as TJSONObject); 610 | end; 611 | end; 612 | end; 613 | 614 | class function TOpenAPIPathItemSerializer.GetTargetInfo: PTypeInfo; 615 | begin 616 | Result := TOpenAPIPathItem.ClassInfo; 617 | end; 618 | 619 | function TOpenAPIPathItemSerializer.Serialize(const AValue: TValue; 620 | ANeonObject: TNeonRttiObject; AContext: ISerializerContext): TJSONValue; 621 | begin 622 | Result := AContext.WriteDataMember(AValue, False); 623 | end; 624 | 625 | { TOpenAPIExtensionsSerializer } 626 | 627 | class function TOpenAPIExtensionsSerializer.CanHandle(AType: PTypeInfo): Boolean; 628 | begin 629 | Result := TypeInfoIs(AType); 630 | end; 631 | 632 | function TOpenAPIExtensionsSerializer.Deserialize(AValue: TJSONValue; 633 | const AData: TValue; ANeonObject: TNeonRttiObject; 634 | AContext: IDeserializerContext): TValue; 635 | var 636 | LExt: TOpenAPIExtensions; 637 | LJSONValues: TJSONObject; 638 | LPair: TJSONPair; 639 | begin 640 | Result := AData; 641 | LExt := AData.AsObject as TOpenAPIExtensions; 642 | LJSONValues := AValue as TJSONObject; 643 | 644 | for LPair in LJSONValues do 645 | begin 646 | if LPair.JsonString.Value.StartsWith('x-') then 647 | LExt.Values.AddPair(LPair.Clone as TJSONPair); 648 | end; 649 | end; 650 | 651 | class function TOpenAPIExtensionsSerializer.GetTargetInfo: PTypeInfo; 652 | begin 653 | Result := TOpenAPIExtensions.ClassInfo; 654 | end; 655 | 656 | function TOpenAPIExtensionsSerializer.Serialize(const AValue: TValue; 657 | ANeonObject: TNeonRttiObject; AContext: ISerializerContext): TJSONValue; 658 | var 659 | LExt: TOpenAPIExtensions; 660 | begin 661 | LExt := AValue.AsType; 662 | 663 | if LExt = nil then 664 | Exit(nil); 665 | 666 | if LExt.Values.Count = 0 then 667 | Exit(nil); 668 | 669 | Result := AContext.WriteDataMember(LExt.Values, True); 670 | end; 671 | 672 | procedure RegisterOpenAPISerializers(ARegistry: TNeonSerializerRegistry); 673 | begin 674 | //Neon Serializers 675 | ARegistry.RegisterSerializer(TJSONValueSerializer); 676 | //ARegistry.RegisterSerializer(TTValueSerializer); 677 | 678 | //Nullable Serializers 679 | ARegistry.RegisterSerializer(TNullableStringSerializer); 680 | ARegistry.RegisterSerializer(TNullableBooleanSerializer); 681 | ARegistry.RegisterSerializer(TNullableIntegerSerializer); 682 | ARegistry.RegisterSerializer(TNullableInt64Serializer); 683 | ARegistry.RegisterSerializer(TNullableDoubleSerializer); 684 | ARegistry.RegisterSerializer(TNullableTDateTimeSerializer); 685 | 686 | //OpenAPI Serializers 687 | ARegistry.RegisterSerializer(TOpenAPIAnySerializer); 688 | ARegistry.RegisterSerializer(TOpenAPIPathItemSerializer); 689 | ARegistry.RegisterSerializer(TOpenAPIReferenceSerializer); 690 | ARegistry.RegisterSerializer(TOpenAPISchemaSerializer); 691 | ARegistry.RegisterSerializer(TOpenAPIExtensionsSerializer); 692 | end; 693 | 694 | end. 695 | -------------------------------------------------------------------------------- /openapi-delphi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paolo-rossi/OpenAPI-Delphi/02c7fbeb4256e1606fbbc5b39e95629c59423df3/openapi-delphi.png --------------------------------------------------------------------------------