diff --git a/packages/plugins/openapi/src/rest-generator.ts b/packages/plugins/openapi/src/rest-generator.ts index 90383f8f9..9e2bdc337 100644 --- a/packages/plugins/openapi/src/rest-generator.ts +++ b/packages/plugins/openapi/src/rest-generator.ts @@ -409,6 +409,8 @@ export class RESTfulOpenAPIGenerator extends OpenAPIGeneratorBase { private generateFilterParameters(model: DataModel) { const result: OAPI.ParameterObject[] = []; + const hasMultipleIds = model.fields.filter((f) => isIdField(f)).length > 1; + for (const field of model.fields) { if (isForeignKeyField(field)) { // no filtering with foreign keys because one can filter @@ -416,7 +418,8 @@ export class RESTfulOpenAPIGenerator extends OpenAPIGeneratorBase { continue; } - if (isIdField(field)) { + // For multiple ids, make each id field filterable like a regular field + if (isIdField(field) && !hasMultipleIds) { // id filter result.push(this.makeFilterParameter(field, 'id', 'Id filter')); continue; @@ -843,7 +846,9 @@ export class RESTfulOpenAPIGenerator extends OpenAPIGeneratorBase { } private generateModelEntity(model: DataModel, mode: 'read' | 'create' | 'update'): OAPI.SchemaObject { - const fields = model.fields.filter((f) => !isIdField(f)); + const idFields = model.fields.filter((f) => isIdField(f)); + // For compound ids, each component is also exposed as a separate field + const fields = idFields.length > 1 ? model.fields : model.fields.filter((f) => !isIdField(f)); const attributes: Record = {}; const relationships: Record = {}; @@ -886,8 +891,8 @@ export class RESTfulOpenAPIGenerator extends OpenAPIGeneratorBase { if (mode === 'create') { // 'id' is required if there's no default value - const idField = model.fields.find((f) => isIdField(f)); - if (idField && !hasAttribute(idField, '@default')) { + const idFields = model.fields.filter((f) => isIdField(f)); + if (idFields.length && idFields.every((f) => !hasAttribute(f, '@default'))) { properties = { id: { type: 'string' }, ...properties }; toplevelRequired.unshift('id'); } diff --git a/packages/plugins/openapi/tests/baseline/rest-3.0.0.baseline.yaml b/packages/plugins/openapi/tests/baseline/rest-3.0.0.baseline.yaml index 263624c66..0b05005a6 100644 --- a/packages/plugins/openapi/tests/baseline/rest-3.0.0.baseline.yaml +++ b/packages/plugins/openapi/tests/baseline/rest-3.0.0.baseline.yaml @@ -9,6 +9,8 @@ tags: description: Profile operations - name: post_Item description: Post-related operations + - name: postLike + description: PostLike operations paths: /user: get: @@ -193,6 +195,16 @@ paths: explode: false schema: type: string + - name: filter[likes] + required: false + description: Equality filter for "likes" + in: query + style: form + explode: false + schema: + type: array + items: + type: string responses: '200': description: Successful operation @@ -543,6 +555,16 @@ paths: explode: false schema: type: string + - name: filter[likes] + required: false + description: Equality filter for "likes" + in: query + style: form + explode: false + schema: + type: array + items: + type: string responses: '200': description: Successful operation @@ -745,6 +767,16 @@ paths: explode: false schema: type: string + - name: filter[likes] + required: false + description: Equality filter for "likes" + in: query + style: form + explode: false + schema: + type: array + items: + type: string responses: '200': description: Successful operation @@ -1593,6 +1625,16 @@ paths: explode: false schema: type: string + - name: filter[likes] + required: false + description: Equality filter for "likes" + in: query + style: form + explode: false + schema: + type: array + items: + type: string responses: '200': description: Successful operation @@ -1876,230 +1918,657 @@ paths: application/vnd.api+json: schema: $ref: '#/components/schemas/_errorResponse' -components: - schemas: - _jsonapi: - type: object - description: An object describing the server’s implementation - required: - - version - properties: - version: - type: string - _meta: - type: object - description: Meta information about the request or response - properties: - serialization: - description: Superjson serialization metadata - additionalProperties: true - _resourceIdentifier: - type: object - description: Identifier for a resource - required: - - type - - id - properties: - type: - type: string - description: Resource type - id: - type: string - description: Resource id - _resource: - allOf: - - $ref: '#/components/schemas/_resourceIdentifier' - - type: object - description: A resource with attributes and relationships - properties: - attributes: - type: object - description: Resource attributes - relationships: - type: object - description: Resource relationships - _links: - type: object - required: - - self - description: Links related to the resource - properties: - self: - type: string - description: Link for refetching the curent results - _pagination: - type: object - description: Pagination information - required: - - first - - last - - prev - - next - properties: - first: - type: string - description: Link to the first page - nullable: true - last: - type: string - description: Link to the last page - nullable: true - prev: - type: string - description: Link to the previous page - nullable: true - next: - type: string - description: Link to the next page - nullable: true - _errors: - type: array - description: An array of error objects - items: - type: object - required: - - status - - code - properties: - status: - type: string - description: HTTP status - code: - type: string - description: Error code - prismaCode: - type: string - description: Prisma error code if the error is thrown by Prisma - title: - type: string - description: Error title - detail: - type: string - description: Error detail - reason: - type: string - description: Detailed error reason - zodErrors: - type: object - additionalProperties: true - description: Zod validation errors if the error is due to data validation - failure - _errorResponse: - type: object - required: - - errors - description: An error response - properties: - jsonapi: - $ref: '#/components/schemas/_jsonapi' - errors: - $ref: '#/components/schemas/_errors' - _relationLinks: - type: object - required: - - self - - related - description: Links related to a relationship - properties: - self: - type: string - description: Link for fetching this relationship - related: - type: string - description: Link for fetching the resource represented by this relationship - _toOneRelationship: - type: object - description: A to-one relationship - properties: - data: - allOf: - - $ref: '#/components/schemas/_resourceIdentifier' - nullable: true - _toOneRelationshipWithLinks: - type: object - required: - - links - - data - description: A to-one relationship with links - properties: - links: - $ref: '#/components/schemas/_relationLinks' - data: - allOf: - - $ref: '#/components/schemas/_resourceIdentifier' - nullable: true - _toManyRelationship: - type: object - required: - - data - description: A to-many relationship - properties: - data: - type: array - items: - $ref: '#/components/schemas/_resourceIdentifier' - _toManyRelationshipWithLinks: - type: object - required: - - links - - data - description: A to-many relationship with links - properties: - links: - $ref: '#/components/schemas/_pagedRelationLinks' - data: - type: array - items: - $ref: '#/components/schemas/_resourceIdentifier' - _pagedRelationLinks: - description: Relationship links with pagination information - allOf: - - $ref: '#/components/schemas/_pagination' - - $ref: '#/components/schemas/_relationLinks' - _toManyRelationshipRequest: - type: object - required: - - data - description: Input for manipulating a to-many relationship - properties: - data: - type: array - items: - $ref: '#/components/schemas/_resourceIdentifier' - _toOneRelationshipRequest: - description: Input for manipulating a to-one relationship - type: object - required: - - data - properties: - data: - $ref: '#/components/schemas/_resourceIdentifier' - nullable: true - _toManyRelationshipResponse: - description: Response for a to-many relationship - allOf: - - $ref: '#/components/schemas/_toManyRelationshipWithLinks' - - type: object - properties: - jsonapi: - $ref: '#/components/schemas/_jsonapi' - _toOneRelationshipResponse: - description: Response for a to-one relationship - allOf: - - $ref: '#/components/schemas/_toOneRelationshipWithLinks' - - type: object - properties: - jsonapi: - $ref: '#/components/schemas/_jsonapi' - role: - type: string - description: The "role" Enum - enum: - - USER - - ADMIN - User: - type: object - description: The "User" model + /postLike: + get: + operationId: list-PostLike + description: List "PostLike" resources + tags: + - postLike + parameters: + - $ref: '#/components/parameters/include' + - $ref: '#/components/parameters/sort' + - $ref: '#/components/parameters/page-offset' + - $ref: '#/components/parameters/page-limit' + - name: filter[post] + required: false + description: Equality filter for "post" + in: query + style: form + explode: false + schema: + type: string + - name: filter[user] + required: false + description: Equality filter for "user" + in: query + style: form + explode: false + schema: + type: string + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PostLikeListResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + post: + operationId: create-PostLike + description: Create a "PostLike" resource + tags: + - postLike + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PostLikeCreateRequest' + responses: + '201': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PostLikeResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '422': + description: Request is unprocessable due to validation errors + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + /postLike/{id}: + get: + operationId: fetch-PostLike + description: Fetch a "PostLike" resource + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + - $ref: '#/components/parameters/include' + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PostLikeResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + put: + operationId: update-PostLike-put + description: Update a "PostLike" resource + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PostLikeUpdateRequest' + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PostLikeResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '422': + description: Request is unprocessable due to validation errors + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + patch: + operationId: update-PostLike-patch + description: Update a "PostLike" resource + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PostLikeUpdateRequest' + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PostLikeResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '422': + description: Request is unprocessable due to validation errors + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + delete: + operationId: delete-PostLike + description: Delete a "PostLike" resource + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + responses: + '200': + description: Successful operation + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + /postLike/{id}/post: + get: + operationId: fetch-PostLike-related-post + description: Fetch the related "post" resource for "PostLike" + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + - $ref: '#/components/parameters/include' + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/post_ItemResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + /postLike/{id}/relationships/post: + get: + operationId: fetch-PostLike-relationship-post + description: Fetch the "post" relationships for a "PostLike" + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_toOneRelationshipResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + put: + operationId: update-PostLike-relationship-post-put + description: Update "post" relationship for a "PostLike" + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_toOneRelationshipRequest' + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_toOneRelationshipResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + patch: + operationId: update-PostLike-relationship-post-patch + description: Update "post" relationship for a "PostLike" + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_toOneRelationshipRequest' + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_toOneRelationshipResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + /postLike/{id}/user: + get: + operationId: fetch-PostLike-related-user + description: Fetch the related "user" resource for "PostLike" + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + - $ref: '#/components/parameters/include' + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/UserResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + /postLike/{id}/relationships/user: + get: + operationId: fetch-PostLike-relationship-user + description: Fetch the "user" relationships for a "PostLike" + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_toOneRelationshipResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + put: + operationId: update-PostLike-relationship-user-put + description: Update "user" relationship for a "PostLike" + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_toOneRelationshipRequest' + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_toOneRelationshipResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + patch: + operationId: update-PostLike-relationship-user-patch + description: Update "user" relationship for a "PostLike" + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_toOneRelationshipRequest' + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_toOneRelationshipResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' +components: + schemas: + _jsonapi: + type: object + description: An object describing the server’s implementation + required: + - version + properties: + version: + type: string + _meta: + type: object + description: Meta information about the request or response + properties: + serialization: + description: Superjson serialization metadata + additionalProperties: true + _resourceIdentifier: + type: object + description: Identifier for a resource + required: + - type + - id + properties: + type: + type: string + description: Resource type + id: + type: string + description: Resource id + _resource: + allOf: + - $ref: '#/components/schemas/_resourceIdentifier' + - type: object + description: A resource with attributes and relationships + properties: + attributes: + type: object + description: Resource attributes + relationships: + type: object + description: Resource relationships + _links: + type: object + required: + - self + description: Links related to the resource + properties: + self: + type: string + description: Link for refetching the curent results + _pagination: + type: object + description: Pagination information + required: + - first + - last + - prev + - next + properties: + first: + type: string + description: Link to the first page + nullable: true + last: + type: string + description: Link to the last page + nullable: true + prev: + type: string + description: Link to the previous page + nullable: true + next: + type: string + description: Link to the next page + nullable: true + _errors: + type: array + description: An array of error objects + items: + type: object + required: + - status + - code + properties: + status: + type: string + description: HTTP status + code: + type: string + description: Error code + prismaCode: + type: string + description: Prisma error code if the error is thrown by Prisma + title: + type: string + description: Error title + detail: + type: string + description: Error detail + reason: + type: string + description: Detailed error reason + zodErrors: + type: object + additionalProperties: true + description: Zod validation errors if the error is due to data validation + failure + _errorResponse: + type: object + required: + - errors + description: An error response + properties: + jsonapi: + $ref: '#/components/schemas/_jsonapi' + errors: + $ref: '#/components/schemas/_errors' + _relationLinks: + type: object + required: + - self + - related + description: Links related to a relationship + properties: + self: + type: string + description: Link for fetching this relationship + related: + type: string + description: Link for fetching the resource represented by this relationship + _toOneRelationship: + type: object + description: A to-one relationship + properties: + data: + allOf: + - $ref: '#/components/schemas/_resourceIdentifier' + nullable: true + _toOneRelationshipWithLinks: + type: object + required: + - links + - data + description: A to-one relationship with links + properties: + links: + $ref: '#/components/schemas/_relationLinks' + data: + allOf: + - $ref: '#/components/schemas/_resourceIdentifier' + nullable: true + _toManyRelationship: + type: object + required: + - data + description: A to-many relationship + properties: + data: + type: array + items: + $ref: '#/components/schemas/_resourceIdentifier' + _toManyRelationshipWithLinks: + type: object + required: + - links + - data + description: A to-many relationship with links + properties: + links: + $ref: '#/components/schemas/_pagedRelationLinks' + data: + type: array + items: + $ref: '#/components/schemas/_resourceIdentifier' + _pagedRelationLinks: + description: Relationship links with pagination information + allOf: + - $ref: '#/components/schemas/_pagination' + - $ref: '#/components/schemas/_relationLinks' + _toManyRelationshipRequest: + type: object + required: + - data + description: Input for manipulating a to-many relationship + properties: + data: + type: array + items: + $ref: '#/components/schemas/_resourceIdentifier' + _toOneRelationshipRequest: + description: Input for manipulating a to-one relationship + type: object + required: + - data + properties: + data: + $ref: '#/components/schemas/_resourceIdentifier' + nullable: true + _toManyRelationshipResponse: + description: Response for a to-many relationship + allOf: + - $ref: '#/components/schemas/_toManyRelationshipWithLinks' + - type: object + properties: + jsonapi: + $ref: '#/components/schemas/_jsonapi' + _toOneRelationshipResponse: + description: Response for a to-one relationship + allOf: + - $ref: '#/components/schemas/_toOneRelationshipWithLinks' + - type: object + properties: + jsonapi: + $ref: '#/components/schemas/_jsonapi' + role: + type: string + description: The "role" Enum + enum: + - USER + - ADMIN + User: + type: object + description: The "User" model required: - id - type @@ -2611,6 +3080,162 @@ components: allOf: - $ref: '#/components/schemas/_links' - $ref: '#/components/schemas/_pagination' + PostLike: + type: object + description: The "PostLike" model + required: + - id + - type + - attributes + properties: + id: + type: string + type: + type: string + attributes: + type: object + properties: + postId: + type: string + userId: + type: string + relationships: + type: object + properties: + post: + $ref: '#/components/schemas/_toOneRelationshipWithLinks' + user: + $ref: '#/components/schemas/_toOneRelationshipWithLinks' + PostLikeCreateRequest: + type: object + description: Input for creating a "PostLike" + required: + - data + properties: + data: + type: object + description: The "PostLike" model + required: + - id + - type + - attributes + properties: + type: + type: string + attributes: + type: object + required: + - postId + - userId + properties: + postId: + type: string + userId: + type: string + relationships: + type: object + properties: + post: + $ref: '#/components/schemas/_toOneRelationship' + user: + $ref: '#/components/schemas/_toOneRelationship' + meta: + $ref: '#/components/schemas/_meta' + PostLikeUpdateRequest: + type: object + description: Input for updating a "PostLike" + required: + - data + properties: + data: + type: object + description: The "PostLike" model + required: + - id + - type + - attributes + properties: + id: + type: string + type: + type: string + attributes: + type: object + properties: + postId: + type: string + userId: + type: string + relationships: + type: object + properties: + post: + $ref: '#/components/schemas/_toOneRelationship' + user: + $ref: '#/components/schemas/_toOneRelationship' + meta: + $ref: '#/components/schemas/_meta' + PostLikeResponse: + type: object + description: Response for a "PostLike" + required: + - data + properties: + jsonapi: + $ref: '#/components/schemas/_jsonapi' + data: + allOf: + - $ref: '#/components/schemas/PostLike' + - type: object + properties: + relationships: + type: object + properties: + post: + $ref: '#/components/schemas/_toOneRelationship' + user: + $ref: '#/components/schemas/_toOneRelationship' + meta: + $ref: '#/components/schemas/_meta' + included: + type: array + items: + $ref: '#/components/schemas/_resource' + links: + $ref: '#/components/schemas/_links' + PostLikeListResponse: + type: object + description: Response for a list of "PostLike" + required: + - data + - links + properties: + jsonapi: + $ref: '#/components/schemas/_jsonapi' + data: + type: array + items: + allOf: + - $ref: '#/components/schemas/PostLike' + - type: object + properties: + relationships: + type: object + properties: + post: + $ref: '#/components/schemas/_toOneRelationship' + user: + $ref: '#/components/schemas/_toOneRelationship' + meta: + $ref: '#/components/schemas/_meta' + included: + type: array + items: + $ref: '#/components/schemas/_resource' + links: + allOf: + - $ref: '#/components/schemas/_links' + - $ref: '#/components/schemas/_pagination' parameters: id: name: id diff --git a/packages/plugins/openapi/tests/baseline/rest-3.1.0.baseline.yaml b/packages/plugins/openapi/tests/baseline/rest-3.1.0.baseline.yaml index e0c7cce3d..8096c90d6 100644 --- a/packages/plugins/openapi/tests/baseline/rest-3.1.0.baseline.yaml +++ b/packages/plugins/openapi/tests/baseline/rest-3.1.0.baseline.yaml @@ -9,6 +9,8 @@ tags: description: Profile operations - name: post_Item description: Post-related operations + - name: postLike + description: PostLike operations paths: /user: get: @@ -193,6 +195,16 @@ paths: explode: false schema: type: string + - name: filter[likes] + required: false + description: Equality filter for "likes" + in: query + style: form + explode: false + schema: + type: array + items: + type: string responses: '200': description: Successful operation @@ -543,6 +555,16 @@ paths: explode: false schema: type: string + - name: filter[likes] + required: false + description: Equality filter for "likes" + in: query + style: form + explode: false + schema: + type: array + items: + type: string responses: '200': description: Successful operation @@ -745,6 +767,16 @@ paths: explode: false schema: type: string + - name: filter[likes] + required: false + description: Equality filter for "likes" + in: query + style: form + explode: false + schema: + type: array + items: + type: string responses: '200': description: Successful operation @@ -1593,6 +1625,16 @@ paths: explode: false schema: type: string + - name: filter[likes] + required: false + description: Equality filter for "likes" + in: query + style: form + explode: false + schema: + type: array + items: + type: string responses: '200': description: Successful operation @@ -1876,230 +1918,657 @@ paths: application/vnd.api+json: schema: $ref: '#/components/schemas/_errorResponse' -components: - schemas: - _jsonapi: - type: object - description: An object describing the server’s implementation - required: - - version - properties: - version: - type: string - _meta: - type: object - description: Meta information about the request or response - properties: - serialization: - description: Superjson serialization metadata - additionalProperties: true - _resourceIdentifier: - type: object - description: Identifier for a resource - required: - - type - - id - properties: - type: - type: string - description: Resource type - id: - type: string - description: Resource id - _resource: - allOf: - - $ref: '#/components/schemas/_resourceIdentifier' - - type: object - description: A resource with attributes and relationships - properties: - attributes: - type: object - description: Resource attributes - relationships: - type: object - description: Resource relationships - _links: - type: object - required: - - self - description: Links related to the resource - properties: - self: - type: string - description: Link for refetching the curent results - _pagination: - type: object - description: Pagination information - required: - - first - - last - - prev - - next - properties: - first: - oneOf: - - type: 'null' - - type: string - description: Link to the first page - last: - oneOf: - - type: 'null' - - type: string - description: Link to the last page - prev: - oneOf: - - type: 'null' - - type: string - description: Link to the previous page - next: - oneOf: - - type: 'null' - - type: string - description: Link to the next page - _errors: - type: array - description: An array of error objects - items: - type: object - required: - - status - - code - properties: - status: - type: string - description: HTTP status - code: - type: string - description: Error code - prismaCode: - type: string - description: Prisma error code if the error is thrown by Prisma - title: - type: string - description: Error title - detail: - type: string - description: Error detail - reason: - type: string - description: Detailed error reason - zodErrors: - type: object - additionalProperties: true - description: Zod validation errors if the error is due to data validation - failure - _errorResponse: - type: object - required: - - errors - description: An error response - properties: - jsonapi: - $ref: '#/components/schemas/_jsonapi' - errors: - $ref: '#/components/schemas/_errors' - _relationLinks: - type: object - required: - - self - - related - description: Links related to a relationship - properties: - self: - type: string - description: Link for fetching this relationship - related: - type: string - description: Link for fetching the resource represented by this relationship - _toOneRelationship: - type: object - description: A to-one relationship - properties: - data: - oneOf: - - type: 'null' - - $ref: '#/components/schemas/_resourceIdentifier' - _toOneRelationshipWithLinks: - type: object - required: - - links - - data - description: A to-one relationship with links - properties: - links: - $ref: '#/components/schemas/_relationLinks' - data: - oneOf: - - type: 'null' - - $ref: '#/components/schemas/_resourceIdentifier' - _toManyRelationship: - type: object - required: - - data - description: A to-many relationship - properties: - data: - type: array - items: - $ref: '#/components/schemas/_resourceIdentifier' - _toManyRelationshipWithLinks: - type: object - required: - - links - - data - description: A to-many relationship with links - properties: - links: - $ref: '#/components/schemas/_pagedRelationLinks' - data: - type: array - items: - $ref: '#/components/schemas/_resourceIdentifier' - _pagedRelationLinks: - description: Relationship links with pagination information - allOf: - - $ref: '#/components/schemas/_pagination' - - $ref: '#/components/schemas/_relationLinks' - _toManyRelationshipRequest: - type: object - required: - - data - description: Input for manipulating a to-many relationship - properties: - data: - type: array - items: - $ref: '#/components/schemas/_resourceIdentifier' - _toOneRelationshipRequest: - description: Input for manipulating a to-one relationship - oneOf: - - type: 'null' - - type: object - required: - - data - properties: - data: - $ref: '#/components/schemas/_resourceIdentifier' - _toManyRelationshipResponse: - description: Response for a to-many relationship - allOf: - - $ref: '#/components/schemas/_toManyRelationshipWithLinks' - - type: object - properties: - jsonapi: - $ref: '#/components/schemas/_jsonapi' - _toOneRelationshipResponse: - description: Response for a to-one relationship - allOf: - - $ref: '#/components/schemas/_toOneRelationshipWithLinks' - - type: object - properties: - jsonapi: - $ref: '#/components/schemas/_jsonapi' - role: - type: string - description: The "role" Enum - enum: + /postLike: + get: + operationId: list-PostLike + description: List "PostLike" resources + tags: + - postLike + parameters: + - $ref: '#/components/parameters/include' + - $ref: '#/components/parameters/sort' + - $ref: '#/components/parameters/page-offset' + - $ref: '#/components/parameters/page-limit' + - name: filter[post] + required: false + description: Equality filter for "post" + in: query + style: form + explode: false + schema: + type: string + - name: filter[user] + required: false + description: Equality filter for "user" + in: query + style: form + explode: false + schema: + type: string + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PostLikeListResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + post: + operationId: create-PostLike + description: Create a "PostLike" resource + tags: + - postLike + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PostLikeCreateRequest' + responses: + '201': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PostLikeResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '422': + description: Request is unprocessable due to validation errors + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + /postLike/{id}: + get: + operationId: fetch-PostLike + description: Fetch a "PostLike" resource + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + - $ref: '#/components/parameters/include' + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PostLikeResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + put: + operationId: update-PostLike-put + description: Update a "PostLike" resource + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PostLikeUpdateRequest' + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PostLikeResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '422': + description: Request is unprocessable due to validation errors + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + patch: + operationId: update-PostLike-patch + description: Update a "PostLike" resource + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PostLikeUpdateRequest' + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/PostLikeResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '422': + description: Request is unprocessable due to validation errors + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + delete: + operationId: delete-PostLike + description: Delete a "PostLike" resource + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + responses: + '200': + description: Successful operation + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + /postLike/{id}/post: + get: + operationId: fetch-PostLike-related-post + description: Fetch the related "post" resource for "PostLike" + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + - $ref: '#/components/parameters/include' + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/post_ItemResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + /postLike/{id}/relationships/post: + get: + operationId: fetch-PostLike-relationship-post + description: Fetch the "post" relationships for a "PostLike" + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_toOneRelationshipResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + put: + operationId: update-PostLike-relationship-post-put + description: Update "post" relationship for a "PostLike" + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_toOneRelationshipRequest' + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_toOneRelationshipResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + patch: + operationId: update-PostLike-relationship-post-patch + description: Update "post" relationship for a "PostLike" + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_toOneRelationshipRequest' + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_toOneRelationshipResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + /postLike/{id}/user: + get: + operationId: fetch-PostLike-related-user + description: Fetch the related "user" resource for "PostLike" + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + - $ref: '#/components/parameters/include' + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/UserResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + /postLike/{id}/relationships/user: + get: + operationId: fetch-PostLike-relationship-user + description: Fetch the "user" relationships for a "PostLike" + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_toOneRelationshipResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + put: + operationId: update-PostLike-relationship-user-put + description: Update "user" relationship for a "PostLike" + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_toOneRelationshipRequest' + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_toOneRelationshipResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + patch: + operationId: update-PostLike-relationship-user-patch + description: Update "user" relationship for a "PostLike" + tags: + - postLike + parameters: + - $ref: '#/components/parameters/id' + requestBody: + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_toOneRelationshipRequest' + responses: + '200': + description: Successful operation + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_toOneRelationshipResponse' + '403': + description: Request is forbidden + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' + '404': + description: Resource is not found + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/_errorResponse' +components: + schemas: + _jsonapi: + type: object + description: An object describing the server’s implementation + required: + - version + properties: + version: + type: string + _meta: + type: object + description: Meta information about the request or response + properties: + serialization: + description: Superjson serialization metadata + additionalProperties: true + _resourceIdentifier: + type: object + description: Identifier for a resource + required: + - type + - id + properties: + type: + type: string + description: Resource type + id: + type: string + description: Resource id + _resource: + allOf: + - $ref: '#/components/schemas/_resourceIdentifier' + - type: object + description: A resource with attributes and relationships + properties: + attributes: + type: object + description: Resource attributes + relationships: + type: object + description: Resource relationships + _links: + type: object + required: + - self + description: Links related to the resource + properties: + self: + type: string + description: Link for refetching the curent results + _pagination: + type: object + description: Pagination information + required: + - first + - last + - prev + - next + properties: + first: + oneOf: + - type: 'null' + - type: string + description: Link to the first page + last: + oneOf: + - type: 'null' + - type: string + description: Link to the last page + prev: + oneOf: + - type: 'null' + - type: string + description: Link to the previous page + next: + oneOf: + - type: 'null' + - type: string + description: Link to the next page + _errors: + type: array + description: An array of error objects + items: + type: object + required: + - status + - code + properties: + status: + type: string + description: HTTP status + code: + type: string + description: Error code + prismaCode: + type: string + description: Prisma error code if the error is thrown by Prisma + title: + type: string + description: Error title + detail: + type: string + description: Error detail + reason: + type: string + description: Detailed error reason + zodErrors: + type: object + additionalProperties: true + description: Zod validation errors if the error is due to data validation + failure + _errorResponse: + type: object + required: + - errors + description: An error response + properties: + jsonapi: + $ref: '#/components/schemas/_jsonapi' + errors: + $ref: '#/components/schemas/_errors' + _relationLinks: + type: object + required: + - self + - related + description: Links related to a relationship + properties: + self: + type: string + description: Link for fetching this relationship + related: + type: string + description: Link for fetching the resource represented by this relationship + _toOneRelationship: + type: object + description: A to-one relationship + properties: + data: + oneOf: + - type: 'null' + - $ref: '#/components/schemas/_resourceIdentifier' + _toOneRelationshipWithLinks: + type: object + required: + - links + - data + description: A to-one relationship with links + properties: + links: + $ref: '#/components/schemas/_relationLinks' + data: + oneOf: + - type: 'null' + - $ref: '#/components/schemas/_resourceIdentifier' + _toManyRelationship: + type: object + required: + - data + description: A to-many relationship + properties: + data: + type: array + items: + $ref: '#/components/schemas/_resourceIdentifier' + _toManyRelationshipWithLinks: + type: object + required: + - links + - data + description: A to-many relationship with links + properties: + links: + $ref: '#/components/schemas/_pagedRelationLinks' + data: + type: array + items: + $ref: '#/components/schemas/_resourceIdentifier' + _pagedRelationLinks: + description: Relationship links with pagination information + allOf: + - $ref: '#/components/schemas/_pagination' + - $ref: '#/components/schemas/_relationLinks' + _toManyRelationshipRequest: + type: object + required: + - data + description: Input for manipulating a to-many relationship + properties: + data: + type: array + items: + $ref: '#/components/schemas/_resourceIdentifier' + _toOneRelationshipRequest: + description: Input for manipulating a to-one relationship + oneOf: + - type: 'null' + - type: object + required: + - data + properties: + data: + $ref: '#/components/schemas/_resourceIdentifier' + _toManyRelationshipResponse: + description: Response for a to-many relationship + allOf: + - $ref: '#/components/schemas/_toManyRelationshipWithLinks' + - type: object + properties: + jsonapi: + $ref: '#/components/schemas/_jsonapi' + _toOneRelationshipResponse: + description: Response for a to-one relationship + allOf: + - $ref: '#/components/schemas/_toOneRelationshipWithLinks' + - type: object + properties: + jsonapi: + $ref: '#/components/schemas/_jsonapi' + role: + type: string + description: The "role" Enum + enum: - USER - ADMIN User: @@ -2625,6 +3094,162 @@ components: allOf: - $ref: '#/components/schemas/_links' - $ref: '#/components/schemas/_pagination' + PostLike: + type: object + description: The "PostLike" model + required: + - id + - type + - attributes + properties: + id: + type: string + type: + type: string + attributes: + type: object + properties: + postId: + type: string + userId: + type: string + relationships: + type: object + properties: + post: + $ref: '#/components/schemas/_toOneRelationshipWithLinks' + user: + $ref: '#/components/schemas/_toOneRelationshipWithLinks' + PostLikeCreateRequest: + type: object + description: Input for creating a "PostLike" + required: + - data + properties: + data: + type: object + description: The "PostLike" model + required: + - id + - type + - attributes + properties: + type: + type: string + attributes: + type: object + required: + - postId + - userId + properties: + postId: + type: string + userId: + type: string + relationships: + type: object + properties: + post: + $ref: '#/components/schemas/_toOneRelationship' + user: + $ref: '#/components/schemas/_toOneRelationship' + meta: + $ref: '#/components/schemas/_meta' + PostLikeUpdateRequest: + type: object + description: Input for updating a "PostLike" + required: + - data + properties: + data: + type: object + description: The "PostLike" model + required: + - id + - type + - attributes + properties: + id: + type: string + type: + type: string + attributes: + type: object + properties: + postId: + type: string + userId: + type: string + relationships: + type: object + properties: + post: + $ref: '#/components/schemas/_toOneRelationship' + user: + $ref: '#/components/schemas/_toOneRelationship' + meta: + $ref: '#/components/schemas/_meta' + PostLikeResponse: + type: object + description: Response for a "PostLike" + required: + - data + properties: + jsonapi: + $ref: '#/components/schemas/_jsonapi' + data: + allOf: + - $ref: '#/components/schemas/PostLike' + - type: object + properties: + relationships: + type: object + properties: + post: + $ref: '#/components/schemas/_toOneRelationship' + user: + $ref: '#/components/schemas/_toOneRelationship' + meta: + $ref: '#/components/schemas/_meta' + included: + type: array + items: + $ref: '#/components/schemas/_resource' + links: + $ref: '#/components/schemas/_links' + PostLikeListResponse: + type: object + description: Response for a list of "PostLike" + required: + - data + - links + properties: + jsonapi: + $ref: '#/components/schemas/_jsonapi' + data: + type: array + items: + allOf: + - $ref: '#/components/schemas/PostLike' + - type: object + properties: + relationships: + type: object + properties: + post: + $ref: '#/components/schemas/_toOneRelationship' + user: + $ref: '#/components/schemas/_toOneRelationship' + meta: + $ref: '#/components/schemas/_meta' + included: + type: array + items: + $ref: '#/components/schemas/_resource' + links: + allOf: + - $ref: '#/components/schemas/_links' + - $ref: '#/components/schemas/_pagination' parameters: id: name: id diff --git a/packages/plugins/openapi/tests/openapi-restful.test.ts b/packages/plugins/openapi/tests/openapi-restful.test.ts index 9997dd540..8fd0880ff 100644 --- a/packages/plugins/openapi/tests/openapi-restful.test.ts +++ b/packages/plugins/openapi/tests/openapi-restful.test.ts @@ -35,6 +35,7 @@ model User { role role @default(USER) posts post_Item[] profile Profile? + likes PostLike[] } model Profile { @@ -55,12 +56,21 @@ model post_Item { published Boolean @default(false) viewCount Int @default(0) notes String? + likes PostLike[] @@openapi.meta({ tagDescription: 'Post-related operations' }) } +model PostLike { + post post_Item @relation(fields: [postId], references: [id]) + postId String + user User @relation(fields: [userId], references: [id]) + userId String + @@id([postId, userId]) +} + model Foo { id String @id @@openapi.ignore @@ -98,9 +108,15 @@ model Bar { expect(api.paths?.['/user/{id}/relationships/posts']?.['get']).toBeTruthy(); expect(api.paths?.['/user/{id}/relationships/posts']?.['post']).toBeTruthy(); expect(api.paths?.['/user/{id}/relationships/posts']?.['patch']).toBeTruthy(); + expect(api.paths?.['/user/{id}/relationships/likes']?.['get']).toBeTruthy(); + expect(api.paths?.['/user/{id}/relationships/likes']?.['post']).toBeTruthy(); + expect(api.paths?.['/user/{id}/relationships/likes']?.['patch']).toBeTruthy(); expect(api.paths?.['/post_Item/{id}/relationships/author']?.['get']).toBeTruthy(); expect(api.paths?.['/post_Item/{id}/relationships/author']?.['post']).toBeUndefined(); expect(api.paths?.['/post_Item/{id}/relationships/author']?.['patch']).toBeTruthy(); + expect(api.paths?.['/post_Item/{id}/relationships/likes']?.['get']).toBeTruthy(); + expect(api.paths?.['/post_Item/{id}/relationships/likes']?.['post']).toBeTruthy(); + expect(api.paths?.['/post_Item/{id}/relationships/likes']?.['patch']).toBeTruthy(); expect(api.paths?.['/foo']).toBeUndefined(); expect(api.paths?.['/bar']).toBeUndefined(); @@ -323,6 +339,35 @@ model Foo { expect(parsed).toMatchObject(baseline); } }); + + it('exposes individual fields from a compound id as attributes', async () => { + const { model, dmmf, modelFile } = await loadZModelAndDmmf(` +plugin openapi { + provider = '${normalizePath(path.resolve(__dirname, '../dist'))}' +} + +model User { + email String + role String + company String + @@id([role, company]) +} + `); + + const { name: output } = tmp.fileSync({ postfix: '.yaml' }); + + const options = buildOptions(model, modelFile, output, '3.1.0'); + await generate(model, options, dmmf); + + await OpenAPIParser.validate(output); + + const parsed = YAML.parse(fs.readFileSync(output, 'utf-8')); + expect(parsed.openapi).toBe('3.1.0'); + + expect(Object.keys(parsed.components.schemas.User.properties.attributes.properties)).toEqual( + expect.arrayContaining(['role', 'company']) + ); + }); }); function buildOptions(model: Model, modelFile: string, output: string, specVersion = '3.0.0') {