diff --git a/.gitignore b/.gitignore index fd760bf..2c3b7cb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ bin/ -examples/normalized/ +examples/converted/ lib/ node_modules/ diff --git a/README.md b/README.md index f45c0c1..d349722 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![Downloads](https://img.shields.io/npm/dm/@samchon/openapi.svg)](https://www.npmjs.com/package/@samchon/openapi) [![Build Status](https://github.com/samchon/openapi/workflows/build/badge.svg)](https://github.com/samchon/openapi/actions?query=workflow%3Abuild) -OpenAPI definitions and converters for [typia](https://github.com/samchon/typia) and [nestia](https://github.com/samchon/nestia). +OpenAPI definitions and converters (for [typia](https://github.com/samchon/typia) and [nestia](https://github.com/samchon/nestia)). `@samchon/openapi` is a collection of OpenAPI definitions of below versions. Those type definitions does not contain every properties of OpenAPI specification, but just have only some features essentially required for `typia` and `nestia` (especially [`@nestia/editor`](https://nestia.io/docs/editor/)). @@ -15,7 +15,7 @@ OpenAPI definitions and converters for [typia](https://github.com/samchon/typia) 2. [OpenAPI v3.0](https://github.com/samchon/openapi/blob/master/src/OpenApiV3.ts) 3. [OpenAPI v3.1](https://github.com/samchon/openapi/blob/master/src/OpenApiV3_1.ts) -Also, `@samchon/openapi` provides emended OpenAPI v3.1 definition and its converter from above versions for convenient development. The keyword "emended" means that [`OpenApi`](https://github.com/samchon/openapi/blob/master/src/OpenApi.ts) is not a direct OpenAPI v3.1 specification (OpenApiV3_1), but a little bit shrinked to remove ambiguous and duplicated expressions of OpenAPI v3.1 for the convenience of typia and nestia +Also, `@samchon/openapi` provides emended OpenAPI v3.1 definition and its converter/inverter from above versions for convenient development. The keyword "emended" means that [`OpenApi`](https://github.com/samchon/openapi/blob/master/src/OpenApi.ts) is not a direct OpenAPI v3.1 specification (OpenApiV3_1), but a little bit shrinked to remove ambiguous and duplicated expressions of OpenAPI v3.1 for the convenience of typia and nestia For example, when representing nullable type, OpenAPI v3.1 supports three ways. In that case, OpenApi remains only the third way, so that makes `typia` and `nestia` (especially [`@nestia/editor`](https://nestia.io/docs/editor/)) to be simple and easy to implement. @@ -55,6 +55,14 @@ const input: // you can convert it to emended OpenAPI v3.1 const output: OpenApi.IDocument = OpenApi.convert(input); + +// it is possible to downgrade to Swagger v2 or OpenAPI v3 +const v2: SwaggerV2 = OpenApi.downgrade(output, "2.0"); +const v3: OpenApiV3 = OpenApi.downgrade(output, "3.0"); + +// you can utilize it like below +OpenApi.downgrade(OpenApi.convert(v2), "3.0"); +OpenApi.downgrade(OpenApi.convert(v3), "2.0"); ``` diff --git a/examples/v3.0/shopping.json b/examples/v3.1/shopping.json similarity index 92% rename from examples/v3.0/shopping.json rename to examples/v3.1/shopping.json index 09fddbf..c814d21 100644 --- a/examples/v3.0/shopping.json +++ b/examples/v3.1/shopping.json @@ -1,5 +1,5 @@ { - "openapi": "3.0.1", + "openapi": "3.1.0", "servers": [ { "url": "http://localhost:37001", @@ -7,7 +7,7 @@ } ], "info": { - "version": "0.2.0", + "version": "0.4.0", "title": "@samchon/shopping-backend", "description": "Backend for shopping", "license": { @@ -3122,9 +3122,15 @@ "name": "cartId", "in": "path", "schema": { - "type": "string", - "format": "uuid", - "nullable": true + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "uuid" + } + ] }, "description": "Belonged cart's ID", "required": true @@ -3165,9 +3171,15 @@ "name": "cartId", "in": "path", "schema": { - "type": "string", - "format": "uuid", - "nullable": true + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "uuid" + } + ] }, "description": "Belonged cart's ID", "required": true @@ -3210,9 +3222,15 @@ "name": "cartId", "in": "path", "schema": { - "type": "string", - "format": "uuid", - "nullable": true + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "uuid" + } + ] }, "description": "Belonged cart's ID", "required": true @@ -3252,9 +3270,15 @@ "name": "cartId", "in": "path", "schema": { - "type": "string", - "format": "uuid", - "nullable": true + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "uuid" + } + ] }, "description": "Belonged cart's ID", "required": true @@ -3298,9 +3322,15 @@ "name": "cartId", "in": "path", "schema": { - "type": "string", - "format": "uuid", - "nullable": true + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "uuid" + } + ] }, "description": "Belonged cart's ID", "required": true @@ -3335,9 +3365,15 @@ "name": "cartId", "in": "path", "schema": { - "type": "string", - "format": "uuid", - "nullable": true + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "uuid" + } + ] }, "description": "Belonged cart's ID", "required": true @@ -3379,9 +3415,15 @@ "name": "cartId", "in": "path", "schema": { - "type": "string", - "format": "uuid", - "nullable": true + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "uuid" + } + ] }, "description": "Belonged cart's ID", "required": true @@ -7797,7 +7839,6 @@ "$ref": "#/components/schemas/process.global.NodeJS.ResourceUsage" } }, - "nullable": false, "required": [ "cpu", "memory", @@ -7814,7 +7855,6 @@ "type": "number" } }, - "nullable": false, "required": [ "user", "system" @@ -7839,7 +7879,6 @@ "type": "number" } }, - "nullable": false, "required": [ "rss", "heapTotal", @@ -7900,7 +7939,6 @@ "type": "number" } }, - "nullable": false, "required": [ "fsRead", "fsWrite", @@ -7933,7 +7971,6 @@ "items": { "type": "string" }, - "title": "`process", "description": "`process.argv`" }, "commit": { @@ -7943,7 +7980,6 @@ }, "package": { "$ref": "#/components/schemas/ISystem.IPackage", - "title": "`package", "description": "`package.json`" }, "created_at": { @@ -7952,7 +7988,6 @@ "description": "Creation time of this server." } }, - "nullable": false, "required": [ "uid", "arguments", @@ -8005,7 +8040,6 @@ } } }, - "nullable": false, "required": [ "shortHash", "branch", @@ -8031,7 +8065,6 @@ "type": "string" } }, - "nullable": false, "required": [ "name", "email" @@ -8063,16 +8096,12 @@ "type": "object", "properties": { "type": { - "type": "string", - "enum": [ - "git" - ] + "const": "git" }, "url": { "type": "string" } }, - "nullable": false, "required": [ "type", "url" @@ -8091,7 +8120,6 @@ "type": "string" } }, - "nullable": false, "required": [ "url" ] @@ -8112,7 +8140,6 @@ "type": "string" } }, - "nullable": false, "required": [ "registry" ] @@ -8124,7 +8151,6 @@ } } }, - "nullable": false, "required": [ "name", "version", @@ -8142,17 +8168,16 @@ "Recordstringstring": { "type": "object", "properties": {}, - "nullable": false, - "description": "Construct a type with a set of properties K of type T" + "description": "Construct a type with a set of properties K of type T", + "additionalProperties": { + "type": "string" + } }, "IShoppingAdministrator.IInvert": { "type": "object", "properties": { "type": { - "type": "string", - "enum": [ - "administrator" - ], + "const": "administrator", "title": "Discriminant for the type of customer", "description": "Discriminant for the type of customer." }, @@ -8184,7 +8209,6 @@ "description": "Creation time of record.\n\nAnother words, the time when the administrator has signed up." } }, - "nullable": false, "required": [ "type", "member", @@ -8224,7 +8248,6 @@ "description": "Creation time of record.\n\nAnother words, the time when the member has signed up." } }, - "nullable": false, "required": [ "id", "nickname", @@ -8255,7 +8278,6 @@ "description": "Creation time of record." } }, - "nullable": false, "required": [ "id", "value", @@ -8278,7 +8300,14 @@ "description": "Belonged channel." }, "external_user": { - "$ref": "#/components/schemas/IShoppingExternalUser.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingExternalUser" + } + ], "title": "External user information", "description": "External user information.\n\nWhen the customer has come frome an external service." }, @@ -8289,9 +8318,15 @@ "description": "Connection address.\n\nSame with {@link window.location.href } of client." }, "referrer": { - "type": "string", - "format": "uri", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "uri" + } + ], "title": "Referrer address", "description": "Referrer address.\n\nSame with {@link window.document.referrer } of client." }, @@ -8316,7 +8351,6 @@ "description": "Creation time of the connection record." } }, - "nullable": false, "required": [ "id", "channel", @@ -8354,7 +8388,6 @@ "description": "Name of the channel." } }, - "nullable": false, "required": [ "id", "created_at", @@ -8363,7 +8396,7 @@ ], "description": "Channel information.\n\n`IShoppingChannel` is a concept that shapes the distribution channel in the\nmarket. Therefore, the difference in the channel in this e-commerce system\nmeans that it is another site or application.\n\nBy the way, if your shopping mall system requires only one channel, then\njust use only one. This concept is designed to be expandable in the future." }, - "IShoppingExternalUser.Nullable": { + "IShoppingExternalUser": { "type": "object", "properties": { "id": { @@ -8373,7 +8406,14 @@ "description": "Primary Key." }, "citizen": { - "$ref": "#/components/schemas/IShoppingCitizen.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingCitizen" + } + ], "title": "Citizen activation info", "description": "Citizen activation info." }, @@ -8402,7 +8442,6 @@ "description": "Additional information about external user from the external\nsystem." } }, - "nullable": true, "required": [ "id", "citizen", @@ -8414,42 +8453,6 @@ ], "description": "External user information.\n\n`IShoppingExternalUser` is an entity dsigned for when this system needs\nto connect with external services and welcome their users as\n{@link IShoppingCustomer customers} of this service.\n\nFor reference, customers who connect from an external service must have\nthis record, and the external service user is identified through the two\nattributes {@link application } and {@link uid }. If a customer connected\nfrom an external service completes\n{@link IShoppingCitizen real-name authentication} from this service, each\ntime the external service user reconnects to this service and issues a\nnew customer authentication token, real-name authentication begins with\ncompleted.\n\nAnd {@link password } is the password issued to the user by the external\nservice system (the so-called permanent user authentication token), and\nis never the actual user password. However, for customers who entered the\nsame application and uid as the current external system user, this is to\ndetermine whether to view this as a correct external system user or a\nviolation.\n\nIn addition, additional information received from external services can\nbe recorded in the data field in JSON format." }, - "IShoppingCitizen.Nullable": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid", - "title": "Primary Key", - "description": "Primary Key." - }, - "created_at": { - "type": "string", - "format": "date-time", - "title": "Creation time of record", - "description": "Creation time of record." - }, - "mobile": { - "type": "string", - "pattern": "^[0-9]*$", - "title": "Mobile number", - "description": "Mobile number." - }, - "name": { - "type": "string", - "title": "Real name, or equivalent nickname", - "description": "Real name, or equivalent nickname." - } - }, - "nullable": true, - "required": [ - "id", - "created_at", - "mobile", - "name" - ], - "description": "Citizen verification information.\n\n`IShoppingCitizen` is an entity that records the user's\n{@link name real name} and {@link mobile } input information.\n\nFor reference, in South Korea, real name authentication is required for\ne-commerce participants, so the name attribute is important. However, the\nsituation is different overseas, so in reality, mobile attributes are the\nmost important, and identification of individual person is also done based\non this mobile.\n\nOf course, real name and mobile phone authentication information are\nencrypted and stored." - }, "IShoppingCitizen": { "type": "object", "properties": { @@ -8477,7 +8480,6 @@ "description": "Real name, or equivalent nickname." } }, - "nullable": false, "required": [ "id", "created_at", @@ -8489,7 +8491,6 @@ "IShoppingAdministrator.IJoin": { "type": "object", "properties": {}, - "nullable": false, "description": "Joining request info." }, "IShoppingMember.ILogin": { @@ -8507,7 +8508,6 @@ "description": "Password of the member account." } }, - "nullable": false, "required": [ "email", "password" @@ -8568,21 +8568,32 @@ "description": "Representative name of the coupon." }, "opened_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Opening time of the coupon", "description": "Opening time of the coupon." }, "closed_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Closing time of the coupon", "description": "Closing time of the coupon.\n\nTickets cannot be issued after this time.\n\nHowever, previously issued tickets can still be used until their\nexpiration date." } }, - "nullable": false, "required": [ "discount", "restriction", @@ -8598,10 +8609,7 @@ "type": "object", "properties": { "unit": { - "type": "string", - "enum": [ - "amount" - ], + "const": "amount", "title": "Discount unit as amount", "description": "Discount unit as amount.\n\nIt means the order price would be discounted by the amount value." }, @@ -8611,17 +8619,29 @@ "description": "Discount value as amount." }, "threshold": { - "type": "number", - "minimum": 0, - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "number", + "minimum": 0 + } + ], "title": "Minimum purchase amount for discount", "description": "Minimum purchase amount for discount.\n\nWhen setting this value, discount coupons cannot be applied to\norder totals that are less than this value." }, "limit": { - "type": "number", - "minimum": 0, - "exclusiveMinimum": true, - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "number", + "exclusiveMinimum": true, + "minimum": 0 + } + ], "title": "Maximum amount available for discount", "description": "Maximum amount available for discount.\n\nWhen this value is set, no further discount will be given no\nmatter how much you order. This property would be meaningful\nonly when the {@link multiplicative } is `true`." }, @@ -8631,7 +8651,6 @@ "description": "Multiplicative or not.\n\nIf this property is `true`, the discount value would be multiplied\nto the {@link IShoppingCartCommodity.volume } or\n{@link IShoppingOrderGood.volume } value. Also, in that case,\nthe {@link limit } property would be meaningful." } }, - "nullable": false, "required": [ "unit", "value", @@ -8645,37 +8664,45 @@ "type": "object", "properties": { "unit": { - "type": "string", - "enum": [ - "percent" - ], + "const": "percent", "title": "Discount unit as percent", "description": "Discount unit as percent.\n\nIt means the order price would be discounted by the percent value." }, "value": { "type": "number", - "maximum": 100, "minimum": 0, + "maximum": 100, "title": "Discount value as percent", "description": "Discount value as percent." }, "threshold": { - "type": "number", - "minimum": 0, - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "number", + "minimum": 0 + } + ], "title": "Minimum purchase amount for discount", "description": "Minimum purchase amount for discount.\n\nWhen setting this value, discount coupons cannot be applied to\norder totals that are less than this value." }, "limit": { - "type": "number", - "minimum": 0, - "exclusiveMinimum": true, - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "number", + "exclusiveMinimum": true, + "minimum": 0 + } + ], "title": "Maximum amount available for discount", "description": "Maximum amount available for discount.\n\nWhen this value is set, no further discount will be given no\nmatter how much you order." } }, - "nullable": false, "required": [ "unit", "value", @@ -8688,10 +8715,13 @@ "type": "object", "properties": { "access": { - "type": "string", - "enum": [ - "public", - "private" + "oneOf": [ + { + "const": "public" + }, + { + "const": "private" + } ], "title": "Access level of coupon", "description": "Access level of coupon.\n\n- public: possible to find from public API\n- private: unable to find from public API\n - arbitrarily assigned by the seller or administrator\n - issued from one-time link" @@ -8702,32 +8732,55 @@ "description": "Exclusivity or not.\n\nAn exclusive discount coupon refers to a discount coupon that has an\nexclusive relationship with other discount coupons and can only be\nused alone. That is, when an exclusive discount coupon is used, no\nother discount coupon can be used for the same\n{@link IShoppingOrder order} or {@link IShoppingOrderGood good}.\n\nPlease note that this exclusive attribute is a very different concept\nfrom multiplicative, which means whether the same coupon can be\nmultiplied and applied to multiple coupons of the same order, so\nplease do not confuse them." }, "volume": { - "type": "integer", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "integer" + } + ], "title": "Limited quantity issued", "description": "Limited quantity issued.\n\nIf there is a limit to the quantity issued, it becomes impossible to issue tickets exceeding this value.\n\nIn other words, the concept of N coupons being issued on a first-come, first-served basis is created." }, "volume_per_citizen": { - "type": "integer", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "integer" + } + ], "title": "Limited quantity issued per person", "description": "Limited quantity issued per person.\n\nAs a limit to the total amount of issuance per person, it is common to assign 1 to limit duplicate issuance to the same citizen, or to use the NULL value to set no limit.\n\nOf course, by assigning a value of N, the total amount issued to the same citizen can be limited." }, "expired_in": { - "type": "integer", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "integer" + } + ], "title": "Expiration day(s) value", "description": "Expiration day(s) value.\n\nThe concept of expiring N days after a discount coupon ticket is issued.\n\nTherefore, customers must use the ticket within N days, if possible, from the time it is issued." }, "expired_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Expiration date", "description": "Expiration date.\n\nA concept that expires after YYYY-MM-DD after a discount coupon ticket is issued.\n\nDouble restrictions are possible with expired_in, of which the one with the shorter expiration date is used." } }, - "nullable": false, "required": [ "access", "exclusive", @@ -8751,20 +8804,19 @@ "description": "List of target channels and categories." }, "type": { - "type": "string", - "enum": [ - "channel" - ] + "const": "channel" }, "direction": { - "type": "string", - "enum": [ - "include", - "exclude" + "oneOf": [ + { + "const": "include" + }, + { + "const": "exclude" + } ] } }, - "nullable": false, "required": [ "channels", "type", @@ -8777,21 +8829,26 @@ "properties": { "channel_code": { "type": "string", - "title": "Target channel's {@link IShoppingChannel", + "title": "Target channel's {@link IShoppingChannel.code }", "description": "Target channel's {@link IShoppingChannel.code }." }, "category_ids": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "nullable": true, - "title": "Target categories' {@link IShoppingChannelCategory", + "oneOf": [ + { + "type": "null" + }, + { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + ], + "title": "Target categories' {@link IShoppingChannelCategory.id }s", "description": "Target categories' {@link IShoppingChannelCategory.id }s." } }, - "nullable": false, "required": [ "channel_code", "category_ids" @@ -8807,24 +8864,23 @@ "type": "string" }, "minItems": 1, - "title": "List of target section's {@link IShoppingSection", + "title": "List of target section's {@link IShoppingSection.code }s", "description": "List of target section's {@link IShoppingSection.code }s." }, "type": { - "type": "string", - "enum": [ - "section" - ] + "const": "section" }, "direction": { - "type": "string", - "enum": [ - "include", - "exclude" + "oneOf": [ + { + "const": "include" + }, + { + "const": "exclude" + } ] } }, - "nullable": false, "required": [ "section_codes", "type", @@ -8842,24 +8898,23 @@ "format": "uuid" }, "minItems": 1, - "title": "List of target seller's {@link IShoppingSeller", + "title": "List of target seller's {@link IShoppingSeller.id }s", "description": "List of target seller's {@link IShoppingSeller.id }s." }, "type": { - "type": "string", - "enum": [ - "seller" - ] + "const": "seller" }, "direction": { - "type": "string", - "enum": [ - "include", - "exclude" + "oneOf": [ + { + "const": "include" + }, + { + "const": "exclude" + } ] } }, - "nullable": false, "required": [ "seller_ids", "type", @@ -8877,24 +8932,23 @@ "format": "uuid" }, "minItems": 1, - "title": "List of target sale's {@link IShoppingSale", + "title": "List of target sale's {@link IShoppingSale.id }s", "description": "List of target sale's {@link IShoppingSale.id }s." }, "type": { - "type": "string", - "enum": [ - "sale" - ] + "const": "sale" }, "direction": { - "type": "string", - "enum": [ - "include", - "exclude" + "oneOf": [ + { + "const": "include" + }, + { + "const": "exclude" + } ] } }, - "nullable": false, "required": [ "sale_ids", "type", @@ -8922,20 +8976,19 @@ "description": "List of target funnels." }, "type": { - "type": "string", - "enum": [ - "funnel" - ] + "const": "funnel" }, "direction": { - "type": "string", - "enum": [ - "include", - "exclude" - ] - } + "oneOf": [ + { + "const": "include" + }, + { + "const": "exclude" + } + ] + } }, - "nullable": false, "required": [ "funnels", "type", @@ -8947,10 +9000,13 @@ "type": "object", "properties": { "kind": { - "type": "string", - "enum": [ - "url", - "referrer" + "oneOf": [ + { + "const": "url" + }, + { + "const": "referrer" + } ], "title": "Kind of funnel restriction", "description": "Kind of funnel restriction." @@ -8961,7 +9017,6 @@ "description": "Target value." } }, - "nullable": false, "required": [ "kind", "value" @@ -8972,10 +9027,7 @@ "type": "object", "properties": { "kind": { - "type": "string", - "enum": [ - "variable" - ], + "const": "variable", "title": "Kind of funnel restriction", "description": "Kind of funnel restriction." }, @@ -8990,7 +9042,6 @@ "description": "Target variable's value." } }, - "nullable": false, "required": [ "kind", "key", @@ -9048,16 +9099,28 @@ "description": "Representative name of the coupon." }, "opened_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Opening time of the coupon", "description": "Opening time of the coupon." }, "closed_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Closing time of the coupon", "description": "Closing time of the coupon.\n\nTickets cannot be issued after this time.\n\nHowever, previously issued tickets can still be used until their\nexpiration date." }, @@ -9068,7 +9131,6 @@ "description": "Creation tie of the record." } }, - "nullable": false, "required": [ "id", "designer", @@ -9099,7 +9161,6 @@ "description": "Creation tmie of record.\n\nAnother words, the time when the seller has signed up." } }, - "nullable": false, "required": [ "id", "created_at" @@ -9122,7 +9183,6 @@ "description": "Creation time of record.\n\nAnother words, the time when the administrator has signed up." } }, - "nullable": false, "required": [ "id", "created_at" @@ -9133,19 +9193,30 @@ "type": "object", "properties": { "volume": { - "type": "integer", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "integer" + } + ], "title": "Remaining volume for everyone", "description": "Remaining volume for everyone.\n\nIf there is a limit to the quantity issued, it becomes impossible to\nissue tickets exceeding this value.\n\nIn other words, the concept of N coupons being issued on a first-come,\nfirst-served basis is created." }, "volume_per_citizen": { - "type": "integer", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "integer" + } + ], "title": "Remaining volume per citizen", "description": "Remaining volume per citizen.\n\nAs a limit to the total amount of issuance per person, it is common to\nassign 1 to limit duplicate issuance to the same citizen, or to use the\n`nul`` value to set no limit.\n\nOf course, by assigning a value of N, the total amount issued to the\nsame citizen can be limited." } }, - "nullable": false, "required": [ "volume", "volume_per_citizen" @@ -9185,24 +9256,23 @@ "description": "List of target channels and categories." }, "type": { - "type": "string", - "enum": [ - "channel" - ], + "const": "channel", "title": "Descrimanator type", "description": "Descrimanator type." }, "direction": { - "type": "string", - "enum": [ - "include", - "exclude" + "oneOf": [ + { + "const": "include" + }, + { + "const": "exclude" + } ], "title": "Direction of the criteria", "description": "Direction of the criteria." } }, - "nullable": false, "required": [ "channels", "type", @@ -9219,17 +9289,22 @@ "description": "Target channel." }, "categories": { - "type": "array", - "items": { - "$ref": "#/components/schemas/IShoppingChannelCategory.IInvert" - }, - "minItems": 1, - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/IShoppingChannelCategory.IInvert" + }, + "minItems": 1 + } + ], "title": "List of target categories", "description": "List of target categories." } }, - "nullable": false, "required": [ "channel", "categories" @@ -9239,50 +9314,14 @@ "type": "object", "properties": { "parent": { - "$ref": "#/components/schemas/IShoppingChannelCategory.IInvert.Nullable", - "title": "Parent category info with recursive structure", - "description": "Parent category info with recursive structure.\n\nIf no parent exists, then be `null`." - }, - "id": { - "type": "string", - "format": "uuid", - "title": "Primary Key", - "description": "Primary Key." - }, - "parent_id": { - "type": "string", - "format": "uuid", - "nullable": true, - "title": "Parent category's ID", - "description": "Parent category's ID." - }, - "name": { - "type": "string", - "title": "Representative name of the category", - "description": "Representative name of the category.\n\nThe name must be unique within the parent category. If no parent exists,\nthen the name must be unique within the channel between no parent\ncategories." - }, - "created_at": { - "type": "string", - "format": "date-time", - "title": "Creation time of record", - "description": "Creation time of record." - } - }, - "nullable": false, - "required": [ - "parent", - "id", - "parent_id", - "name", - "created_at" - ], - "description": "Invert category information with parent category." - }, - "IShoppingChannelCategory.IInvert.Nullable": { - "type": "object", - "properties": { - "parent": { - "$ref": "#/components/schemas/IShoppingChannelCategory.IInvert.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingChannelCategory.IInvert" + } + ], "title": "Parent category info with recursive structure", "description": "Parent category info with recursive structure.\n\nIf no parent exists, then be `null`." }, @@ -9293,9 +9332,15 @@ "description": "Primary Key." }, "parent_id": { - "type": "string", - "format": "uuid", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "uuid" + } + ], "title": "Parent category's ID", "description": "Parent category's ID." }, @@ -9311,7 +9356,6 @@ "description": "Creation time of record." } }, - "nullable": true, "required": [ "parent", "id", @@ -9334,24 +9378,23 @@ "description": "Target sections to include or exclude." }, "type": { - "type": "string", - "enum": [ - "section" - ], + "const": "section", "title": "Descrimanator type", "description": "Descrimanator type." }, "direction": { - "type": "string", - "enum": [ - "include", - "exclude" + "oneOf": [ + { + "const": "include" + }, + { + "const": "exclude" + } ], "title": "Direction of the criteria", "description": "Direction of the criteria." } }, - "nullable": false, "required": [ "sections", "type", @@ -9385,7 +9428,6 @@ "description": "Creation time of record." } }, - "nullable": false, "required": [ "id", "code", @@ -9407,24 +9449,23 @@ "description": "Target sellers to include or exclude." }, "type": { - "type": "string", - "enum": [ - "seller" - ], + "const": "seller", "title": "Descrimanator type", "description": "Descrimanator type." }, "direction": { - "type": "string", - "enum": [ - "include", - "exclude" + "oneOf": [ + { + "const": "include" + }, + { + "const": "exclude" + } ], "title": "Direction of the criteria", "description": "Direction of the criteria." } }, - "nullable": false, "required": [ "sellers", "type", @@ -9445,24 +9486,23 @@ "description": "Target sales to include or exclude." }, "type": { - "type": "string", - "enum": [ - "sale" - ], + "const": "sale", "title": "Descrimanator type", "description": "Descrimanator type." }, "direction": { - "type": "string", - "enum": [ - "include", - "exclude" + "oneOf": [ + { + "const": "include" + }, + { + "const": "exclude" + } ], "title": "Direction of the criteria", "description": "Direction of the criteria." } }, - "nullable": false, "required": [ "sales", "type", @@ -9549,35 +9589,58 @@ "description": "Last updated time of the record.\n\nIn another words, creation time of the last snapshot." }, "paused_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Paused time of the sale", "description": "Paused time of the sale.\n\nThe sale is paused by the seller, for some reason.\n\n{@link IShoppingCustomer Customers} can still see the sale on the\nboth list and detail pages, but the sale has a warning label\n\"The sale is paused by the seller\"." }, "suspended_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Suspended time of the sale", "description": "Suspended time of the sale.\n\nThe sale is suspended by the seller, for some reason.\n\n{@link IShoppingCustomer Customers} cannot see the sale on the\nboth list and detail pages. It is almost same with soft delettion,\nbut there's a little bit difference that the owner\n{@link IShoppingSeller seller} can still see the sale and resume it.\n\nOf course, the {@link IShoppingCustomer customers} who have\nalready purchased the sale can still see the sale on the\n{@link IShoppingOrder order} page." }, "opened_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Opening time of the sale", "description": "Opening time of the sale." }, "closed_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Closing time of the sale", "description": "Closing time of the sale.\n\nIf this value is `null`, the sale be continued forever." } }, - "nullable": false, "required": [ "section", "seller", @@ -9602,10 +9665,7 @@ "type": "object", "properties": { "type": { - "type": "string", - "enum": [ - "seller" - ], + "const": "seller", "title": "Discriminant for the type of customer", "description": "Discriminant for the type of customer." }, @@ -9637,7 +9697,6 @@ "description": "Creation tmie of record.\n\nAnother words, the time when the seller has signed up." } }, - "nullable": false, "required": [ "type", "member", @@ -9658,7 +9717,6 @@ "$ref": "#/components/schemas/IShoppingPrice" } }, - "nullable": false, "required": [ "lowest", "highest" @@ -9680,7 +9738,6 @@ "description": "Real price to pay." } }, - "nullable": false, "required": [ "nominal", "real" @@ -9704,7 +9761,6 @@ } } }, - "nullable": false, "required": [ "id", "title", @@ -9733,10 +9789,16 @@ "description": "File name, except extension.\n\nIf there's file `.gitignore`, then its name is an empty string." }, "extension": { - "type": "string", - "maxLength": 8, - "minLength": 1, - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "minLength": 1, + "maxLength": 8 + } + ], "title": "Extension", "description": "Extension.\n\nPossible to omit like `README` case." }, @@ -9747,7 +9809,6 @@ "description": "URL path of the real file." } }, - "nullable": false, "required": [ "id", "created_at", @@ -9791,7 +9852,6 @@ "description": "Name of the channel." } }, - "nullable": false, "required": [ "categories", "id", @@ -9829,7 +9889,6 @@ "description": "Whether the unit is required or not.\n\nWhen the unit is required, the customer must select the unit. If do not\nselect, customer can't buy it.\n\nFor example, if there's a sale \"Macbook Set\" and one of the unit is the\n\"Main Body\", is it possible to buy the \"Macbook Set\" without the\n\"Main Body\" unit? This property is for that case." } }, - "nullable": false, "required": [ "price_range", "id", @@ -9858,24 +9917,23 @@ "description": "List of target funnels." }, "type": { - "type": "string", - "enum": [ - "funnel" - ], + "const": "funnel", "title": "Descrimanator type", "description": "Descrimanator type." }, "direction": { - "type": "string", - "enum": [ - "include", - "exclude" + "oneOf": [ + { + "const": "include" + }, + { + "const": "exclude" + } ], "title": "Direction of the criteria", "description": "Direction of the criteria." } }, - "nullable": false, "required": [ "funnels", "type", @@ -9903,20 +9961,43 @@ "sort": { "type": "array", "items": { - "type": "string", - "enum": [ - "-coupon.name", - "-coupon.unit", - "-coupon.value", - "-coupon.created_at", - "-coupon.opened_at", - "-coupon.closed_at", - "+coupon.name", - "+coupon.unit", - "+coupon.value", - "+coupon.created_at", - "+coupon.opened_at", - "+coupon.closed_at" + "oneOf": [ + { + "const": "-coupon.name" + }, + { + "const": "-coupon.unit" + }, + { + "const": "-coupon.value" + }, + { + "const": "-coupon.created_at" + }, + { + "const": "-coupon.opened_at" + }, + { + "const": "-coupon.closed_at" + }, + { + "const": "+coupon.name" + }, + { + "const": "+coupon.unit" + }, + { + "const": "+coupon.value" + }, + { + "const": "+coupon.created_at" + }, + { + "const": "+coupon.opened_at" + }, + { + "const": "+coupon.closed_at" + } ] } }, @@ -9927,12 +10008,10 @@ }, "limit": { "type": "integer", - "default": 100, "title": "Limitation of records per a page", "description": "Limitation of records per a page." } - }, - "nullable": false + } }, "IShoppingCoupon.IRequest.ISearch": { "type": "object", @@ -9940,8 +10019,7 @@ "name": { "type": "string" } - }, - "nullable": false + } }, "IPageIShoppingCoupon": { "type": "object", @@ -9960,7 +10038,6 @@ "description": "List of records." } }, - "nullable": false, "required": [ "pagination", "data" @@ -9977,7 +10054,6 @@ }, "limit": { "type": "integer", - "default": 100, "title": "Limitation of records per a page", "description": "Limitation of records per a page." }, @@ -9992,7 +10068,6 @@ "description": "Total pages.\n\nEqual to {@link records } / {@link limit } with ceiling." } }, - "nullable": false, "required": [ "current", "limit", @@ -10010,14 +10085,25 @@ "sort": { "type": "array", "items": { - "type": "string", - "enum": [ - "-deposit.source", - "-deposit.code", - "-deposit.direction", - "+deposit.source", - "+deposit.code", - "+deposit.direction" + "oneOf": [ + { + "const": "-deposit.source" + }, + { + "const": "-deposit.code" + }, + { + "const": "-deposit.direction" + }, + { + "const": "+deposit.source" + }, + { + "const": "+deposit.code" + }, + { + "const": "+deposit.direction" + } ] } }, @@ -10028,12 +10114,10 @@ }, "limit": { "type": "integer", - "default": 100, "title": "Limitation of records per a page", "description": "Limitation of records per a page." } - }, - "nullable": false + } }, "IShoppingDeposit.IRequest.ISearch": { "type": "object", @@ -10045,14 +10129,16 @@ "type": "string" }, "direction": { - "type": "number", - "enum": [ - 1, - -1 + "oneOf": [ + { + "const": -1 + }, + { + "const": 1 + } ] } - }, - "nullable": false + } }, "IPageIShoppingDeposit": { "type": "object", @@ -10071,7 +10157,6 @@ "description": "List of records." } }, - "nullable": false, "required": [ "pagination", "data" @@ -10099,7 +10184,6 @@ "$ref": "#/components/schemas/IShoppingDeposit.Direction" } }, - "nullable": false, "required": [ "id", "created_at", @@ -10109,10 +10193,13 @@ ] }, "IShoppingDeposit.Direction": { - "type": "number", - "enum": [ - 1, - -1 + "oneOf": [ + { + "const": 1 + }, + { + "const": -1 + } ] }, "IShoppingDeposit.ICreate": { @@ -10125,14 +10212,16 @@ "type": "string" }, "direction": { - "type": "number", - "enum": [ - 1, - -1 + "oneOf": [ + { + "const": -1 + }, + { + "const": 1 + } ] } }, - "nullable": false, "required": [ "code", "source", @@ -10148,14 +10237,25 @@ "sort": { "type": "array", "items": { - "type": "string", - "enum": [ - "-mileage.source", - "-mileage.code", - "-mileage.direction", - "+mileage.source", - "+mileage.code", - "+mileage.direction" + "oneOf": [ + { + "const": "-mileage.source" + }, + { + "const": "-mileage.code" + }, + { + "const": "-mileage.direction" + }, + { + "const": "+mileage.source" + }, + { + "const": "+mileage.code" + }, + { + "const": "+mileage.direction" + } ] } }, @@ -10166,12 +10266,10 @@ }, "limit": { "type": "integer", - "default": 100, "title": "Limitation of records per a page", "description": "Limitation of records per a page." } - }, - "nullable": false + } }, "IShoppingMileage.IRequest.ISearch": { "type": "object", @@ -10183,14 +10281,16 @@ "type": "string" }, "direction": { - "type": "number", - "enum": [ - 1, - -1 + "oneOf": [ + { + "const": -1 + }, + { + "const": 1 + } ] } - }, - "nullable": false + } }, "IPageIShoppingMileage": { "type": "object", @@ -10209,7 +10309,6 @@ "description": "List of records." } }, - "nullable": false, "required": [ "pagination", "data" @@ -10224,8 +10323,14 @@ "format": "uuid" }, "value": { - "type": "number", - "nullable": true + "oneOf": [ + { + "type": "null" + }, + { + "type": "number" + } + ] }, "created_at": { "type": "string", @@ -10241,7 +10346,6 @@ "$ref": "#/components/schemas/IShoppingMileage.Direction" } }, - "nullable": false, "required": [ "id", "value", @@ -10252,10 +10356,13 @@ ] }, "IShoppingMileage.Direction": { - "type": "number", - "enum": [ - 1, - -1 + "oneOf": [ + { + "const": 1 + }, + { + "const": -1 + } ] }, "IShoppingMileage.ICreate": { @@ -10268,18 +10375,26 @@ "type": "string" }, "direction": { - "type": "number", - "enum": [ - 1, - -1 + "oneOf": [ + { + "const": -1 + }, + { + "const": 1 + } ] }, "value": { - "type": "number", - "nullable": true + "oneOf": [ + { + "type": "null" + }, + { + "type": "number" + } + ] } }, - "nullable": false, "required": [ "code", "source", @@ -10296,14 +10411,25 @@ "sort": { "type": "array", "items": { - "type": "string", - "enum": [ - "-donation.created_at", - "-donation.value", - "-donation.reason", - "+donation.created_at", - "+donation.value", - "+donation.reason" + "oneOf": [ + { + "const": "-donation.created_at" + }, + { + "const": "-donation.value" + }, + { + "const": "-donation.reason" + }, + { + "const": "+donation.created_at" + }, + { + "const": "+donation.value" + }, + { + "const": "+donation.reason" + } ] } }, @@ -10314,12 +10440,10 @@ }, "limit": { "type": "integer", - "default": 100, "title": "Limitation of records per a page", "description": "Limitation of records per a page." } - }, - "nullable": false + } }, "IShoppingMileageDonation.IRequest.ISearch": { "type": "object", @@ -10343,8 +10467,7 @@ "type": "string", "format": "date-time" } - }, - "nullable": false + } }, "IShoppingCitizen.IRequest.ISearch": { "type": "object", @@ -10356,8 +10479,7 @@ "name": { "type": "string" } - }, - "nullable": false + } }, "IPageIShoppingMileageDonation": { "type": "object", @@ -10376,7 +10498,6 @@ "description": "List of records." } }, - "nullable": false, "required": [ "pagination", "data" @@ -10407,7 +10528,6 @@ "format": "date-time" } }, - "nullable": false, "required": [ "id", "administrator", @@ -10431,7 +10551,6 @@ "type": "string" } }, - "nullable": false, "required": [ "citizen_id", "value", @@ -10447,16 +10566,31 @@ "sort": { "type": "array", "items": { - "type": "string", - "enum": [ - "-order.price", - "-order.quantity", - "-order.created_at", - "-order.publish.paid_at", - "+order.price", - "+order.quantity", - "+order.created_at", - "+order.publish.paid_at" + "oneOf": [ + { + "const": "-order.price" + }, + { + "const": "-order.quantity" + }, + { + "const": "-order.created_at" + }, + { + "const": "-order.publish.paid_at" + }, + { + "const": "+order.price" + }, + { + "const": "+order.quantity" + }, + { + "const": "+order.created_at" + }, + { + "const": "+order.publish.paid_at" + } ] } }, @@ -10467,12 +10601,10 @@ }, "limit": { "type": "integer", - "default": 100, "title": "Limitation of records per a page", "description": "Limitation of records per a page." } }, - "nullable": false, "description": "Request of orders with pagination and searching/sorting conditions." }, "IShoppingOrder.IRequest.ISearch": { @@ -10485,14 +10617,19 @@ "type": "number" }, "paid": { - "type": "boolean", - "nullable": true + "oneOf": [ + { + "type": "null" + }, + { + "type": "boolean" + } + ] }, "sale": { "$ref": "#/components/schemas/IShoppingSale.IRequest.ISearch" } - }, - "nullable": false + } }, "IShoppingSale.IRequest.ISearch": { "type": "object", @@ -10503,10 +10640,7 @@ "show_suspended": { "oneOf": [ { - "type": "string", - "enum": [ - "only" - ] + "const": "only" }, { "type": "boolean" @@ -10555,8 +10689,7 @@ "seller": { "$ref": "#/components/schemas/IShoppingSeller.IRequest.ISearch" } - }, - "nullable": false + } }, "IShoppingPrice.ISearch": { "type": "object", @@ -10567,8 +10700,7 @@ "maximum": { "type": "number" } - }, - "nullable": false + } }, "IShoppingSaleReview.IInvertSearch": { "type": "object", @@ -10579,24 +10711,22 @@ "count": { "$ref": "#/components/schemas/IShoppingSaleReview.IInvertSearch.ICountRange" } - }, - "nullable": false + } }, "IShoppingSaleReview.IInvertSearch.IScoreRange": { "type": "object", "properties": { "minimum": { "type": "number", - "maximum": 100, - "minimum": 0 + "minimum": 0, + "maximum": 100 }, "maximum": { "type": "number", - "maximum": 100, - "minimum": 0 + "minimum": 0, + "maximum": 100 } - }, - "nullable": false + } }, "IShoppingSaleReview.IInvertSearch.ICountRange": { "type": "object", @@ -10607,8 +10737,7 @@ "maximum": { "type": "integer" } - }, - "nullable": false + } }, "IShoppingSeller.IRequest.ISearch": { "type": "object", @@ -10631,8 +10760,7 @@ "nickname": { "type": "string" } - }, - "nullable": false + } }, "IPageIShoppingOrder": { "type": "object", @@ -10651,7 +10779,6 @@ "description": "List of records." } }, - "nullable": false, "required": [ "pagination", "data" @@ -10687,7 +10814,14 @@ "description": "Price information including discounts.\n\nFor reference, this price value has multiplied by the {@link volume } value.\nTherefore, even if {@link volume } value is equal to the target\n{@link IShoppingCartCommodity.volume }, this price value can be different\nwith the {@link IShoppingCartCommodity.price } value." }, "publish": { - "$ref": "#/components/schemas/IShoppingOrderPublish.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingOrderPublish" + } + ], "title": "Order completion and payment information", "description": "Order completion and payment information." }, @@ -10698,7 +10832,6 @@ "description": "Creation time of the record." } }, - "nullable": false, "required": [ "id", "customer", @@ -10713,20 +10846,31 @@ "type": "object", "properties": { "type": { - "type": "string", - "enum": [ - "customer" - ], + "const": "customer", "title": "Discriminant for the type of customer", "description": "Discriminant for the type of customer." }, "member": { - "$ref": "#/components/schemas/IShoppingMember.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingMember" + } + ], "title": "Membership information", "description": "Membership information.\n\nIf the customer has joined as a member." }, "citizen": { - "$ref": "#/components/schemas/IShoppingCitizen.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingCitizen" + } + ], "title": "Citizen information", "description": "Citizen information.\n\nIf the customer has verified his real name and mobile number." }, @@ -10742,7 +10886,14 @@ "description": "Belonged channel." }, "external_user": { - "$ref": "#/components/schemas/IShoppingExternalUser.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingExternalUser" + } + ], "title": "External user information", "description": "External user information.\n\nWhen the customer has come frome an external service." }, @@ -10753,9 +10904,15 @@ "description": "Connection address.\n\nSame with {@link window.location.href } of client." }, "referrer": { - "type": "string", - "format": "uri", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "uri" + } + ], "title": "Referrer address", "description": "Referrer address.\n\nSame with {@link window.document.referrer } of client." }, @@ -10780,7 +10937,6 @@ "description": "Creation time of the connection record." } }, - "nullable": false, "required": [ "type", "member", @@ -10795,21 +10951,42 @@ ], "description": "Customer information, but not a person but a connection basis.\n\n`IShoppingCustomer` is an entity that literally embodies the information of\nthose who participated in the market as customers. By the way, the\n`IShoppingCustomer` does not mean a person, but a connection basis. Therefore,\neven if the same person connects to the shopping mall multiple, multiple\nrecords are created in `IShoppingCustomer`.\n\nThe first purpose of this is to track the customer's inflow path in detail,\nand it is for cases where the same person enters as a non-member,\n{@link IShoppingCartCommodity puts items in the shopping cart} in advance,\nand only authenticates their {@link IShoppingCitizen real name} or\nregisters/logs in at the moment of {@link IShoppingOrderPublish payment}.\nIt is the second. Lastly, it is to accurately track the activities that\na person performs at the shopping mall in various ways like below.\n\n- Same person comes from an {@link IShoppingExternalUser external service}\n- Same person creates multiple accounts\n- Same person makes a {@link IShoppingOrderPublish purchase} as a non-member with only {@link IShoppingCitizen real name authentication}\n- Same person acts both {@link IShoppingSeller seller} and {@link IShoppingAdministrator admin} at the same time\n\nTherefore, `IShoppingCustomer` can have multiple records with the same\n{@link IShoppingCitizen }, {@link IShoppingMember }, and\n{@link IShoppingExternalUser }. Additionally, if a customer signs up for\nmembership after verifying their real name or signs up for our service after\nbeing a user of an external service, all related records are changed at once.\nTherefore, identification and tracking of customers can be done very\nsystematically." }, - "IShoppingMember.Nullable": { + "IShoppingMember": { "type": "object", "properties": { "citizen": { - "$ref": "#/components/schemas/IShoppingCitizen.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingCitizen" + } + ], "title": "Citizen information", "description": "Citizen information.\n\nOnly when has verified as a citizen, with mobile number and real name.\n\nFor reference, if the member has signed up as a seller or administrator,\nthis citizen information must be." }, "seller": { - "$ref": "#/components/schemas/IShoppingSeller.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingSeller" + } + ], "title": "Seller information", "description": "Seller information.\n\nIf the member also signed up as a seller." }, "administrator": { - "$ref": "#/components/schemas/IShoppingAdministrator.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingAdministrator" + } + ], "title": "Administrator information", "description": "Administrator information.\n\nIf the member also signed up as an administrator." }, @@ -10839,7 +11016,6 @@ "description": "Creation time of record.\n\nAnother words, the time when the member has signed up." } }, - "nullable": true, "required": [ "citizen", "seller", @@ -10851,52 +11027,6 @@ ], "description": "Member Account.\n\n`IShoppingMember` is an entity that symbolizes the case when a\n{@link IShoppingCustomer } signs up as a member of this shopping mall\nsystem.\n\nIf a `IShoppingMember` has seller or administrator property. it means that\nthe {@link IShoppingCustomer } has acting as a {@link IShoppingSeller seller}\nor {@link IShoppingAdministrator administrator} at the same time." }, - "IShoppingSeller.Nullable": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid", - "title": "Primary Key", - "description": "Primary Key." - }, - "created_at": { - "type": "string", - "format": "date-time", - "title": "Creation tmie of record", - "description": "Creation tmie of record.\n\nAnother words, the time when the seller has signed up." - } - }, - "nullable": true, - "required": [ - "id", - "created_at" - ], - "description": "Seller information.\n\n`IShoppingSeller` is an entity that embodies a person who registers\n{@link IShoppingSale sales} to operate selling activities, with\n{@link IShoppingMember membership} joining.\n\nFor reference, unlike {@link IShoppingCustomer customers} which can\nparticipate even without membership joining, seller must join membership\nto operate sales. Also, seller must do the\n{@link IShoppingCitizen real-name and mobile authentication}, too." - }, - "IShoppingAdministrator.Nullable": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid", - "title": "Primary Key", - "description": "Primary Key." - }, - "created_at": { - "type": "string", - "format": "date-time", - "title": "Creation time of record", - "description": "Creation time of record.\n\nAnother words, the time when the administrator has signed up." - } - }, - "nullable": true, - "required": [ - "id", - "created_at" - ], - "description": "Administrator account.\n\n`IShoppingAdministrator` is an entity that embodies a person who manages\nthe shopping mall system, with {@link IShoppingMember membership} joining.\n\nFor reference, unlike {@link IShoppingCustomer customers} which can participate\neven without membership joining, administrator must join membership to operate\nmanagements. Also, administrator must perform the\n{@link IShoppingCitizen real-name and mobile authentication}, too." - }, "IShoppingOrderGood": { "type": "object", "properties": { @@ -10922,29 +11052,49 @@ "description": "Price information including discounts and multipled volume." }, "state": { - "type": "string", - "enum": [ - "none", - "underway", - "preparing", - "manufacturing", - "shipping", - "delivering", - "arrived" + "oneOf": [ + { + "type": "null" + }, + { + "const": "none" + }, + { + "const": "underway" + }, + { + "const": "preparing" + }, + { + "const": "manufacturing" + }, + { + "const": "shipping" + }, + { + "const": "delivering" + }, + { + "const": "arrived" + } ], - "nullable": true, "title": "State of delivery about the good", "description": "State of delivery about the good." }, "confirmed_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Confirmation time of order good", "description": "Confirmation time of order good.\n\nWhen be confirmed, customer can't request refund or exchange.\n\nThe confirmation be accomplished by following cases.\n\n- Customer does it directly.\n- 14 days after the delivery." } }, - "nullable": false, "required": [ "id", "commodity", @@ -10997,7 +11147,6 @@ "description": "Creation time of the record." } }, - "nullable": false, "required": [ "id", "sale", @@ -11083,35 +11232,58 @@ "description": "Last updated time of the record.\n\nIn another words, creation time of the last snapshot." }, "paused_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Paused time of the sale", "description": "Paused time of the sale.\n\nThe sale is paused by the seller, for some reason.\n\n{@link IShoppingCustomer Customers} can still see the sale on the\nboth list and detail pages, but the sale has a warning label\n\"The sale is paused by the seller\"." }, "suspended_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "title": "Suspended time of the sale", - "description": "Suspended time of the sale.\n\nThe sale is suspended by the seller, for some reason.\n\n{@link IShoppingCustomer Customers} cannot see the sale on the\nboth list and detail pages. It is almost same with soft delettion,\nbut there's a little bit difference that the owner\n{@link IShoppingSeller seller} can still see the sale and resume it.\n\nOf course, the {@link IShoppingCustomer customers} who have\nalready purchased the sale can still see the sale on the\n{@link IShoppingOrder order} page." - }, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], + "title": "Suspended time of the sale", + "description": "Suspended time of the sale.\n\nThe sale is suspended by the seller, for some reason.\n\n{@link IShoppingCustomer Customers} cannot see the sale on the\nboth list and detail pages. It is almost same with soft delettion,\nbut there's a little bit difference that the owner\n{@link IShoppingSeller seller} can still see the sale and resume it.\n\nOf course, the {@link IShoppingCustomer customers} who have\nalready purchased the sale can still see the sale on the\n{@link IShoppingOrder order} page." + }, "opened_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Opening time of the sale", "description": "Opening time of the sale." }, "closed_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Closing time of the sale", "description": "Closing time of the sale.\n\nIf this value is `null`, the sale be continued forever." } }, - "nullable": false, "required": [ "section", "seller", @@ -11165,7 +11337,6 @@ "description": "Whether the unit is required or not.\n\nWhen the unit is required, the customer must select the unit. If do not\nselect, customer can't buy it.\n\nFor example, if there's a sale \"Macbook Set\" and one of the unit is the\n\"Main Body\", is it possible to buy the \"Macbook Set\" without the\n\"Main Body\" unit? This property is for that case." } }, - "nullable": false, "required": [ "stocks", "id", @@ -11213,7 +11384,6 @@ "description": "List of choices.\n\nWhich values being written for each option." } }, - "nullable": false, "required": [ "id", "name", @@ -11238,7 +11408,6 @@ "description": "Total outcome quantity." } }, - "nullable": false, "required": [ "income", "outcome" @@ -11260,30 +11429,36 @@ "description": "Target option." }, "candidate": { - "$ref": "#/components/schemas/IShoppingSaleUnitOptionCandidate.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingSaleUnitOptionCandidate" + } + ], "title": "Selected candidate value", "description": "Selected candidate value." }, "value": { "oneOf": [ { - "type": "string", - "nullable": true + "type": "null" }, { - "type": "number", - "nullable": true + "type": "string" }, { - "type": "boolean", - "nullable": true + "type": "number" + }, + { + "type": "boolean" } ], "title": "Written value", "description": "Written value." } }, - "nullable": false, "required": [ "id", "option", @@ -11313,10 +11488,7 @@ "description": "Primary Key." }, "type": { - "type": "string", - "enum": [ - "select" - ], + "const": "select", "title": "Discriminant for the type of selectable option", "description": "Discriminant for the type of selectable option." }, @@ -11331,7 +11503,6 @@ "description": "Whether the option is variable or not.\n\nWhen type of current option is \"select\", this attribute means whether\nselecting different candidate value affects the final stock or not." } }, - "nullable": false, "required": [ "id", "type", @@ -11349,11 +11520,16 @@ "description": "Primary Key." }, "type": { - "type": "string", - "enum": [ - "string", - "number", - "boolean" + "oneOf": [ + { + "const": "string" + }, + { + "const": "number" + }, + { + "const": "boolean" + } ], "title": "Type of descriptive option", "description": "Type of descriptive option.\n\nWhich typed value should be written when purchasing." @@ -11364,7 +11540,6 @@ "description": "Readable name of the option." } }, - "nullable": false, "required": [ "id", "type", @@ -11372,7 +11547,7 @@ ], "description": "Descriptive option.\n\nWhen type of the option not `\"select\"`, it means the option is descriptive\nthat requiring {@link IShoppingCustomer customers} to write some value to\n{@link IShoppingOrder purchase}. Also, whatever customer writes about the\noption, it does not affect the {@link IShoppingSaleUnitStock final stock}.\n\nAnother words, the descriptive option is just for information transfer." }, - "IShoppingSaleUnitOptionCandidate.Nullable": { + "IShoppingSaleUnitOptionCandidate": { "type": "object", "properties": { "id": { @@ -11387,7 +11562,6 @@ "description": "Represents the name of the candidate value." } }, - "nullable": true, "required": [ "id", "name" @@ -11434,7 +11608,6 @@ "description": "Real price to pay." } }, - "nullable": false, "required": [ "cash", "deposit", @@ -11493,7 +11666,6 @@ "description": "Real price to pay." } }, - "nullable": false, "required": [ "ticket_payments", "cash", @@ -11526,7 +11698,6 @@ "description": "Creation time of the record." } }, - "nullable": false, "required": [ "id", "ticket", @@ -11560,14 +11731,19 @@ "description": "Creation time of the record." }, "expired_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Expiration time of the ticket", "description": "Expiration time of the ticket." } }, - "nullable": false, "required": [ "id", "customer", @@ -11577,7 +11753,7 @@ ], "description": "Discount coupon ticket issuance details.\n\n`IShoppingCouponTicket` is an entity that symbolizes\n{@link IShoppingCoupon discount coupon} tickets issued by\n{@link IShoppingCustomer customers}.\n\nAnd if the target discount coupon specification itself has an expiration\ndate, the expiration date is recorded in expired_at and is automatically\ndiscarded after that expiration date. Of course, it doesn't matter if you\nuse the discount coupon for your order within the deadline." }, - "IShoppingOrderPublish.Nullable": { + "IShoppingOrderPublish": { "type": "object", "properties": { "deliveries": { @@ -11606,16 +11782,28 @@ "description": "Creation time of the record." }, "paid_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Time when the order was paid", "description": "Time when the order was paid." }, "cancelled_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Time when the payment was cancelled", "description": "Time when the payment was cancelled." }, @@ -11625,7 +11813,6 @@ "description": "Address where the {@link IShoppingOrderGood goods} to be delivered." } }, - "nullable": true, "required": [ "deliveries", "state", @@ -11688,7 +11875,6 @@ "description": "Creation time of the record." } }, - "nullable": false, "required": [ "id", "seller", @@ -11716,9 +11902,15 @@ "description": "Creation time of the record." }, "deleted_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Deletion time of the record", "description": "Deletion time of the record." }, @@ -11728,33 +11920,56 @@ "description": "Type of journey.\n\n- preparing\n- manufacturing\n- shipping\n- delivering" }, "title": { - "type": "string", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], "title": "Title of journey", "description": "Title of journey." }, "description": { - "type": "string", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], "title": "Description of journey", "description": "Description of journey." }, "started_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Start time of the journey", "description": "Start time of the journey." }, "completed_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Completion time of the journey", "description": "Completion time of the journey." } }, - "nullable": false, "required": [ "id", "created_at", @@ -11768,12 +11983,19 @@ "description": "Journey of delivery.\n\n`IShoppingDeliveryJourney` is a subsidiary entity of {@link IShoppingDelivery },\ndescribing each journey of the delivery. For reference, the word journey\nmeans each step of the delivery process, such as preparing, shipping, and\ndelivering {@link IShoppingOrderGood goods} to the\n{@link IShoppingCustomer customer}." }, "IShoppingDeliveryJourney.Type": { - "type": "string", - "enum": [ - "preparing", - "manufacturing", - "shipping", - "delivering" + "oneOf": [ + { + "const": "preparing" + }, + { + "const": "manufacturing" + }, + { + "const": "shipping" + }, + { + "const": "delivering" + } ] }, "IShoppingDeliveryPiece": { @@ -11788,19 +12010,19 @@ "publish_id": { "type": "string", "format": "uuid", - "title": "Target order's {@link IShoppingOrderPublish", + "title": "Target order's {@link IShoppingOrderPublish.id }", "description": "Target order's {@link IShoppingOrderPublish.id }." }, "good_id": { "type": "string", "format": "uuid", - "title": "Target good's {@link IShoppingOrderGood", + "title": "Target good's {@link IShoppingOrderGood.id }", "description": "Target good's {@link IShoppingOrderGood.id }." }, "stock_id": { "type": "string", "format": "uuid", - "title": "Target stock's {@link IShoppingSaleUnitStock", + "title": "Target stock's {@link IShoppingSaleUnitStock.id }", "description": "Target stock's {@link IShoppingSaleUnitStock.id }." }, "quantity": { @@ -11810,7 +12032,6 @@ "description": "Quantity of the stock.\n\nIt can be precision value to express splitted shipping." } }, - "nullable": false, "required": [ "id", "publish_id", @@ -11832,8 +12053,14 @@ "format": "date-time" }, "company": { - "type": "string", - "nullable": true + "oneOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ] }, "name": { "type": "string" @@ -11842,7 +12069,6 @@ "type": "string" } }, - "nullable": false, "required": [ "id", "created_at", @@ -11852,15 +12078,28 @@ ] }, "IShoppingDelivery.State": { - "type": "string", - "enum": [ - "none", - "underway", - "preparing", - "manufacturing", - "shipping", - "delivering", - "arrived" + "oneOf": [ + { + "const": "none" + }, + { + "const": "underway" + }, + { + "const": "preparing" + }, + { + "const": "manufacturing" + }, + { + "const": "shipping" + }, + { + "const": "delivering" + }, + { + "const": "arrived" + } ], "description": "State of delivery\n\n- `none`: No delivery or journey record\n- `underway`: Some pieces are over preparing, but others are not\n- `preparing`: At least preparing\n- `manufacturing`: At least manufacturing\n- `shipping`: At least shipping\n- `delivering`: At least delivering\n- `arrived`: Every pieces are arrived" }, @@ -11921,13 +12160,18 @@ "description": "Zip code, or postal code." }, "special_note": { - "type": "string", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], "title": "Special description if required", "description": "Special description if required." } }, - "nullable": false, "required": [ "id", "created_at", @@ -11954,40 +12198,103 @@ "sort": { "type": "array", "items": { - "type": "string", - "enum": [ - "-seller.created_at", - "-seller.goods.payments.real", - "-seller.goods.publish_count", - "-seller.reviews.average", - "-seller.reviews.count", - "-goods.publish_count", - "-goods.payments.real", - "-reviews.average", - "-reviews.count", - "-sale.created_at", - "-sale.updated_at", - "-sale.opened_at", - "-sale.closed_at", - "-sale.content.title", - "-sale.price_range.lowest.real", - "-sale.price_range.highest.real", - "+seller.created_at", - "+seller.goods.payments.real", - "+seller.goods.publish_count", - "+seller.reviews.average", - "+seller.reviews.count", - "+goods.publish_count", - "+goods.payments.real", - "+reviews.average", - "+reviews.count", - "+sale.created_at", - "+sale.updated_at", - "+sale.opened_at", - "+sale.closed_at", - "+sale.content.title", - "+sale.price_range.lowest.real", - "+sale.price_range.highest.real" + "oneOf": [ + { + "const": "-seller.created_at" + }, + { + "const": "-seller.goods.payments.real" + }, + { + "const": "-seller.goods.publish_count" + }, + { + "const": "-seller.reviews.average" + }, + { + "const": "-seller.reviews.count" + }, + { + "const": "-goods.publish_count" + }, + { + "const": "-goods.payments.real" + }, + { + "const": "-reviews.average" + }, + { + "const": "-reviews.count" + }, + { + "const": "-sale.created_at" + }, + { + "const": "-sale.updated_at" + }, + { + "const": "-sale.opened_at" + }, + { + "const": "-sale.closed_at" + }, + { + "const": "-sale.content.title" + }, + { + "const": "-sale.price_range.lowest.real" + }, + { + "const": "-sale.price_range.highest.real" + }, + { + "const": "+seller.created_at" + }, + { + "const": "+seller.goods.payments.real" + }, + { + "const": "+seller.goods.publish_count" + }, + { + "const": "+seller.reviews.average" + }, + { + "const": "+seller.reviews.count" + }, + { + "const": "+goods.publish_count" + }, + { + "const": "+goods.payments.real" + }, + { + "const": "+reviews.average" + }, + { + "const": "+reviews.count" + }, + { + "const": "+sale.created_at" + }, + { + "const": "+sale.updated_at" + }, + { + "const": "+sale.opened_at" + }, + { + "const": "+sale.closed_at" + }, + { + "const": "+sale.content.title" + }, + { + "const": "+sale.price_range.lowest.real" + }, + { + "const": "+sale.price_range.highest.real" + } ] }, "title": "Sorting conditions", @@ -12000,12 +12307,10 @@ }, "limit": { "type": "integer", - "default": 100, "title": "Limitation of records per a page", "description": "Limitation of records per a page." } }, - "nullable": false, "description": "Request of summarized sales with pagination and searching/sorting options." }, "IPageIShoppingSale.ISummary": { @@ -12025,7 +12330,6 @@ "description": "List of records." } }, - "nullable": false, "required": [ "pagination", "data" @@ -12106,35 +12410,58 @@ "description": "Last updated time of the record.\n\nIn another words, creation time of the last snapshot." }, "paused_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Paused time of the sale", "description": "Paused time of the sale.\n\nThe sale is paused by the seller, for some reason.\n\n{@link IShoppingCustomer Customers} can still see the sale on the\nboth list and detail pages, but the sale has a warning label\n\"The sale is paused by the seller\"." }, "suspended_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "title": "Suspended time of the sale", - "description": "Suspended time of the sale.\n\nThe sale is suspended by the seller, for some reason.\n\n{@link IShoppingCustomer Customers} cannot see the sale on the\nboth list and detail pages. It is almost same with soft delettion,\nbut there's a little bit difference that the owner\n{@link IShoppingSeller seller} can still see the sale and resume it.\n\nOf course, the {@link IShoppingCustomer customers} who have\nalready purchased the sale can still see the sale on the\n{@link IShoppingOrder order} page." - }, - "opened_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "title": "Opening time of the sale", + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], + "title": "Suspended time of the sale", + "description": "Suspended time of the sale.\n\nThe sale is suspended by the seller, for some reason.\n\n{@link IShoppingCustomer Customers} cannot see the sale on the\nboth list and detail pages. It is almost same with soft delettion,\nbut there's a little bit difference that the owner\n{@link IShoppingSeller seller} can still see the sale and resume it.\n\nOf course, the {@link IShoppingCustomer customers} who have\nalready purchased the sale can still see the sale on the\n{@link IShoppingOrder order} page." + }, + "opened_at": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], + "title": "Opening time of the sale", "description": "Opening time of the sale." }, "closed_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Closing time of the sale", "description": "Closing time of the sale.\n\nIf this value is `null`, the sale be continued forever." } }, - "nullable": false, "required": [ "section", "seller", @@ -12195,7 +12522,6 @@ "description": "List of thumbnails." } }, - "nullable": false, "required": [ "id", "title", @@ -12207,11 +12533,16 @@ "description": "Content information of sale snapshot.\n\n`IShoppingSaleContent` is an entity embodies the description contents\nof {@link IShoppingSale }." }, "IShoppingSaleContent.Type": { - "type": "string", - "enum": [ - "html", - "md", - "txt" + "oneOf": [ + { + "const": "html" + }, + { + "const": "md" + }, + { + "const": "txt" + } ] }, "IShoppingSaleUnit": { @@ -12256,7 +12587,6 @@ "description": "Whether the unit is required or not.\n\nWhen the unit is required, the customer must select the unit. If do not\nselect, customer can't buy it.\n\nFor example, if there's a sale \"Macbook Set\" and one of the unit is the\n\"Main Body\", is it possible to buy the \"Macbook Set\" without the\n\"Main Body\" unit? This property is for that case." } }, - "nullable": false, "required": [ "options", "stocks", @@ -12297,10 +12627,7 @@ "description": "Primary Key." }, "type": { - "type": "string", - "enum": [ - "select" - ], + "const": "select", "title": "Discriminant for the type of selectable option", "description": "Discriminant for the type of selectable option." }, @@ -12315,7 +12642,6 @@ "description": "Whether the option is variable or not.\n\nWhen type of current option is \"select\", this attribute means whether\nselecting different candidate value affects the final stock or not." } }, - "nullable": false, "required": [ "candidates", "id", @@ -12325,28 +12651,6 @@ ], "description": "Individual option information on units for sale.\n\n`IShoppingSaleUnitSelectableOption` is a subsidiary entity of\n{@link IShoppingSaleUnit } that represents individual products in the\n{@link IShoppingSale sale}, and is an entity designed to represent individual\nselectable option information for the unit.\n\n- Examples of Options\n - selectable options\n - Computer: CPU, RAM, SSD, etc.\n - Clothes: size, color, style, etc.\n - descriptive options\n - Engrave\n - Simple question\n\nIf the {@link variable } property value is `true`, the final stock that the\n{@link IShoppingCustomer customer} will purchase changes depending on the\nselection of the {@link IShoppingSaleUnitOptionCandidate candidate value}.\n\nConversely, if it is a type other than \"select\", or if the {@link variable }\nproperty value is \"false\", , this is an option that has no meaning beyond\nsimple information transfer. Therefore, no matter what value the customer\nchooses when purchasing it, the option in this case does not affect the\n{@link IShoppingSaleUnitStock final stock}." }, - "IShoppingSaleUnitOptionCandidate": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid", - "title": "Primary Key", - "description": "Primary Key." - }, - "name": { - "type": "string", - "title": "Represents the name of the candidate value", - "description": "Represents the name of the candidate value." - } - }, - "nullable": false, - "required": [ - "id", - "name" - ], - "description": "Selectable candidate values within an option.\n\n`IShoppingSaleUnitOptionCandidate` is an entity that represents individual\ncandidate values that can be selected from\n{@link IShoppingSaleUnitSelectableOption options of the \"select\" type}.\n\n- Example\n - RAM: 8GB, 16GB, 32GB\n - GPU: RTX 3060, RTX 4080, TESLA\n - License: Private, Commercial, Educatiion\n\nBy the way, if belonged option is not \"select\" type, this entity never\nbeing used." - }, "IShoppingSaleUnitStock": { "type": "object", "properties": { @@ -12380,7 +12684,6 @@ "description": "List of choices.\n\nWhich candidate values being chosen for each option." } }, - "nullable": false, "required": [ "id", "name", @@ -12402,17 +12705,14 @@ "option_id": { "type": "string", "format": "uuid", - "title": "Target option's {@link IShoppingSaleUnitOption", "description": "Target option's {@link IShoppingSaleUnitOption.id }" }, "candidate_id": { "type": "string", "format": "uuid", - "title": "Target candidate's {@link IShoppingSaleUnitOptionCandidate", "description": "Target candidate's {@link IShoppingSaleUnitOptionCandidate.id }" } }, - "nullable": false, "required": [ "id", "option_id", @@ -12429,10 +12729,13 @@ "sort": { "type": "array", "items": { - "type": "string", - "enum": [ - "-created_at", - "+created_at" + "oneOf": [ + { + "const": "-created_at" + }, + { + "const": "+created_at" + } ] } }, @@ -12443,12 +12746,10 @@ }, "limit": { "type": "integer", - "default": 100, "title": "Limitation of records per a page", "description": "Limitation of records per a page." } }, - "nullable": false, "description": "Request of the comments with pagination and searching/sorting options." }, "IShoppingSaleInquiryComment.IRequest.ISearch": { @@ -12463,8 +12764,7 @@ "body": { "type": "string" } - }, - "nullable": false + } }, "IPageIShoppingSaleInquiryComment": { "type": "object", @@ -12483,7 +12783,6 @@ "description": "List of records." } }, - "nullable": false, "required": [ "pagination", "data" @@ -12505,9 +12804,15 @@ "description": "Primary Key." }, "parent_id": { - "type": "string", - "format": "uuid", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "uuid" + } + ], "title": "Parent comment's ID", "description": "Parent comment's ID." }, @@ -12527,7 +12832,6 @@ "description": "Creation time of comment." } }, - "nullable": false, "required": [ "writer", "id", @@ -12584,7 +12888,6 @@ "description": "List of attachment files." } }, - "nullable": false, "required": [ "id", "created_at", @@ -12595,11 +12898,16 @@ "description": "Snapshot of comment.\n\n`IBbsArticleComment.ISnapshot` is a snapshot entity that contains\nthe contents of the comment.\n\nAs mentioned in {@link IBbsArticleComment }, designed to keep evidence\nand prevent fraud." }, "IBbsArticleComment.Format": { - "type": "string", - "enum": [ - "html", - "md", - "txt" + "oneOf": [ + { + "const": "html" + }, + { + "const": "md" + }, + { + "const": "txt" + } ] }, "IAttachmentFile.ICreate": { @@ -12612,10 +12920,16 @@ "description": "File name, except extension.\n\nIf there's file `.gitignore`, then its name is an empty string." }, "extension": { - "type": "string", - "maxLength": 8, - "minLength": 1, - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "minLength": 1, + "maxLength": 8 + } + ], "title": "Extension", "description": "Extension.\n\nPossible to omit like `README` case." }, @@ -12626,7 +12940,6 @@ "description": "URL path of the real file." } }, - "nullable": false, "required": [ "name", "extension", @@ -12637,11 +12950,16 @@ "type": "object", "properties": { "format": { - "type": "string", - "enum": [ - "html", - "md", - "txt" + "oneOf": [ + { + "const": "html" + }, + { + "const": "md" + }, + { + "const": "txt" + } ], "title": "Format of body", "description": "Format of body.\n\nSame meaning with extension like `html`, `md`, `txt`." @@ -12660,7 +12978,6 @@ "description": "List of attachment files." } }, - "nullable": false, "required": [ "format", "body", @@ -12702,7 +13019,6 @@ "description": "List of attachment files." } }, - "nullable": false, "required": [ "id", "created_at", @@ -12723,18 +13039,37 @@ "sort": { "type": "array", "items": { - "type": "string", - "enum": [ - "-created_at", - "+created_at", - "-nickname", - "-answered_at", - "-title", - "-updated_at", - "+nickname", - "+answered_at", - "+title", - "+updated_at" + "oneOf": [ + { + "const": "-created_at" + }, + { + "const": "+created_at" + }, + { + "const": "-nickname" + }, + { + "const": "-answered_at" + }, + { + "const": "-title" + }, + { + "const": "-updated_at" + }, + { + "const": "+nickname" + }, + { + "const": "+answered_at" + }, + { + "const": "+title" + }, + { + "const": "+updated_at" + } ] }, "title": "Sorting conditions", @@ -12747,12 +13082,10 @@ }, "limit": { "type": "integer", - "default": 100, "title": "Limitation of records per a page", "description": "Limitation of records per a page." } }, - "nullable": false, "description": "Request of summarized informations with pagination searching/sorting options." }, "IShoppingSaleInquiry.IRequest.ISearch": { @@ -12765,8 +13098,14 @@ "type": "string" }, "answered": { - "type": "boolean", - "nullable": true + "oneOf": [ + { + "type": "null" + }, + { + "type": "boolean" + } + ] }, "title": { "type": "string" @@ -12785,8 +13124,7 @@ "type": "string", "format": "date-time" } - }, - "nullable": false + } }, "IPageIShoppingSaleQuestion.ISummary": { "type": "object", @@ -12805,7 +13143,6 @@ "description": "List of records." } }, - "nullable": false, "required": [ "pagination", "data" @@ -12826,7 +13163,14 @@ "description": "Customer who wrote the inquiry." }, "answer": { - "$ref": "#/components/schemas/IShoppingSaleInquiryAnswer.ISummary.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingSaleInquiryAnswer.ISummary" + } + ], "title": "Formal answer for the inquiry by the seller", "description": "Formal answer for the inquiry by the seller." }, @@ -12859,7 +13203,6 @@ "description": "Modification time of the article.\n\nIn other words, the time when the last snapshot was created." } }, - "nullable": false, "required": [ "secret", "customer", @@ -12872,7 +13215,7 @@ ], "description": "Summarized information of the question." }, - "IShoppingSaleInquiryAnswer.ISummary.Nullable": { + "IShoppingSaleInquiryAnswer.ISummary": { "type": "object", "properties": { "seller": { @@ -12902,7 +13245,6 @@ "description": "Modification time of the article.\n\nIn other words, the time when the last snapshot was created." } }, - "nullable": true, "required": [ "seller", "id", @@ -12928,7 +13270,6 @@ "description": "List of records." } }, - "nullable": false, "required": [ "pagination", "data" @@ -12949,7 +13290,14 @@ "description": "Customer who wrote the inquiry." }, "answer": { - "$ref": "#/components/schemas/IShoppingSaleInquiryAnswer.IAbridge.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingSaleInquiryAnswer.IAbridge" + } + ], "title": "Formal answer for the inquiry by the seller", "description": "Formal answer for the inquiry by the seller." }, @@ -13000,7 +13348,6 @@ "description": "List of attachment files." } }, - "nullable": false, "required": [ "secret", "customer", @@ -13016,7 +13363,7 @@ ], "description": "Abridged information of the question." }, - "IShoppingSaleInquiryAnswer.IAbridge.Nullable": { + "IShoppingSaleInquiryAnswer.IAbridge": { "type": "object", "properties": { "seller": { @@ -13064,7 +13411,6 @@ "description": "List of attachment files." } }, - "nullable": true, "required": [ "seller", "id", @@ -13077,11 +13423,16 @@ ] }, "IBbsArticle.Format": { - "type": "string", - "enum": [ - "html", - "md", - "txt" + "oneOf": [ + { + "const": "html" + }, + { + "const": "md" + }, + { + "const": "txt" + } ] }, "IShoppingSaleQuestion": { @@ -13093,10 +13444,7 @@ "description": "Whether the question article is secret or not.\n\nIf secret article, only the writer customer and related seller can see\nthe detailed content." }, "type": { - "type": "string", - "enum": [ - "question" - ], + "const": "question", "title": "Type of the derived inquiry", "description": "Type of the derived inquiry.\n\n- `question`: {@link IShoppingSaleQuestion }\n- `review`: {@link IShoppingSaleReview }" }, @@ -13106,7 +13454,14 @@ "description": "Customer who wrote the inquiry." }, "answer": { - "$ref": "#/components/schemas/IShoppingSaleInquiryAnswer.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingSaleInquiryAnswer" + } + ], "title": "Formal answer for the inquiry by the seller", "description": "Formal answer for the inquiry by the seller." }, @@ -13137,7 +13492,6 @@ "description": "Creation time of article." } }, - "nullable": false, "required": [ "secret", "type", @@ -13150,7 +13504,7 @@ ], "description": "Question about sale snapshot.\n\n`IShoppingSaleQuestion` is a subtype entity of {@link IShoppingSaleInquiry },\nand is used when a {@link IShoppingCustomer customer} wants to ask something\nabout a {@link IShoppingSale sale} ({@link IShoppingSaleSnapshot snapshot} at\nthe time) registered by the {@link IShoppingSeller seller}.\n\nAnd, like most shopping malls, `IShoppingSaleQuestion` also provides\na {@link secret } attribute, allowing you to create a \"secret message\" that can\nonly be viewed by the seller and the customer who wrote the question." }, - "IShoppingSaleInquiryAnswer.Nullable": { + "IShoppingSaleInquiryAnswer": { "type": "object", "properties": { "seller": { @@ -13180,7 +13534,6 @@ "description": "Creation time of article." } }, - "nullable": true, "required": [ "seller", "id", @@ -13227,7 +13580,6 @@ "description": "List of attachment files." } }, - "nullable": false, "required": [ "id", "created_at", @@ -13249,20 +13601,43 @@ "sort": { "type": "array", "items": { - "type": "string", - "enum": [ - "-created_at", - "+created_at", - "-nickname", - "-answered_at", - "-title", - "-updated_at", - "+nickname", - "+answered_at", - "+title", - "+updated_at", - "-score", - "+score" + "oneOf": [ + { + "const": "-created_at" + }, + { + "const": "+created_at" + }, + { + "const": "-nickname" + }, + { + "const": "-answered_at" + }, + { + "const": "-title" + }, + { + "const": "-updated_at" + }, + { + "const": "+nickname" + }, + { + "const": "+answered_at" + }, + { + "const": "+title" + }, + { + "const": "+updated_at" + }, + { + "const": "-score" + }, + { + "const": "+score" + } ] }, "title": "Sorting conditions", @@ -13275,12 +13650,10 @@ }, "limit": { "type": "integer", - "default": 100, "title": "Limitation of records per a page", "description": "Limitation of records per a page." } }, - "nullable": false, "description": "Request of summarized informations with pagination searching/sorting options." }, "IShoppingSaleReview.IRequest.ISearch": { @@ -13293,8 +13666,14 @@ "type": "string" }, "answered": { - "type": "boolean", - "nullable": true + "oneOf": [ + { + "type": "null" + }, + { + "type": "boolean" + } + ] }, "title": { "type": "string" @@ -13315,16 +13694,15 @@ }, "minimum": { "type": "number", - "maximum": 100, - "minimum": 0 + "minimum": 0, + "maximum": 100 }, "maximum": { "type": "number", - "maximum": 100, - "minimum": 0 + "minimum": 0, + "maximum": 100 } - }, - "nullable": false + } }, "IPageIShoppingSaleReview.ISummary": { "type": "object", @@ -13343,7 +13721,6 @@ "description": "List of records." } }, - "nullable": false, "required": [ "pagination", "data" @@ -13364,7 +13741,14 @@ "description": "Customer who wrote the inquiry." }, "answer": { - "$ref": "#/components/schemas/IShoppingSaleInquiryAnswer.ISummary.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingSaleInquiryAnswer.ISummary" + } + ], "title": "Formal answer for the inquiry by the seller", "description": "Formal answer for the inquiry by the seller." }, @@ -13397,7 +13781,6 @@ "description": "Modification time of the article.\n\nIn other words, the time when the last snapshot was created." } }, - "nullable": false, "required": [ "score", "customer", @@ -13427,7 +13810,6 @@ "description": "List of records." } }, - "nullable": false, "required": [ "pagination", "data" @@ -13439,8 +13821,8 @@ "properties": { "score": { "type": "number", - "maximum": 100, "minimum": 0, + "maximum": 100, "title": "Score of the review", "description": "Score of the review." }, @@ -13450,7 +13832,14 @@ "description": "Customer who wrote the inquiry." }, "answer": { - "$ref": "#/components/schemas/IShoppingSaleInquiryAnswer.IAbridge.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingSaleInquiryAnswer.IAbridge" + } + ], "title": "Formal answer for the inquiry by the seller", "description": "Formal answer for the inquiry by the seller." }, @@ -13501,7 +13890,6 @@ "description": "List of attachment files." } }, - "nullable": false, "required": [ "score", "customer", @@ -13521,10 +13909,7 @@ "type": "object", "properties": { "type": { - "type": "string", - "enum": [ - "review" - ], + "const": "review", "title": "Type of the derived inquiry", "description": "Type of the derived inquiry.\n\n- `question`: {@link IShoppingSaleQuestion }\n- `review`: {@link IShoppingSaleReview }" }, @@ -13534,7 +13919,14 @@ "description": "Customer who wrote the inquiry." }, "answer": { - "$ref": "#/components/schemas/IShoppingSaleInquiryAnswer.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingSaleInquiryAnswer" + } + ], "title": "Formal answer for the inquiry by the seller", "description": "Formal answer for the inquiry by the seller." }, @@ -13565,7 +13957,6 @@ "description": "Creation time of article." } }, - "nullable": false, "required": [ "type", "customer", @@ -13582,8 +13973,8 @@ "properties": { "score": { "type": "number", - "maximum": 100, "minimum": 0, + "maximum": 100, "title": "Score of the review", "description": "Score of the review." }, @@ -13622,7 +14013,6 @@ "description": "List of attachment files." } }, - "nullable": false, "required": [ "score", "id", @@ -13644,12 +14034,10 @@ }, "limit": { "type": "integer", - "default": 100, "title": "Limitation of records per a page", "description": "Limitation of records per a page." } }, - "nullable": false, "description": "Page request data" }, "IPageIShoppingSaleSnapshot.ISummary": { @@ -13669,7 +14057,6 @@ "description": "List of records." } }, - "nullable": false, "required": [ "pagination", "data" @@ -13733,7 +14120,6 @@ "description": "List of units.\n\nRecords about individual product composition informations that are sold\nin the sale. Each {@link IShoppingSaleUnit unit} record has configurable\n{@link IShoppingSaleUnitOption options},\n{@link IShoppingSaleUnitOptionCandidate candidate} values for each\noption, and {@link IShoppingSaleUnitStock final stocks} determined by\nselecting every candidate values of each option." } }, - "nullable": false, "required": [ "price_range", "id", @@ -13798,7 +14184,6 @@ "description": "List of units.\n\nRecords about individual product composition informations that are sold\nin the sale. Each {@link IShoppingSaleUnit unit} record has configurable\n{@link IShoppingSaleUnitOption options},\n{@link IShoppingSaleUnitOptionCandidate candidate} values for each\noption, and {@link IShoppingSaleUnitStock final stocks} determined by\nselecting every candidate values of each option." } }, - "nullable": false, "required": [ "id", "snapshot_id", @@ -13814,9 +14199,15 @@ "type": "object", "properties": { "parent_id": { - "type": "string", - "format": "uuid", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "uuid" + } + ], "title": "Parent category's ID", "description": "Parent category's ID." }, @@ -13826,7 +14217,6 @@ "description": "Representative name of the category.\n\nThe name must be unique within the parent category. If no parent exists,\nthen the name must be unique within the channel between no parent\ncategories." } }, - "nullable": false, "required": [ "parent_id", "name" @@ -13837,7 +14227,14 @@ "type": "object", "properties": { "parent": { - "$ref": "#/components/schemas/IShoppingChannelCategory.IInvert.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingChannelCategory.IInvert" + } + ], "title": "Parent category info", "description": "Parent category info." }, @@ -13856,9 +14253,15 @@ "description": "Primary Key." }, "parent_id": { - "type": "string", - "format": "uuid", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "uuid" + } + ], "title": "Parent category's ID", "description": "Parent category's ID." }, @@ -13874,7 +14277,6 @@ "description": "Creation time of record." } }, - "nullable": false, "required": [ "parent", "children", @@ -13903,15 +14305,21 @@ "description": "Primary Key." }, "parent_id": { - "type": "string", - "format": "uuid", - "nullable": true, - "title": "Parent category's ID", - "description": "Parent category's ID." - }, - "name": { - "type": "string", - "title": "Representative name of the category", + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "uuid" + } + ], + "title": "Parent category's ID", + "description": "Parent category's ID." + }, + "name": { + "type": "string", + "title": "Representative name of the category", "description": "Representative name of the category.\n\nThe name must be unique within the parent category. If no parent exists,\nthen the name must be unique within the channel between no parent\ncategories." }, "created_at": { @@ -13921,7 +14329,6 @@ "description": "Creation time of record." } }, - "nullable": false, "required": [ "children", "id", @@ -13950,7 +14357,6 @@ "description": "To be absorbed to {@link keep } after merging." } }, - "nullable": false, "required": [ "keep", "absorbed" @@ -13971,7 +14377,6 @@ "description": "Name of the channel." } }, - "nullable": false, "required": [ "code", "name" @@ -13987,7 +14392,6 @@ "description": "Name of the channel." } }, - "nullable": false, "required": [ "name" ], @@ -14002,14 +14406,25 @@ "sort": { "type": "array", "items": { - "type": "string", - "enum": [ - "-channel.code", - "-channel.name", - "-channel.created_at", - "+channel.code", - "+channel.name", - "+channel.created_at" + "oneOf": [ + { + "const": "-channel.code" + }, + { + "const": "-channel.name" + }, + { + "const": "-channel.created_at" + }, + { + "const": "+channel.code" + }, + { + "const": "+channel.name" + }, + { + "const": "+channel.created_at" + } ] } }, @@ -14020,12 +14435,10 @@ }, "limit": { "type": "integer", - "default": 100, "title": "Limitation of records per a page", "description": "Limitation of records per a page." } }, - "nullable": false, "description": "Request of the channels with pagination and searching/sorting options." }, "IShoppingChannel.IRequest.ISearch": { @@ -14037,8 +14450,7 @@ "name": { "type": "string" } - }, - "nullable": false + } }, "IPageIShoppingChannel": { "type": "object", @@ -14057,7 +14469,6 @@ "description": "List of records." } }, - "nullable": false, "required": [ "pagination", "data" @@ -14081,7 +14492,6 @@ "description": "List of records." } }, - "nullable": false, "required": [ "pagination", "data" @@ -14122,7 +14532,6 @@ "description": "Name of the channel." } }, - "nullable": false, "required": [ "categories", "id", @@ -14146,7 +14555,6 @@ "description": "Representative name of the section." } }, - "nullable": false, "required": [ "code", "name" @@ -14162,7 +14570,6 @@ "description": "Representative name of the section." } }, - "nullable": false, "required": [ "name" ], @@ -14177,14 +14584,25 @@ "sort": { "type": "array", "items": { - "type": "string", - "enum": [ - "-section.code", - "-section.name", - "-section.created_at", - "+section.code", - "+section.name", - "+section.created_at" + "oneOf": [ + { + "const": "-section.code" + }, + { + "const": "-section.name" + }, + { + "const": "-section.created_at" + }, + { + "const": "+section.code" + }, + { + "const": "+section.name" + }, + { + "const": "+section.created_at" + } ] } }, @@ -14195,12 +14613,10 @@ }, "limit": { "type": "integer", - "default": 100, "title": "Limitation of records per a page", "description": "Limitation of records per a page." } }, - "nullable": false, "description": "Request of the sections with pagination and searching/sorting options." }, "IShoppingSection.IRequest.ISearch": { @@ -14212,8 +14628,7 @@ "name": { "type": "string" } - }, - "nullable": false + } }, "IPageIShoppingSection": { "type": "object", @@ -14232,7 +14647,6 @@ "description": "List of records." } }, - "nullable": false, "required": [ "pagination", "data" @@ -14246,7 +14660,6 @@ "type": "string" } }, - "nullable": false, "required": [ "value" ] @@ -14261,7 +14674,6 @@ "type": "string" } }, - "nullable": false, "required": [ "Authorization" ] @@ -14270,20 +14682,31 @@ "$ref": "#/components/schemas/IShoppingCustomer.IToken" }, "type": { - "type": "string", - "enum": [ - "customer" - ], + "const": "customer", "title": "Discriminant for the type of customer", "description": "Discriminant for the type of customer." }, "member": { - "$ref": "#/components/schemas/IShoppingMember.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingMember" + } + ], "title": "Membership information", "description": "Membership information.\n\nIf the customer has joined as a member." }, "citizen": { - "$ref": "#/components/schemas/IShoppingCitizen.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingCitizen" + } + ], "title": "Citizen information", "description": "Citizen information.\n\nIf the customer has verified his real name and mobile number." }, @@ -14299,7 +14722,14 @@ "description": "Belonged channel." }, "external_user": { - "$ref": "#/components/schemas/IShoppingExternalUser.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingExternalUser" + } + ], "title": "External user information", "description": "External user information.\n\nWhen the customer has come frome an external service." }, @@ -14310,9 +14740,15 @@ "description": "Connection address.\n\nSame with {@link window.location.href } of client." }, "referrer": { - "type": "string", - "format": "uri", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "uri" + } + ], "title": "Referrer address", "description": "Referrer address.\n\nSame with {@link window.document.referrer } of client." }, @@ -14337,7 +14773,6 @@ "description": "Creation time of the connection record." } }, - "nullable": false, "required": [ "setHeaders", "token", @@ -14371,7 +14806,6 @@ "format": "date-time" } }, - "nullable": false, "required": [ "access", "refresh", @@ -14386,16 +14820,29 @@ "type": "string" }, "external_user": { - "$ref": "#/components/schemas/IShoppingExternalUser.ICreate.Nullable" + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingExternalUser.ICreate" + } + ] }, "href": { "type": "string", "format": "uri" }, "referrer": { - "type": "string", - "format": "uri", - "nullable": true + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "uri" + } + ] }, "ip": { "oneOf": [ @@ -14410,7 +14857,6 @@ ] } }, - "nullable": false, "required": [ "channel_code", "external_user", @@ -14418,11 +14864,18 @@ "referrer" ] }, - "IShoppingExternalUser.ICreate.Nullable": { + "IShoppingExternalUser.ICreate": { "type": "object", "properties": { "citizen": { - "$ref": "#/components/schemas/IShoppingCitizen.ICreate.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingCitizen.ICreate" + } + ], "title": "Citizen activation info", "description": "Citizen activation info." }, @@ -14450,7 +14903,6 @@ "description": "Additional information about external user from the external\nsystem." } }, - "nullable": true, "required": [ "citizen", "application", @@ -14461,7 +14913,7 @@ ], "description": "Creation information of external user." }, - "IShoppingCitizen.ICreate.Nullable": { + "IShoppingCitizen.ICreate": { "type": "object", "properties": { "mobile": { @@ -14476,7 +14928,6 @@ "description": "Real name, or equivalent nickname." } }, - "nullable": true, "required": [ "mobile", "name" @@ -14492,7 +14943,14 @@ "description": "Nickname that uniquely identifies the member." }, "citizen": { - "$ref": "#/components/schemas/IShoppingCitizen.ICreate.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingCitizen.ICreate" + } + ], "title": "Citizen information", "description": "Citizen information." }, @@ -14508,7 +14966,6 @@ "description": "Password of the member account." } }, - "nullable": false, "required": [ "nickname", "citizen", @@ -14517,71 +14974,6 @@ ], "description": "Joining request info." }, - "IShoppingCitizen.ICreate": { - "type": "object", - "properties": { - "mobile": { - "type": "string", - "pattern": "^[0-9]*$", - "title": "Mobile number", - "description": "Mobile number." - }, - "name": { - "type": "string", - "title": "Real name, or equivalent nickname", - "description": "Real name, or equivalent nickname." - } - }, - "nullable": false, - "required": [ - "mobile", - "name" - ], - "description": "Creation information of citizen verification." - }, - "IShoppingExternalUser.ICreate": { - "type": "object", - "properties": { - "citizen": { - "$ref": "#/components/schemas/IShoppingCitizen.ICreate.Nullable", - "title": "Citizen activation info", - "description": "Citizen activation info." - }, - "application": { - "type": "string", - "title": "Identifier code of the external service", - "description": "Identifier code of the external service.\n\nIt can be same with {@link IShoppingChannel.code } in common." - }, - "uid": { - "type": "string", - "title": "Identifier key of external user from the external system", - "description": "Identifier key of external user from the external system." - }, - "nickname": { - "type": "string", - "title": "Nickname of external user in the external system", - "description": "Nickname of external user in the external system." - }, - "password": { - "type": "string", - "title": "Password of external user from the external system", - "description": "Password of external user from the external system.\n\nThis is a password issued to the user by an external service,\nand is by no means the actual user password. However, for\n{@link IShoppingCustomer customers} who entered the same\napplication and code as the current external system user, this is\nto determine whether to view this as a correct external system\nuser or a violation." - }, - "data": { - "description": "Additional information about external user from the external\nsystem." - } - }, - "nullable": false, - "required": [ - "citizen", - "application", - "uid", - "nickname", - "password", - "data" - ], - "description": "Creation information of external user." - }, "IShoppingMember.IPasswordChange": { "type": "object", "properties": { @@ -14596,7 +14988,6 @@ "description": "The new password to change." } }, - "nullable": false, "required": [ "oldbie", "newbie" @@ -14609,12 +15000,19 @@ "sort": { "type": "array", "items": { - "type": "string", - "enum": [ - "-ticket.created_at", - "-ticket.expired_at", - "+ticket.created_at", - "+ticket.expired_at" + "oneOf": [ + { + "const": "-ticket.created_at" + }, + { + "const": "-ticket.expired_at" + }, + { + "const": "+ticket.created_at" + }, + { + "const": "+ticket.expired_at" + } ] } }, @@ -14625,12 +15023,10 @@ }, "limit": { "type": "integer", - "default": 100, "title": "Limitation of records per a page", "description": "Limitation of records per a page." } - }, - "nullable": false + } }, "IPageIShoppingCouponTicket": { "type": "object", @@ -14649,7 +15045,6 @@ "description": "List of records." } }, - "nullable": false, "required": [ "pagination", "data" @@ -14664,7 +15059,6 @@ "format": "uuid" } }, - "nullable": false, "required": [ "coupon_id" ] @@ -14678,16 +15072,31 @@ "sort": { "type": "array", "items": { - "type": "string", - "enum": [ - "-created_at", - "+created_at", - "-value", - "-publish.created_at", - "-publish.paid_at", - "+value", - "+publish.created_at", - "+publish.paid_at" + "oneOf": [ + { + "const": "-created_at" + }, + { + "const": "+created_at" + }, + { + "const": "-value" + }, + { + "const": "-publish.created_at" + }, + { + "const": "-publish.paid_at" + }, + { + "const": "+value" + }, + { + "const": "+publish.created_at" + }, + { + "const": "+publish.paid_at" + } ] } }, @@ -14698,12 +15107,10 @@ }, "limit": { "type": "integer", - "default": 100, "title": "Limitation of records per a page", "description": "Limitation of records per a page." } - }, - "nullable": false + } }, "IShoppingDepositCharge.IRequest.ISearch": { "type": "object", @@ -14723,12 +15130,19 @@ "type": "number" }, "state": { - "type": "string", - "enum": [ - "pending", - "published", - "payed", - "cancelled" + "oneOf": [ + { + "const": "pending" + }, + { + "const": "published" + }, + { + "const": "payed" + }, + { + "const": "cancelled" + } ] }, "publish": { @@ -14753,14 +15167,11 @@ "type": "string", "format": "date-time" } - }, - "nullable": false + } } - }, - "nullable": false + } } }, - "nullable": false, "required": [ "state" ] @@ -14782,7 +15193,6 @@ "description": "List of records." } }, - "nullable": false, "required": [ "pagination", "data" @@ -14800,7 +15210,14 @@ "$ref": "#/components/schemas/IShoppingCustomer" }, "publish": { - "$ref": "#/components/schemas/IShoppingDepositChargePublish.Nullable" + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingDepositChargePublish" + } + ] }, "created_at": { "type": "string", @@ -14810,7 +15227,6 @@ "type": "number" } }, - "nullable": false, "required": [ "id", "customer", @@ -14819,7 +15235,7 @@ "value" ] }, - "IShoppingDepositChargePublish.Nullable": { + "IShoppingDepositChargePublish": { "type": "object", "properties": { "id": { @@ -14831,17 +15247,28 @@ "format": "date-time" }, "paid_at": { - "type": "string", - "format": "date-time", - "nullable": true + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ] }, "cancelled_at": { - "type": "string", - "format": "date-time", - "nullable": true + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ] } }, - "nullable": true, "required": [ "id", "created_at", @@ -14856,7 +15283,6 @@ "type": "number" } }, - "nullable": false, "required": [ "value" ] @@ -14871,42 +15297,11 @@ "type": "string" } }, - "nullable": false, "required": [ "vendor", "uid" ] }, - "IShoppingDepositChargePublish": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "paid_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "cancelled_at": { - "type": "string", - "format": "date-time", - "nullable": true - } - }, - "nullable": false, - "required": [ - "id", - "created_at", - "paid_at", - "cancelled_at" - ] - }, "IShoppingDepositHistory.IRequest": { "type": "object", "properties": { @@ -14916,18 +15311,37 @@ "sort": { "type": "array", "items": { - "type": "string", - "enum": [ - "-deposit.source", - "-deposit.code", - "-deposit.direction", - "+deposit.source", - "+deposit.code", - "+deposit.direction", - "-history.value", - "-history.created_at", - "+history.value", - "+history.created_at" + "oneOf": [ + { + "const": "-deposit.source" + }, + { + "const": "-deposit.code" + }, + { + "const": "-deposit.direction" + }, + { + "const": "+deposit.source" + }, + { + "const": "+deposit.code" + }, + { + "const": "+deposit.direction" + }, + { + "const": "-history.value" + }, + { + "const": "-history.created_at" + }, + { + "const": "+history.value" + }, + { + "const": "+history.created_at" + } ] } }, @@ -14938,12 +15352,10 @@ }, "limit": { "type": "integer", - "default": 100, "title": "Limitation of records per a page", "description": "Limitation of records per a page." } - }, - "nullable": false + } }, "IShoppingDepositHistory.IRequest.ISearch": { "type": "object", @@ -14971,8 +15383,7 @@ "type": "number", "minimum": 0 } - }, - "nullable": false + } }, "IPageIShoppingDepositHistory": { "type": "object", @@ -14991,7 +15402,6 @@ "description": "List of records." } }, - "nullable": false, "required": [ "pagination", "data" @@ -15026,7 +15436,6 @@ "format": "date-time" } }, - "nullable": false, "required": [ "id", "citizen", @@ -15046,18 +15455,37 @@ "sort": { "type": "array", "items": { - "type": "string", - "enum": [ - "-mileage.source", - "-mileage.code", - "-mileage.direction", - "+mileage.source", - "+mileage.code", - "+mileage.direction", - "-history.value", - "-history.created_at", - "+history.value", - "+history.created_at" + "oneOf": [ + { + "const": "-mileage.source" + }, + { + "const": "-mileage.code" + }, + { + "const": "-mileage.direction" + }, + { + "const": "+mileage.source" + }, + { + "const": "+mileage.code" + }, + { + "const": "+mileage.direction" + }, + { + "const": "-history.value" + }, + { + "const": "-history.created_at" + }, + { + "const": "+history.value" + }, + { + "const": "+history.created_at" + } ] } }, @@ -15068,12 +15496,10 @@ }, "limit": { "type": "integer", - "default": 100, "title": "Limitation of records per a page", "description": "Limitation of records per a page." } - }, - "nullable": false + } }, "IShoppingMileageHistory.IRequest.ISearch": { "type": "object", @@ -15101,8 +15527,7 @@ "type": "number", "minimum": 0 } - }, - "nullable": false + } }, "IPageIShoppingMileageHistory": { "type": "object", @@ -15121,7 +15546,6 @@ "description": "List of records." } }, - "nullable": false, "required": [ "pagination", "data" @@ -15156,7 +15580,6 @@ "format": "date-time" } }, - "nullable": false, "required": [ "id", "citizen", @@ -15176,48 +15599,127 @@ "sort": { "type": "array", "items": { - "type": "string", - "enum": [ - "-seller.created_at", - "-seller.goods.payments.real", - "-seller.goods.publish_count", - "-seller.reviews.average", - "-seller.reviews.count", - "-goods.publish_count", - "-goods.payments.real", - "-reviews.average", - "-reviews.count", - "-sale.created_at", - "-sale.updated_at", - "-sale.opened_at", - "-sale.closed_at", - "-sale.content.title", - "-sale.price_range.lowest.real", - "-sale.price_range.highest.real", - "+seller.created_at", - "+seller.goods.payments.real", - "+seller.goods.publish_count", - "+seller.reviews.average", - "+seller.reviews.count", - "+goods.publish_count", - "+goods.payments.real", - "+reviews.average", - "+reviews.count", - "+sale.created_at", - "+sale.updated_at", - "+sale.opened_at", - "+sale.closed_at", - "+sale.content.title", - "+sale.price_range.lowest.real", - "+sale.price_range.highest.real", - "-commodity.price", - "-commodity.volume", - "-commodity.volumed_price", - "-commodity.created_at", - "+commodity.price", - "+commodity.volume", - "+commodity.volumed_price", - "+commodity.created_at" + "oneOf": [ + { + "const": "-seller.created_at" + }, + { + "const": "-seller.goods.payments.real" + }, + { + "const": "-seller.goods.publish_count" + }, + { + "const": "-seller.reviews.average" + }, + { + "const": "-seller.reviews.count" + }, + { + "const": "-goods.publish_count" + }, + { + "const": "-goods.payments.real" + }, + { + "const": "-reviews.average" + }, + { + "const": "-reviews.count" + }, + { + "const": "-sale.created_at" + }, + { + "const": "-sale.updated_at" + }, + { + "const": "-sale.opened_at" + }, + { + "const": "-sale.closed_at" + }, + { + "const": "-sale.content.title" + }, + { + "const": "-sale.price_range.lowest.real" + }, + { + "const": "-sale.price_range.highest.real" + }, + { + "const": "+seller.created_at" + }, + { + "const": "+seller.goods.payments.real" + }, + { + "const": "+seller.goods.publish_count" + }, + { + "const": "+seller.reviews.average" + }, + { + "const": "+seller.reviews.count" + }, + { + "const": "+goods.publish_count" + }, + { + "const": "+goods.payments.real" + }, + { + "const": "+reviews.average" + }, + { + "const": "+reviews.count" + }, + { + "const": "+sale.created_at" + }, + { + "const": "+sale.updated_at" + }, + { + "const": "+sale.opened_at" + }, + { + "const": "+sale.closed_at" + }, + { + "const": "+sale.content.title" + }, + { + "const": "+sale.price_range.lowest.real" + }, + { + "const": "+sale.price_range.highest.real" + }, + { + "const": "-commodity.price" + }, + { + "const": "-commodity.volume" + }, + { + "const": "-commodity.volumed_price" + }, + { + "const": "-commodity.created_at" + }, + { + "const": "+commodity.price" + }, + { + "const": "+commodity.volume" + }, + { + "const": "+commodity.volumed_price" + }, + { + "const": "+commodity.created_at" + } ] } }, @@ -15228,12 +15730,10 @@ }, "limit": { "type": "integer", - "default": 100, "title": "Limitation of records per a page", "description": "Limitation of records per a page." } - }, - "nullable": false + } }, "IShoppingCartCommodity.IRequest.ISearch": { "type": "object", @@ -15253,8 +15753,7 @@ "sale": { "$ref": "#/components/schemas/IShoppingSale.IRequest.ISearch" } - }, - "nullable": false + } }, "IPageIShoppingCartCommodity": { "type": "object", @@ -15273,7 +15772,6 @@ "description": "List of records." } }, - "nullable": false, "required": [ "pagination", "data" @@ -15286,7 +15784,7 @@ "sale_id": { "type": "string", "format": "uuid", - "title": "Target sale's {@link IShoppingSale", + "title": "Target sale's {@link IShoppingSale.id }", "description": "Target sale's {@link IShoppingSale.id }." }, "stocks": { @@ -15305,13 +15803,11 @@ "description": "Volume of the commodity to purchase.\n\nA value indicating how many sets would be multiplied to the children\n{@link IShoppingSaleUnitStock.IInvert.quantity } values." }, "accumulate": { - "default": true, "type": "boolean", "title": "Whether to accumulate the volume or not", "description": "Whether to accumulate the volume or not.\n\nIf this attribute is not `false` and there's same commodity that\ncomposed with same stocks and options, then the volume will be\naccumulated to the existed one.\n\nOtherwise, duplicated commodity would be newly created." } }, - "nullable": false, "required": [ "sale_id", "stocks", @@ -15325,13 +15821,13 @@ "unit_id": { "type": "string", "format": "uuid", - "title": "Target unit's {@link IShoppingSaleUnit", + "title": "Target unit's {@link IShoppingSaleUnit.id }", "description": "Target unit's {@link IShoppingSaleUnit.id }." }, "stock_id": { "type": "string", "format": "uuid", - "title": "Target stock's {@link IShoppingSaleUnitStock", + "title": "Target stock's {@link IShoppingSaleUnitStock.id }", "description": "Target stock's {@link IShoppingSaleUnitStock.id }.\n\nIt must be matched with the {@link choices } property." }, "choices": { @@ -15349,7 +15845,6 @@ "description": "Quantity of the stock to purchase.\n\nThis value is multiplied by the {@link IShoppingCartCommodity.volume }." } }, - "nullable": false, "required": [ "unit_id", "stock_id", @@ -15364,36 +15859,41 @@ "option_id": { "type": "string", "format": "uuid", - "title": "Target option's {@link IShoppingSaleUnitOption", + "title": "Target option's {@link IShoppingSaleUnitOption.id }", "description": "Target option's {@link IShoppingSaleUnitOption.id }." }, "candidate_id": { - "type": "string", - "format": "uuid", - "nullable": true, - "title": "Target candidate's {@link IShoppingSaleUnitOptionCandidate", + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "uuid" + } + ], + "title": "Target candidate's {@link IShoppingSaleUnitOptionCandidate.id }", "description": "Target candidate's {@link IShoppingSaleUnitOptionCandidate.id }.\n\nWhen target option's type is `select`, then this attribute is not\n`null` but has a value." }, "value": { "oneOf": [ { - "type": "string", - "nullable": true + "type": "null" }, { - "type": "number", - "nullable": true + "type": "string" + }, + { + "type": "number" }, { - "type": "boolean", - "nullable": true + "type": "boolean" } ], "title": "Written value about the option", "description": "Written value about the option.\n\nWhen target option's type is not `select`, but an atomic type value\nlike `boolean`, `number` or `string`, then this attribute is not\n`null` but has the matched atomic value." } }, - "nullable": false, "required": [ "option_id", "candidate_id", @@ -15411,7 +15911,6 @@ "description": "Volume of the commodity to purchase.\n\nA value indicating how many sets would be multiplied to the children\n{@link IShoppingSaleUnitStock.IInvert.quantity } values." } }, - "nullable": false, "required": [ "volume" ], @@ -15421,12 +15920,18 @@ "type": "object", "properties": { "commodity_ids": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "nullable": true + "oneOf": [ + { + "type": "null" + }, + { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + ] }, "pseudos": { "type": "array", @@ -15435,7 +15940,6 @@ } } }, - "nullable": false, "required": [ "commodity_ids", "pseudos" @@ -15457,7 +15961,6 @@ } } }, - "nullable": false, "required": [ "deposit", "mileage", @@ -15489,7 +15992,6 @@ "type": "number" } }, - "nullable": false, "required": [ "coupons", "tickets", @@ -15515,7 +16017,6 @@ "type": "number" } }, - "nullable": false, "required": [ "commodity_id", "pseudo", @@ -15535,7 +16036,6 @@ "description": "List of goods in the order." } }, - "nullable": false, "required": [ "goods" ], @@ -15547,7 +16047,7 @@ "commodity_id": { "type": "string", "format": "uuid", - "title": "Target commodity's {@link IShoppingCartCommodity", + "title": "Target commodity's {@link IShoppingCartCommodity.id }", "description": "Target commodity's {@link IShoppingCartCommodity.id }." }, "volume": { @@ -15556,7 +16056,6 @@ "description": "Volume of the good.\n\nThe value multiplied to {@link IShoppingCartCommodityStock.quantity }.\nIt's purpose is exactly same with {@link IShoppingCartCommodity.volume },\nbut rewritten because the {@link IShoppingCartCommodity } records are reusable\nuntil payment." } }, - "nullable": false, "required": [ "commodity_id", "volume" @@ -15567,15 +16066,20 @@ "type": "object", "properties": { "good_ids": { - "type": "array", - "items": { - "type": "string", - "format": "uuid" - }, - "nullable": true + "oneOf": [ + { + "type": "null" + }, + { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + ] } }, - "nullable": false, "required": [ "good_ids" ] @@ -15596,7 +16100,6 @@ } } }, - "nullable": false, "required": [ "deposit", "mileage", @@ -15628,7 +16131,6 @@ "type": "number" } }, - "nullable": false, "required": [ "coupons", "tickets", @@ -15651,7 +16153,6 @@ "type": "number" } }, - "nullable": false, "required": [ "good_id", "coupon_id", @@ -15675,7 +16176,6 @@ } } }, - "nullable": false, "required": [ "deposit", "mileage", @@ -15686,10 +16186,7 @@ "type": "object", "properties": { "type": { - "type": "string", - "enum": [ - "cash" - ] + "const": "cash" }, "address": { "$ref": "#/components/schemas/IShoppingAddress.ICreate" @@ -15701,7 +16198,6 @@ "type": "string" } }, - "nullable": false, "required": [ "type", "address", @@ -15754,13 +16250,18 @@ "description": "Zip code, or postal code." }, "special_note": { - "type": "string", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], "title": "Special description if required", "description": "Special description if required." } }, - "nullable": false, "required": [ "mobile", "name", @@ -15777,81 +16278,17 @@ "type": "object", "properties": { "type": { - "type": "string", - "enum": [ - "zero" - ] + "const": "zero" }, "address": { "$ref": "#/components/schemas/IShoppingAddress.ICreate" } }, - "nullable": false, "required": [ "type", "address" ] }, - "IShoppingOrderPublish": { - "type": "object", - "properties": { - "deliveries": { - "type": "array", - "items": { - "$ref": "#/components/schemas/IShoppingDelivery" - }, - "title": "List of deliveries", - "description": "List of deliveries.\n\nAn {@link IShoppingOrder order} can be delivered in multiple times.\nOf course, the opposite case is also possible, that a\n{@link IShoppingDelivery delivery} can be composed of multiple orders." - }, - "state": { - "$ref": "#/components/schemas/IShoppingDelivery.State", - "title": "State of the order, about the deliveries", - "description": "State of the order, about the deliveries." - }, - "id": { - "type": "string", - "format": "uuid", - "title": "Primary Key", - "description": "Primary Key." - }, - "created_at": { - "type": "string", - "format": "date-time", - "title": "Creation time of the record", - "description": "Creation time of the record." - }, - "paid_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "title": "Time when the order was paid", - "description": "Time when the order was paid." - }, - "cancelled_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "title": "Time when the payment was cancelled", - "description": "Time when the payment was cancelled." - }, - "address": { - "$ref": "#/components/schemas/IShoppingAddress", - "title": "Address where the {@link IShoppingOrderGood goods} to be delivered", - "description": "Address where the {@link IShoppingOrderGood goods} to be delivered." - } - }, - "nullable": false, - "required": [ - "deliveries", - "state", - "id", - "created_at", - "paid_at", - "cancelled_at", - "address" - ], - "description": "Order completion and payment information.\n\n`IShoppingOrderPublish` is an entity that embodies the series of processes\nin which a {@link IShoppingCustomer customer} pays for his or her\n{@link IShoppingOrder order}, thereby completing the order. And only after\nthe order is {@link paid_at completed}, can the {@link IShoppingSeller seller}\nrecognize that the customer has purchased his product.\n\nBy the way, please note that just because the `IShoppingOrderPublish` record\nexists, it does not mean that the payment has been completed. Of course, with\n\"credit cards\" and \"Google Pay\", payment application and payment occur at the\nsame time. However, there are some cases where payment is made after the\npayment application, such as \"bank transfer\" or \"virtual account payment\".\nTherefore, to see the completion of payment, be sure to check the\n{@link paid_at } property.\n\nIn addition, even after payment has been made, there may be cases where it is\nsuddenly cancelled, so please be aware of this as well." - }, "IShoppingSaleQuestion.ICreate": { "type": "object", "properties": { @@ -15861,11 +16298,16 @@ "description": "Whether the question article is secret or not.\n\nIf secret article, only the writer customer and related seller can see\nthe detailed content." }, "format": { - "type": "string", - "enum": [ - "html", - "md", - "txt" + "oneOf": [ + { + "const": "html" + }, + { + "const": "md" + }, + { + "const": "txt" + } ], "title": "Format of body", "description": "Format of body.\n\nSame meaning with extension like `html`, `md`, `txt`." @@ -15889,7 +16331,6 @@ "description": "List of attachment files." } }, - "nullable": false, "required": [ "secret", "format", @@ -15903,11 +16344,16 @@ "type": "object", "properties": { "format": { - "type": "string", - "enum": [ - "html", - "md", - "txt" + "oneOf": [ + { + "const": "html" + }, + { + "const": "md" + }, + { + "const": "txt" + } ], "title": "Format of body", "description": "Format of body.\n\nSame meaning with extension like `html`, `md`, `txt`." @@ -15931,7 +16377,6 @@ "description": "List of attachment files." } }, - "nullable": false, "required": [ "format", "title", @@ -15946,22 +16391,27 @@ "good_id": { "type": "string", "format": "uuid", - "title": "Target good's {@link IShoppingOrderGood", + "title": "Target good's {@link IShoppingOrderGood.id }", "description": "Target good's {@link IShoppingOrderGood.id }." }, "score": { "type": "number", - "maximum": 100, "minimum": 0, + "maximum": 100, "title": "Score of the review", "description": "Score of the review." }, "format": { - "type": "string", - "enum": [ - "html", - "md", - "txt" + "oneOf": [ + { + "const": "html" + }, + { + "const": "md" + }, + { + "const": "txt" + } ], "title": "Format of body", "description": "Format of body.\n\nSame meaning with extension like `html`, `md`, `txt`." @@ -15985,7 +16435,6 @@ "description": "List of attachment files." } }, - "nullable": false, "required": [ "good_id", "score", @@ -16001,17 +16450,22 @@ "properties": { "score": { "type": "number", - "maximum": 100, "minimum": 0, + "maximum": 100, "title": "Score of the review", "description": "Score of the review." }, "format": { - "type": "string", - "enum": [ - "html", - "md", - "txt" + "oneOf": [ + { + "const": "html" + }, + { + "const": "md" + }, + { + "const": "txt" + } ], "title": "Format of body", "description": "Format of body.\n\nSame meaning with extension like `html`, `md`, `txt`." @@ -16035,7 +16489,6 @@ "description": "List of attachment files." } }, - "nullable": false, "required": [ "score", "format", @@ -16047,8 +16500,7 @@ }, "IShoppingSeller.IJoin": { "type": "object", - "properties": {}, - "nullable": false + "properties": {} }, "IShoppingDelivery.IRequest": { "type": "object", @@ -16056,10 +16508,13 @@ "sort": { "type": "array", "items": { - "type": "string", - "enum": [ - "-delivery.created_at", - "+delivery.created_at" + "oneOf": [ + { + "const": "-delivery.created_at" + }, + { + "const": "+delivery.created_at" + } ] } }, @@ -16070,12 +16525,10 @@ }, "limit": { "type": "integer", - "default": 100, "title": "Limitation of records per a page", "description": "Limitation of records per a page." } - }, - "nullable": false + } }, "IPageIShoppingDelivery.IInvert": { "type": "object", @@ -16094,7 +16547,6 @@ "description": "List of records." } }, - "nullable": false, "required": [ "pagination", "data" @@ -16161,7 +16613,6 @@ "description": "Creation time of the record." } }, - "nullable": false, "required": [ "orders", "id", @@ -16203,7 +16654,14 @@ "description": "Price information including discounts." }, "publish": { - "$ref": "#/components/schemas/IShoppingOrderPublish.IInvertFromDelivery.Nullable", + "oneOf": [ + { + "type": "null" + }, + { + "$ref": "#/components/schemas/IShoppingOrderPublish.IInvertFromDelivery" + } + ], "title": "Order completion and payment information", "description": "Order completion and payment information." }, @@ -16214,7 +16672,6 @@ "description": "Creation time of the record." } }, - "nullable": false, "required": [ "id", "customer", @@ -16225,7 +16682,7 @@ ], "description": "Invert information from delivery." }, - "IShoppingOrderPublish.IInvertFromDelivery.Nullable": { + "IShoppingOrderPublish.IInvertFromDelivery": { "type": "object", "properties": { "id": { @@ -16241,16 +16698,28 @@ "description": "Creation time of the record." }, "paid_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Time when the order was paid", "description": "Time when the order was paid." }, "cancelled_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Time when the payment was cancelled", "description": "Time when the payment was cancelled." }, @@ -16260,7 +16729,6 @@ "description": "Address where the {@link IShoppingOrderGood goods} to be delivered." } }, - "nullable": true, "required": [ "id", "created_at", @@ -16299,7 +16767,6 @@ "description": "List of shippers of the delivery." } }, - "nullable": false, "required": [ "pieces", "journeys", @@ -16313,19 +16780,19 @@ "publish_id": { "type": "string", "format": "uuid", - "title": "Target order's {@link IShoppingOrderPublish", + "title": "Target order's {@link IShoppingOrderPublish.id }", "description": "Target order's {@link IShoppingOrderPublish.id }." }, "good_id": { "type": "string", "format": "uuid", - "title": "Target good's {@link IShoppingOrderGood", + "title": "Target good's {@link IShoppingOrderGood.id }", "description": "Target good's {@link IShoppingOrderGood.id }." }, "stock_id": { "type": "string", "format": "uuid", - "title": "Target stock's {@link IShoppingSaleUnitStock", + "title": "Target stock's {@link IShoppingSaleUnitStock.id }", "description": "Target stock's {@link IShoppingSaleUnitStock.id }." }, "quantity": { @@ -16335,7 +16802,6 @@ "description": "Quantity of the stock.\n\nIt can be precision value to express splitted shipping." } }, - "nullable": false, "required": [ "publish_id", "good_id", @@ -16348,44 +16814,74 @@ "type": "object", "properties": { "type": { - "type": "string", - "enum": [ - "preparing", - "manufacturing", - "shipping", - "delivering" + "oneOf": [ + { + "const": "preparing" + }, + { + "const": "manufacturing" + }, + { + "const": "shipping" + }, + { + "const": "delivering" + } ], "title": "Type of journey", "description": "Type of journey.\n\n- preparing\n- manufacturing\n- shipping\n- delivering" }, "title": { - "type": "string", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], "title": "Title of journey", "description": "Title of journey." }, "description": { - "type": "string", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ], "title": "Description of journey", "description": "Description of journey." }, "started_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Start time of the journey", "description": "Start time of the journey." }, "completed_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Completion time of the journey", "description": "Completion time of the journey." } }, - "nullable": false, "required": [ "type", "title", @@ -16399,8 +16895,14 @@ "type": "object", "properties": { "company": { - "type": "string", - "nullable": true + "oneOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ] }, "name": { "type": "string" @@ -16409,7 +16911,6 @@ "type": "string" } }, - "nullable": false, "required": [ "company", "name", @@ -16427,7 +16928,6 @@ } } }, - "nullable": false, "required": [ "publish_ids" ] @@ -16436,14 +16936,19 @@ "type": "object", "properties": { "completed_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Completion time of the journey", "description": "Completion time of the journey." } }, - "nullable": false, "required": [ "completed_at" ], @@ -16454,30 +16959,47 @@ "properties": { "section_code": { "type": "string", - "title": "Belonged section's {@link IShoppingSection", + "title": "Belonged section's {@link IShoppingSection.code }", "description": "Belonged section's {@link IShoppingSection.code }." }, "status": { - "type": "string", - "enum": [ - "paused", - "suspended" + "oneOf": [ + { + "type": "null" + }, + { + "const": "paused" + }, + { + "const": "suspended" + } ], - "nullable": true, "title": "Initial status of the sale", "description": "Initial status of the sale.\n\n`null` or `undefined`: No restriction\n`paused`: Starts with {@link ITimestamps.paused_at paused} status\n`suspended`: Starts with {@link ITimestamps.suspended_at suspended} status" }, "opened_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Opening time of the sale", "description": "Opening time of the sale." }, "closed_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Closing time of the sale", "description": "Closing time of the sale.\n\nIf this value is `null`, the sale be continued forever." }, @@ -16504,7 +17026,6 @@ } } }, - "nullable": false, "required": [ "section_code", "opened_at", @@ -16523,11 +17044,16 @@ "type": "string" }, "format": { - "type": "string", - "enum": [ - "html", - "md", - "txt" + "oneOf": [ + { + "const": "html" + }, + { + "const": "md" + }, + { + "const": "txt" + } ] }, "body": { @@ -16546,7 +17072,6 @@ } } }, - "nullable": false, "required": [ "title", "format", @@ -16560,7 +17085,7 @@ "properties": { "code": { "type": "string", - "title": "Target channel's {@link IShoppingChannel", + "title": "Target channel's {@link IShoppingChannel.code }", "description": "Target channel's {@link IShoppingChannel.code }." }, "category_ids": { @@ -16569,11 +17094,10 @@ "type": "string", "format": "uuid" }, - "title": "List of target categories' {@link IShoppingChannelCategory", + "title": "List of target categories' {@link IShoppingChannelCategory.id }s", "description": "List of target categories' {@link IShoppingChannelCategory.id }s.\n\nIf empty, it means all categories of the channel is listing the sale." } }, - "nullable": false, "required": [ "code", "category_ids" @@ -16623,7 +17147,6 @@ "description": "Whether the unit is required or not.\n\nWhen the unit is required, the customer must select the unit. If do not\nselect, customer can't buy it.\n\nFor example, if there's a sale \"Macbook Set\" and one of the unit is the\n\"Main Body\", is it possible to buy the \"Macbook Set\" without the\n\"Main Body\" unit? This property is for that case." } }, - "nullable": false, "required": [ "options", "stocks", @@ -16637,11 +17160,16 @@ "type": "object", "properties": { "type": { - "type": "string", - "enum": [ - "string", - "number", - "boolean" + "oneOf": [ + { + "const": "string" + }, + { + "const": "number" + }, + { + "const": "boolean" + } ], "title": "Type of descriptive option", "description": "Type of descriptive option.\n\nWhich typed value should be written when purchasing." @@ -16652,7 +17180,6 @@ "description": "Readable name of the option." } }, - "nullable": false, "required": [ "type", "name" @@ -16663,10 +17190,7 @@ "type": "object", "properties": { "type": { - "type": "string", - "enum": [ - "select" - ], + "const": "select", "title": "Discriminant for the type of selectable option", "description": "Discriminant for the type of selectable option." }, @@ -16690,7 +17214,6 @@ "description": "List of candidate values." } }, - "nullable": false, "required": [ "type", "name", @@ -16708,7 +17231,6 @@ "description": "Represents the name of the candidate value." } }, - "nullable": false, "required": [ "name" ], @@ -16742,7 +17264,6 @@ "description": "List of choices.\n\nWhich candidate values being chosen for each option." } }, - "nullable": false, "required": [ "name", "price", @@ -16763,7 +17284,6 @@ "description": "Target candidate's index number in\n{@link IShoppingSaleUnitSelectableOption.ICreate.candidates }." } }, - "nullable": false, "required": [ "option_index", "candidate_index" @@ -16796,7 +17316,6 @@ } } }, - "nullable": false, "required": [ "content", "channels", @@ -16809,66 +17328,38 @@ "type": "object", "properties": { "opened_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Opening time of the sale", "description": "Opening time of the sale." }, "closed_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "oneOf": [ + { + "type": "null" + }, + { + "type": "string", + "format": "date-time" + } + ], "title": "Closing time of the sale", "description": "Closing time of the sale.\n\nIf this value is `null`, the sale be continued forever." } }, - "nullable": false, "required": [ "opened_at", "closed_at" ], "description": "Update opening time information of sale." }, - "IShoppingSaleInquiryAnswer": { - "type": "object", - "properties": { - "seller": { - "$ref": "#/components/schemas/IShoppingSeller", - "title": "Seller who've written the answer", - "description": "Seller who've written the answer." - }, - "id": { - "type": "string", - "format": "uuid", - "title": "Primary Key", - "description": "Primary Key." - }, - "snapshots": { - "type": "array", - "items": { - "$ref": "#/components/schemas/IBbsArticle.ISnapshot" - }, - "minItems": 1, - "title": "List of snapshot contents", - "description": "List of snapshot contents.\n\nIt is created for the first time when an article is created, and is\naccumulated every time the article is modified." - }, - "created_at": { - "type": "string", - "format": "date-time", - "title": "Creation time of article", - "description": "Creation time of article." - } - }, - "nullable": false, - "required": [ - "seller", - "id", - "snapshots", - "created_at" - ], - "description": "Answers to questions about sale snapshots.\n\n`IShoppingSaleInquiryAnswer` is an entity that embodies the official\nanswer written by the {@link IShoppingSeller seller} to the\n{@link IShoppingSaleInquiry inquiry} written by the\n{@link IShoppingCustomer customer}.\n\nOf course, in addition to writing an official response like this, it is\nalso possible for the seller to communicate with the inqjuiry written\ncustomer and multiple customers through\n{@link IShoppingSaleInquiryComment comments} in the attribution inquiry.\n\nFor refererence, it is not possible to write comments on this answer.\nEncourage people to write comments on the inquiry article. This is to\nprevent comments from being scattered in both inquiry and answer\narticles." - }, "IShoppingSaleInquiryAnswer.ISnapshot": { "type": "object", "properties": { @@ -16907,7 +17398,6 @@ "description": "List of attachment files." } }, - "nullable": false, "required": [ "id", "created_at", @@ -16923,10 +17413,13 @@ "sort": { "type": "array", "items": { - "type": "string", - "enum": [ - "-created_at", - "+created_at" + "oneOf": [ + { + "const": "-created_at" + }, + { + "const": "+created_at" + } ] }, "title": "Sortable columns", @@ -16939,12 +17432,10 @@ }, "limit": { "type": "integer", - "default": 100, "title": "Limitation of records per a page", "description": "Limitation of records per a page." } }, - "nullable": false, "description": "Request information of the stock supplement list." }, "IPageIShoppingSaleUnitStockSupplement": { @@ -16964,7 +17455,6 @@ "description": "List of records." } }, - "nullable": false, "required": [ "pagination", "data" @@ -16992,7 +17482,6 @@ "description": "Creation time of the record.\n\nAnother words, the time when inventory of the stock being supplemented." } }, - "nullable": false, "required": [ "id", "value", @@ -17009,7 +17498,6 @@ "description": "Supplemented quantity." } }, - "nullable": false, "required": [ "value" ], @@ -17023,5 +17511,28 @@ "in": "header" } } - } + }, + "tags": [ + { + "name": "Monitor" + }, + { + "name": "Authenticate" + }, + { + "name": "Discount" + }, + { + "name": "Order" + }, + { + "name": "Sale" + }, + { + "name": "Systematic" + }, + { + "name": "Section" + } + ] } \ No newline at end of file diff --git a/package.json b/package.json index 8589613..8c3e0bd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@samchon/openapi", - "version": "0.1.16", + "version": "0.1.18", "description": "OpenAPI definitions and converters for 'typia' and 'nestia'.", "main": "./lib/index.js", "typings": "./lib/index.d.ts", diff --git a/src/OpenApi.ts b/src/OpenApi.ts index fcaf881..ef245ff 100644 --- a/src/OpenApi.ts +++ b/src/OpenApi.ts @@ -2,8 +2,10 @@ import { OpenApiV3 } from "./OpenApiV3"; import { OpenApiV3_1 } from "./OpenApiV3_1"; import { SwaggerV2 } from "./SwaggerV2"; import { OpenApiV3Converter } from "./internal/OpenApiV3Converter"; +import { OpenApiV3Downgrader } from "./internal/OpenApiV3Downgrader"; import { OpenApiV3_1Converter } from "./internal/OpenApiV3_1Converter"; import { SwaggerV2Converter } from "./internal/SwaggerV2Converter"; +import { SwaggerV2Downgrader } from "./internal/SwaggerV2Downgrader"; /** * Emended OpenAPI v3.1 definition used by `typia` and `nestia`. @@ -38,6 +40,16 @@ import { SwaggerV2Converter } from "./internal/SwaggerV2Converter"; * @author Jeongho Nam - https://github.com/samchon */ export namespace OpenApi { + export type Method = + | "get" + | "post" + | "put" + | "delete" + | "options" + | "head" + | "patch" + | "trace"; + /** * Convert Swagger or OpenAPI document into emended OpenAPI v3.1 document. * @@ -57,15 +69,41 @@ export namespace OpenApi { throw new TypeError("Unrecognized Swagger/OpenAPI version."); }; - export type Method = - | "get" - | "post" - | "put" - | "delete" - | "options" - | "head" - | "patch" - | "trace"; + /** + * Downgrade to Swagger v2.0 document. + * + * Downgrade the given document (emeneded OpenAPI v3.1) into Swagger v2.0. + * + * @param document Emended OpenAPI v3.1 document to downgrade + * @param version Version to downgrade + * @returns Swagger v2.0 document + */ + export function downgrade( + document: IDocument, + version: "2.0", + ): SwaggerV2.IDocument; + export function downgrade( + document: IDocument, + version: "3.0", + ): OpenApiV3.IDocument; + + /** + * Downgrade to OpenAPI v2.3 document. + * + * Downgrade the given document (emeneded OpenAPI v3.1) into OpenAPI v3.0. + * + * @param document Emended OpenAPI v3.1 document to downgrade + * @param version Version to downgrade + * @returns OpenAPI v3.0 document + */ + export function downgrade( + document: IDocument, + version: string, + ): SwaggerV2.IDocument | OpenApiV3.IDocument { + if (version === "2.0") return SwaggerV2Downgrader.downgrade(document); + else if (version === "3.0") return OpenApiV3Downgrader.downgrade(document); + throw new TypeError("Unrecognized Swagger/OpenAPI version."); + } /* ----------------------------------------------------------- PATH ITEMS diff --git a/src/internal/OpenApiTypeChecker.ts b/src/internal/OpenApiTypeChecker.ts new file mode 100644 index 0000000..6d84f8f --- /dev/null +++ b/src/internal/OpenApiTypeChecker.ts @@ -0,0 +1,59 @@ +import { OpenApi } from "../OpenApi"; + +export namespace OpenApiTypeChecker { + export const isNull = ( + schema: OpenApi.IJsonSchema, + ): schema is OpenApi.IJsonSchema.INull => + (schema as OpenApi.IJsonSchema.INull).type === "null"; + export const isUnknown = ( + schema: OpenApi.IJsonSchema, + ): schema is OpenApi.IJsonSchema.IUnknown => + (schema as OpenApi.IJsonSchema.IUnknown).type === undefined && + !isConstant(schema) && + !isOneOf(schema) && + !isReference(schema); + + export const isConstant = ( + schema: OpenApi.IJsonSchema, + ): schema is OpenApi.IJsonSchema.IConstant => + (schema as OpenApi.IJsonSchema.IConstant).const !== undefined; + export const isBoolean = ( + schema: OpenApi.IJsonSchema, + ): schema is OpenApi.IJsonSchema.IBoolean => + (schema as OpenApi.IJsonSchema.IBoolean).type === "boolean"; + export const isInteger = ( + schema: OpenApi.IJsonSchema, + ): schema is OpenApi.IJsonSchema.IInteger => + (schema as OpenApi.IJsonSchema.IInteger).type === "integer"; + export const isNumber = ( + schema: OpenApi.IJsonSchema, + ): schema is OpenApi.IJsonSchema.INumber => + (schema as OpenApi.IJsonSchema.INumber).type === "number"; + export const isString = ( + schema: OpenApi.IJsonSchema, + ): schema is OpenApi.IJsonSchema.IString => + (schema as OpenApi.IJsonSchema.IString).type === "string"; + + export const isArray = ( + schema: OpenApi.IJsonSchema, + ): schema is OpenApi.IJsonSchema.IArray => + (schema as OpenApi.IJsonSchema.IArray).type === "array" && + (schema as OpenApi.IJsonSchema.IArray).items !== undefined; + export const isTuple = ( + schema: OpenApi.IJsonSchema, + ): schema is OpenApi.IJsonSchema.ITuple => + (schema as OpenApi.IJsonSchema.ITuple).type === "array" && + (schema as OpenApi.IJsonSchema.ITuple).prefixItems !== undefined; + export const isObject = ( + schema: OpenApi.IJsonSchema, + ): schema is OpenApi.IJsonSchema.IObject => + (schema as OpenApi.IJsonSchema.IObject).type === "object"; + export const isReference = ( + schema: OpenApi.IJsonSchema, + ): schema is OpenApi.IJsonSchema.IReference => + (schema as any).$ref !== undefined; + export const isOneOf = ( + schema: OpenApi.IJsonSchema, + ): schema is OpenApi.IJsonSchema.IOneOf => + (schema as OpenApi.IJsonSchema.IOneOf).oneOf !== undefined; +} diff --git a/src/internal/OpenApiV3Downgrader.ts b/src/internal/OpenApiV3Downgrader.ts new file mode 100644 index 0000000..9501c6a --- /dev/null +++ b/src/internal/OpenApiV3Downgrader.ts @@ -0,0 +1,311 @@ +import { OpenApi } from "../OpenApi"; +import { OpenApiV3 } from "../OpenApiV3"; +import { OpenApiTypeChecker } from "./OpenApiTypeChecker"; + +export namespace OpenApiV3Downgrader { + export interface IComponentsCollection { + original: OpenApi.IComponents; + downgraded: OpenApiV3.IComponents; + } + + export const downgrade = (input: OpenApi.IDocument): OpenApiV3.IDocument => { + const collection: IComponentsCollection = downgradeComponents( + input.components, + ); + return { + openapi: "3.0.0", + servers: input.servers, + info: input.info, + components: collection.downgraded, + paths: input.paths + ? Object.fromEntries( + Object.entries(input.paths) + .filter(([_, v]) => v !== undefined) + .map( + ([key, value]) => + [key, downgradePathItem(collection)(value)] as const, + ), + ) + : undefined, + security: input.security, + tags: input.tags, + }; + }; + + /* ----------------------------------------------------------- + OPERATORS + ----------------------------------------------------------- */ + const downgradePathItem = + (collection: IComponentsCollection) => + (pathItem: OpenApi.IPath): OpenApiV3.IPath => ({ + ...(pathItem as any), + ...(pathItem.get + ? { get: downgradeOperation(collection)(pathItem.get) } + : undefined), + ...(pathItem.put + ? { put: downgradeOperation(collection)(pathItem.put) } + : undefined), + ...(pathItem.post + ? { post: downgradeOperation(collection)(pathItem.post) } + : undefined), + ...(pathItem.delete + ? { delete: downgradeOperation(collection)(pathItem.delete) } + : undefined), + ...(pathItem.options + ? { options: downgradeOperation(collection)(pathItem.options) } + : undefined), + ...(pathItem.head + ? { head: downgradeOperation(collection)(pathItem.head) } + : undefined), + ...(pathItem.patch + ? { patch: downgradeOperation(collection)(pathItem.patch) } + : undefined), + ...(pathItem.trace + ? { trace: downgradeOperation(collection)(pathItem.trace) } + : undefined), + }); + + const downgradeOperation = + (collection: IComponentsCollection) => + (input: OpenApi.IOperation): OpenApiV3.IOperation => ({ + ...input, + parameters: input.parameters + ? input.parameters.map(downgradeParameter(collection)) + : undefined, + requestBody: input.requestBody + ? downgradeRequestBody(collection)(input.requestBody) + : undefined, + responses: input.responses + ? Object.fromEntries( + Object.entries(input.responses) + .filter(([_, v]) => v !== undefined) + .map(([key, value]) => [ + key, + downgradeResponse(collection)(value), + ]), + ) + : undefined, + }); + + const downgradeParameter = + (collection: IComponentsCollection) => + ( + input: OpenApi.IOperation.IParameter, + ): OpenApiV3.IOperation.IParameter => ({ + ...input, + schema: downgradeSchema(collection)(input.schema), + }); + + const downgradeRequestBody = + (collection: IComponentsCollection) => + ( + input: OpenApi.IOperation.IRequestBody, + ): OpenApiV3.IOperation.IRequestBody => ({ + ...input, + content: input.content + ? downgradeContent(collection)(input.content) + : undefined, + }); + + const downgradeResponse = + (collection: IComponentsCollection) => + (input: OpenApi.IOperation.IResponse): OpenApiV3.IOperation.IResponse => ({ + ...input, + content: input.content + ? downgradeContent(collection)(input.content) + : undefined, + headers: input.headers + ? Object.fromEntries( + Object.entries(input.headers) + .filter(([_, v]) => v !== undefined) + .map(([key, value]) => [ + key, + { + ...value, + schema: downgradeSchema(collection)(value.schema), + }, + ]), + ) + : undefined, + }); + + const downgradeContent = + (collection: IComponentsCollection) => + ( + record: OpenApi.IOperation.IContent, + ): Record => + Object.fromEntries( + Object.entries(record) + .filter(([_, v]) => v !== undefined) + .map( + ([key, value]) => + [ + key, + { + ...value, + schema: value?.schema + ? downgradeSchema(collection)(value.schema) + : undefined, + }, + ] as const, + ), + ); + + /* ----------------------------------------------------------- + DEFINITIONS + ----------------------------------------------------------- */ + export const downgradeComponents = ( + input: OpenApi.IComponents, + ): IComponentsCollection => { + const collection: IComponentsCollection = { + original: input, + downgraded: { + securitySchemes: input.securitySchemes, + }, + }; + if (input.schemas) { + collection.downgraded.schemas = {}; + for (const [key, value] of Object.entries(input.schemas)) + if (value !== undefined) + collection.downgraded.schemas[key] = + downgradeSchema(collection)(value); + } + return collection; + }; + + export const downgradeSchema = + (collection: IComponentsCollection) => + (input: OpenApi.IJsonSchema): OpenApiV3.IJsonSchema => { + const nullable: boolean = + OpenApiTypeChecker.isNull(input) || + (OpenApiTypeChecker.isOneOf(input) && + input.oneOf.some(OpenApiTypeChecker.isNull)); + const union: OpenApiV3.IJsonSchema[] = []; + const attribute: OpenApiV3.IJsonSchema.__IAttribute = { + title: input.title, + description: input.description, + ...Object.fromEntries( + Object.entries(input).filter( + ([key, value]) => key.startsWith("x-") && value !== undefined, + ), + ), + }; + const visit = (schema: OpenApi.IJsonSchema): void => { + if (OpenApiTypeChecker.isBoolean(schema)) + union.push({ type: "boolean" }); + else if ( + OpenApiTypeChecker.isBoolean(schema) || + OpenApiTypeChecker.isInteger(schema) || + OpenApiTypeChecker.isNumber(schema) || + OpenApiTypeChecker.isString(schema) || + OpenApiTypeChecker.isReference(schema) + ) + union.push({ ...schema }); + else if (OpenApiTypeChecker.isArray(schema)) + union.push({ + ...schema, + items: downgradeSchema(collection)(schema.items), + }); + else if (OpenApiTypeChecker.isTuple(schema)) + union.push({ + ...schema, + items: ((): OpenApiV3.IJsonSchema => { + if (schema.additionalItems === true) return {}; + const elements = [ + ...schema.prefixItems, + ...(typeof schema.additionalItems === "object" + ? [downgradeSchema(collection)(schema.additionalItems)] + : []), + ]; + if (elements.length === 0) return {}; + return { + oneOf: elements.map(downgradeSchema(collection)), + }; + })(), + minItems: schema.prefixItems.length, + maxItems: + !!schema.additionalItems === true + ? undefined + : schema.prefixItems.length, + ...{ + prefixItems: undefined, + additionalItems: undefined, + }, + }); + else if (OpenApiTypeChecker.isObject(schema)) + union.push({ + ...schema, + properties: schema.properties + ? Object.fromEntries( + Object.entries(schema.properties) + .filter(([_, v]) => v !== undefined) + .map(([key, value]) => [ + key, + downgradeSchema(collection)(value), + ]), + ) + : undefined, + additionalProperties: + typeof schema.additionalProperties === "object" + ? downgradeSchema(collection)(schema.additionalProperties) + : schema.additionalProperties, + required: schema.required, + }); + else if (OpenApiTypeChecker.isOneOf(schema)) + schema.oneOf.forEach(visit); + }; + const visitConstant = (schema: OpenApi.IJsonSchema): void => { + const insert = (value: any): void => { + const matched: OpenApiV3.IJsonSchema.INumber | undefined = union.find( + (u) => + (u as OpenApiV3.IJsonSchema.__ISignificant).type === value, + ) as OpenApiV3.IJsonSchema.INumber | undefined; + if (matched !== undefined) { + matched.enum ??= []; + matched.enum.push(value); + } else union.push({ type: typeof value as "number", enum: [value] }); + if (OpenApiTypeChecker.isConstant(schema)) insert(schema.const); + else if (OpenApiTypeChecker.isOneOf(schema)) + schema.oneOf.forEach(insert); + }; + }; + + visit(input); + visitConstant(input); + if (nullable) + for (const u of union) + if (OpenApiTypeChecker.isReference(u)) + downgradeNullableReference(collection)(u); + else (u as OpenApiV3.IJsonSchema.IArray).nullable = true; + + if (nullable === true && union.length === 0) + return { type: "null", ...attribute }; + return { + ...(union.length === 0 + ? { type: undefined } + : union.length === 1 + ? { ...union[0] } + : { oneOf: union.map((u) => ({ ...u, nullable: undefined })) }), + ...attribute, + }; + }; + + const downgradeNullableReference = + (collection: IComponentsCollection) => + (schema: OpenApiV3.IJsonSchema.IReference): void => { + const key: string = schema.$ref.split("/").pop()!; + if (key.endsWith(".Nullable")) return; + + const found: OpenApi.IJsonSchema | undefined = + collection.original.schemas?.[key]; + if (found === undefined) return; + else if ( + collection.downgraded.schemas![`${key}.Nullable`] === undefined + ) { + collection.downgraded.schemas![`${key}.Nullable`] = {}; + collection.downgraded.schemas![`${key}.Nullable`] = + downgradeSchema(collection)(found); + } + schema.$ref += ".Nullable"; + }; +} diff --git a/src/internal/OpenApiV3_1Converter.ts b/src/internal/OpenApiV3_1Converter.ts index e325e7c..75ca985 100644 --- a/src/internal/OpenApiV3_1Converter.ts +++ b/src/internal/OpenApiV3_1Converter.ts @@ -83,7 +83,7 @@ export namespace OpenApiV3_1Converter { (input: OpenApiV3_1.IOperation): OpenApi.IOperation => ({ ...input, parameters: - pathItem.parameters !== undefined || input.parameters === undefined + pathItem.parameters !== undefined || input.parameters !== undefined ? [...(pathItem.parameters ?? []), ...(input.parameters ?? [])] .map((p) => { if (!TypeChecker.isReference(p)) return convertParameter(p); diff --git a/src/internal/SwaggerV2Converter.ts b/src/internal/SwaggerV2Converter.ts index 6044e24..2f77c40 100644 --- a/src/internal/SwaggerV2Converter.ts +++ b/src/internal/SwaggerV2Converter.ts @@ -76,6 +76,7 @@ export namespace SwaggerV2Converter { .filter( (p) => p !== undefined && + p.in !== "body" && (p as SwaggerV2.IOperation.IBodyParameter).schema === undefined, ) as SwaggerV2.IOperation.IGeneralParameter[] diff --git a/src/internal/SwaggerV2Downgrader.ts b/src/internal/SwaggerV2Downgrader.ts new file mode 100644 index 0000000..1d088c0 --- /dev/null +++ b/src/internal/SwaggerV2Downgrader.ts @@ -0,0 +1,356 @@ +import { OpenApi } from "../OpenApi"; +import { SwaggerV2 } from "../SwaggerV2"; +import { OpenApiTypeChecker } from "./OpenApiTypeChecker"; + +export namespace SwaggerV2Downgrader { + export interface IComponentsCollection { + original: OpenApi.IComponents; + downgraded: Record; + } + + export const downgrade = (input: OpenApi.IDocument): SwaggerV2.IDocument => { + const collection: IComponentsCollection = downgradeComponents( + input.components, + ); + return { + swagger: "2.0", + info: input.info, + host: input.servers?.[0]?.url + ? input.servers[0].url.split("://").pop()! + : "", + definitions: collection.downgraded, + securityDefinitions: input.components?.securitySchemes + ? Object.fromEntries( + Object.entries(input.components.securitySchemes) + .filter(([_, v]) => v !== undefined) + .map(([key, value]) => + downgradeSecurityScheme(value).map((v) => [key, v]), + ) + .flat(), + ) + : undefined, + paths: input.paths + ? Object.fromEntries( + Object.entries(input.paths) + .filter(([_, v]) => v !== undefined) + .map( + ([key, value]) => + [key, downgradePathItem(collection)(value)] as const, + ), + ) + : undefined, + security: input.security, + tags: input.tags, + }; + }; + + /* ----------------------------------------------------------- + OPERATORS + ----------------------------------------------------------- */ + const downgradePathItem = + (collection: IComponentsCollection) => + (pathItem: OpenApi.IPath): SwaggerV2.IPath => ({ + ...(pathItem as any), + ...(pathItem.get + ? { get: downgradeOperation(collection)(pathItem.get) } + : undefined), + ...(pathItem.put + ? { put: downgradeOperation(collection)(pathItem.put) } + : undefined), + ...(pathItem.post + ? { post: downgradeOperation(collection)(pathItem.post) } + : undefined), + ...(pathItem.delete + ? { delete: downgradeOperation(collection)(pathItem.delete) } + : undefined), + ...(pathItem.options + ? { options: downgradeOperation(collection)(pathItem.options) } + : undefined), + ...(pathItem.head + ? { head: downgradeOperation(collection)(pathItem.head) } + : undefined), + ...(pathItem.patch + ? { patch: downgradeOperation(collection)(pathItem.patch) } + : undefined), + ...(pathItem.trace + ? { trace: downgradeOperation(collection)(pathItem.trace) } + : undefined), + }); + + const downgradeOperation = + (collection: IComponentsCollection) => + (input: OpenApi.IOperation): SwaggerV2.IOperation => ({ + ...input, + parameters: + input.parameters !== undefined || input.requestBody !== undefined + ? [ + ...(input.parameters ?? []).map(downgradeParameter(collection)), + ...(input.requestBody + ? [downgradeRequestBody(collection)(input.requestBody)] + : []), + ] + : undefined, + responses: input.responses + ? Object.fromEntries( + Object.entries(input.responses) + .filter(([_, v]) => v !== undefined) + .map(([key, value]) => [ + key, + downgradeResponse(collection)(value), + ]), + ) + : undefined, + ...{ + requestBody: undefined, + servers: undefined, + }, + }); + + const downgradeParameter = + (collection: IComponentsCollection) => + ( + input: OpenApi.IOperation.IParameter, + i: number, + ): SwaggerV2.IOperation.IParameter => + ({ + ...downgradeSchema(collection)(input.schema), + ...input, + schema: undefined, + name: input.name ?? `p${i}`, + }) as any; + + const downgradeRequestBody = + (collection: IComponentsCollection) => + ( + input: OpenApi.IOperation.IRequestBody, + ): SwaggerV2.IOperation.IParameter => ({ + name: "body", + in: "body", + description: input.description, + required: input.required, + schema: downgradeSchema(collection)( + Object.values(input.content ?? {})[0]?.schema ?? {}, + ), + }); + + const downgradeResponse = + (collection: IComponentsCollection) => + (input: OpenApi.IOperation.IResponse): SwaggerV2.IOperation.IResponse => ({ + description: input.description, + schema: downgradeSchema(collection)( + Object.values(input.content ?? {})[0]?.schema ?? {}, + ), + headers: input.headers + ? Object.fromEntries( + Object.entries(input.headers) + .filter(([_, v]) => v !== undefined) + .map(([key, value]) => [ + key, + { + ...value, + schema: downgradeSchema(collection)(value.schema), + }, + ]), + ) + : undefined, + }); + + /* ----------------------------------------------------------- + DEFINITIONS + ----------------------------------------------------------- */ + export const downgradeComponents = ( + input: OpenApi.IComponents, + ): IComponentsCollection => { + const collection: IComponentsCollection = { + original: input, + downgraded: {}, + }; + if (input.schemas) { + collection.downgraded.schemas = {}; + for (const [key, value] of Object.entries(input.schemas)) + if (value !== undefined) + collection.downgraded[key.split("/").pop()!] = + downgradeSchema(collection)(value); + } + return collection; + }; + + export const downgradeSchema = + (collection: IComponentsCollection) => + (input: OpenApi.IJsonSchema): SwaggerV2.IJsonSchema => { + const nullable: boolean = + OpenApiTypeChecker.isNull(input) || + (OpenApiTypeChecker.isOneOf(input) && + input.oneOf.some(OpenApiTypeChecker.isNull)); + const union: SwaggerV2.IJsonSchema[] = []; + const attribute: SwaggerV2.IJsonSchema.__IAttribute = { + title: input.title, + description: input.description, + ...Object.fromEntries( + Object.entries(input).filter( + ([key, value]) => key.startsWith("x-") && value !== undefined, + ), + ), + }; + const visit = (schema: OpenApi.IJsonSchema): void => { + if (OpenApiTypeChecker.isBoolean(schema)) + union.push({ type: "boolean" }); + else if ( + OpenApiTypeChecker.isBoolean(schema) || + OpenApiTypeChecker.isInteger(schema) || + OpenApiTypeChecker.isNumber(schema) || + OpenApiTypeChecker.isString(schema) + ) + union.push({ ...schema }); + else if (OpenApiTypeChecker.isReference(schema)) + union.push({ $ref: `#/definitions/${schema.$ref.split("/").pop()}` }); + else if (OpenApiTypeChecker.isArray(schema)) + union.push({ + ...schema, + items: downgradeSchema(collection)(schema.items), + }); + else if (OpenApiTypeChecker.isTuple(schema)) + union.push({ + ...schema, + items: ((): SwaggerV2.IJsonSchema => { + if (schema.additionalItems === true) return {}; + const elements = [ + ...schema.prefixItems, + ...(typeof schema.additionalItems === "object" + ? [downgradeSchema(collection)(schema.additionalItems)] + : []), + ]; + if (elements.length === 0) return {}; + return { + "x-oneOf": elements.map(downgradeSchema(collection)), + }; + })(), + minItems: schema.prefixItems.length, + maxItems: + !!schema.additionalItems === true + ? undefined + : schema.prefixItems.length, + ...{ + prefixItems: undefined, + additionalItems: undefined, + }, + }); + else if (OpenApiTypeChecker.isObject(schema)) + union.push({ + ...schema, + properties: schema.properties + ? Object.fromEntries( + Object.entries(schema.properties) + .filter(([_, v]) => v !== undefined) + .map(([key, value]) => [ + key, + downgradeSchema(collection)(value), + ]), + ) + : undefined, + additionalProperties: + typeof schema.additionalProperties === "object" + ? downgradeSchema(collection)(schema.additionalProperties) + : schema.additionalProperties, + required: schema.required, + }); + else if (OpenApiTypeChecker.isOneOf(schema)) + schema.oneOf.forEach(visit); + }; + const visitConstant = (schema: OpenApi.IJsonSchema): void => { + const insert = (value: any): void => { + const matched: SwaggerV2.IJsonSchema.INumber | undefined = union.find( + (u) => + (u as SwaggerV2.IJsonSchema.__ISignificant).type === value, + ) as SwaggerV2.IJsonSchema.INumber | undefined; + if (matched !== undefined) { + matched.enum ??= []; + matched.enum.push(value); + } else union.push({ type: typeof value as "number", enum: [value] }); + if (OpenApiTypeChecker.isConstant(schema)) insert(schema.const); + else if (OpenApiTypeChecker.isOneOf(schema)) + schema.oneOf.forEach(insert); + }; + }; + + visit(input); + visitConstant(input); + if (nullable) + for (const u of union) + if (OpenApiTypeChecker.isReference(u)) + downgradeNullableReference(collection)(u); + else (u as SwaggerV2.IJsonSchema.IArray)["x-nullable"] = true; + + if (nullable === true && union.length === 0) + return { type: "null", ...attribute }; + return { + ...(union.length === 0 + ? { type: undefined } + : union.length === 1 + ? { ...union[0] } + : { "x-oneOf": union.map((u) => ({ ...u, nullable: undefined })) }), + ...attribute, + }; + }; + + const downgradeNullableReference = + (collection: IComponentsCollection) => + (schema: SwaggerV2.IJsonSchema.IReference): void => { + const key: string = schema.$ref.split("/").pop()!; + if (key.endsWith(".Nullable")) return; + + const found: OpenApi.IJsonSchema | undefined = + collection.original.schemas?.[key]; + if (found === undefined) return; + else if (collection.downgraded[`${key}.Nullable`] === undefined) { + collection.downgraded[`${key}.Nullable`] = {}; + collection.downgraded[`${key}.Nullable`] = + downgradeSchema(collection)(found); + } + schema.$ref += ".Nullable"; + }; + + const downgradeSecurityScheme = ( + input: OpenApi.ISecurityScheme, + ): SwaggerV2.ISecurityDefinition[] => { + if (input.type === "apiKey") return [input]; + else if (input.type === "http") + if (input.scheme === "basic") + return [{ type: "basic", description: input.description }]; + else return []; + else if (input.type === "oauth2") { + const output: SwaggerV2.ISecurityDefinition[] = []; + if (input.flows.implicit) + output.push({ + type: "oauth2", + flow: "implicit", + authorizationUrl: input.flows.implicit.authorizationUrl, + scopes: input.flows.implicit.scopes, + }); + if (input.flows.password) + output.push({ + type: "oauth2", + flow: "password", + tokenUrl: input.flows.password.tokenUrl, + scopes: input.flows.password.scopes, + }); + if (input.flows.clientCredentials) + output.push({ + type: "oauth2", + flow: "application", + tokenUrl: input.flows.clientCredentials.tokenUrl, + scopes: input.flows.clientCredentials.scopes, + }); + if (input.flows.authorizationCode) + output.push({ + type: "oauth2", + flow: "accessCode", + authorizationUrl: input.flows.authorizationCode.authorizationUrl, + tokenUrl: input.flows.authorizationCode.tokenUrl, + scopes: input.flows.authorizationCode.scopes, + }); + return output; + } + return []; + }; +} diff --git a/test/index.ts b/test/index.ts index b9009f8..5d42503 100644 --- a/test/index.ts +++ b/test/index.ts @@ -4,7 +4,7 @@ import typia from "typia"; import { OpenApi, OpenApiV3, OpenApiV3_1, SwaggerV2 } from "../src"; -console.log("Test OpenAPI conversion"); +const CONVERTED: string = `${__dirname}/../../examples/converted`; const validate = ( title: string, @@ -18,43 +18,56 @@ const validate = ( console.log(" - type assertion"); typia.assert(document); - console.log(" - conversion"); + console.log(" - convert to emended v3.1"); return typia.assert(OpenApi.convert(document)); }; const iterate = async (directory: string): Promise => { for (const file of await fs.promises.readdir(directory)) { const location: string = `${directory}/${file}`; const stat: fs.Stats = await fs.promises.stat(location); + const name: string = file.substring(0, file.length - 5); + if (stat.isDirectory() === true) await iterate(location); else if (file.endsWith(".json") === true) { - const normalized: OpenApi.IDocument = await validate( + const document: OpenApi.IDocument = await validate( path.resolve(location), JSON.parse(await fs.promises.readFile(location, "utf8")), ); await fs.promises.writeFile( - `${__dirname}/../../examples/normalized/${file}`, - JSON.stringify(normalized, null, 2), + `${CONVERTED}/${name}-v31.json`, + JSON.stringify(document, null, 2), + ); + + console.log(` - downgrade to v2.0`); + + const v20: SwaggerV2.IDocument = OpenApi.downgrade(document, "2.0"); + typia.assert(v20); + typia.assert(OpenApi.convert(v20)); + await fs.promises.writeFile( + `${CONVERTED}/${name}-v20.json`, + JSON.stringify(v20, null, 2), + ); + + console.log(` - downgrade to v3.0`); + + const v30: OpenApiV3.IDocument = OpenApi.downgrade(document, "3.0"); + typia.assert(v30); + typia.assert(OpenApi.convert(v30)); + await fs.promises.writeFile( + `${CONVERTED}/${name}-v30.json`, + JSON.stringify(v30, null, 2), ); } } }; const main = async (): Promise => { - // TEST CONVERSION - const NORMALIZED: string = `${__dirname}/../../examples/normalized`; try { - await fs.promises.mkdir(NORMALIZED); + await fs.promises.mkdir(CONVERTED); } catch {} - await iterate(`${__dirname}/../../examples`); - - // TYPE ASSERTIONS - for (const file of await fs.promises.readdir(NORMALIZED)) { - const document: OpenApi.IDocument = JSON.parse( - await fs.promises.readFile(`${NORMALIZED}/${file}`, "utf8"), - ); - if (file === "shopping.json") typia.assertEquals(document); - typia.assert(document); - typia.assert(document); - } + console.log("Test OpenAPI conversion"); + await iterate(`${__dirname}/../../examples/v2.0`); + await iterate(`${__dirname}/../../examples/v3.0`); + await iterate(`${__dirname}/../../examples/v3.1`); }; main().catch((exp) => { console.error(exp);