From e7b0af1fca5d9962b43cd9bdeed209b8c4633419 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 23 Jun 2026 12:30:18 -0500 Subject: [PATCH] docs: add OpenAPI 3.0 spec for REST API Documents the /api/v1/mokoog/tags endpoints with full request/response schemas, authentication, and examples. Closes #80 --- openapi.yaml | 668 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 668 insertions(+) create mode 100644 openapi.yaml diff --git a/openapi.yaml b/openapi.yaml new file mode 100644 index 0000000..c7508b5 --- /dev/null +++ b/openapi.yaml @@ -0,0 +1,668 @@ +openapi: 3.0.3 + +info: + title: MokoSuiteOpenGraph API + version: 1.0.0 + description: | + REST API for managing Open Graph, SEO meta, and structured-data tags in + Joomla via the MokoSuiteOpenGraph extension. + + The API follows Joomla's Web Services conventions and returns responses in + [JSON:API](https://jsonapi.org/) format. All endpoints require + authentication via a Joomla API token. + contact: + name: Moko Consulting + email: hello@mokoconsulting.tech + license: + name: GPL-3.0-or-later + url: https://www.gnu.org/licenses/gpl-3.0.html + +servers: + - url: /api/index.php/v1 + description: Joomla Web Services API + +security: + - apiToken: [] + +tags: + - name: Tags + description: CRUD operations for Open Graph tag records + +paths: + /mokoog/tags: + get: + operationId: listTags + summary: List OG tags + description: | + Returns a paginated collection of OG tag records. Supports filtering + by content type, published state, and language. + tags: [Tags] + parameters: + - name: "filter[content_type]" + in: query + description: Filter by content type (e.g. `com_content`, `menu`, `com_mokoshop`) + schema: + type: string + example: com_content + - name: "filter[content_id]" + in: query + description: Filter by content ID + schema: + type: integer + example: 42 + - name: "filter[published]" + in: query + description: Filter by published state + schema: + type: integer + enum: [0, 1] + - name: "filter[language]" + in: query + description: Filter by language tag (e.g. `en-GB`, `*`) + schema: + type: string + example: "*" + - name: "filter[search]" + in: query + description: Free-text search across tag fields + schema: + type: string + - name: "page[offset]" + in: query + description: Number of records to skip (pagination offset) + schema: + type: integer + minimum: 0 + default: 0 + - name: "page[limit]" + in: query + description: Maximum number of records to return + schema: + type: integer + minimum: 1 + maximum: 100 + default: 25 + - name: "list[fullordering]" + in: query + description: Sort order for results + schema: + type: string + enum: + - a.id ASC + - a.id DESC + - a.og_title ASC + - a.og_title DESC + - a.modified ASC + - a.modified DESC + default: a.modified DESC + responses: + "200": + description: A JSON:API collection of OG tags + content: + application/vnd.api+json: + schema: + $ref: "#/components/schemas/TagCollection" + example: + links: + self: "/api/index.php/v1/mokoog/tags" + data: + - type: tags + id: "1" + attributes: + content_type: com_content + content_id: 42 + og_title: "My Article Title" + og_description: "A brief description for social sharing." + og_image: "images/mokoog/og-banner.jpg" + og_type: article + seo_title: "My Article | Example Site" + meta_description: "A brief meta description for search engines." + robots: "index, follow" + canonical_url: "https://example.com/my-article" + language: "*" + published: 1 + created: "2026-06-01T12:00:00+00:00" + modified: "2026-06-15T08:30:00+00:00" + meta: + total-pages: 1 + "401": + $ref: "#/components/responses/Unauthorized" + + post: + operationId: createTag + summary: Create an OG tag + description: | + Creates a new OG tag record. The combination of `content_type`, + `content_id`, and `language` must be unique. + tags: [Tags] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/TagCreateRequest" + example: + content_type: com_content + content_id: 42 + og_title: "My Article Title" + og_description: "A brief description for social sharing." + og_image: "images/mokoog/og-banner.jpg" + og_type: article + language: "*" + published: 1 + responses: + "200": + description: The created tag + content: + application/vnd.api+json: + schema: + $ref: "#/components/schemas/TagDocument" + example: + links: + self: "/api/index.php/v1/mokoog/tags/1" + data: + type: tags + id: "1" + attributes: + content_type: com_content + content_id: 42 + og_title: "My Article Title" + og_description: "A brief description for social sharing." + og_image: "images/mokoog/og-banner.jpg" + og_type: article + seo_title: "" + meta_description: "" + robots: "" + canonical_url: "" + language: "*" + published: 1 + created: "2026-06-23T10:00:00+00:00" + modified: "2026-06-23T10:00:00+00:00" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + + /mokoog/tags/{id}: + parameters: + - $ref: "#/components/parameters/TagId" + + get: + operationId: getTag + summary: Get a single OG tag + tags: [Tags] + responses: + "200": + description: A single OG tag resource + content: + application/vnd.api+json: + schema: + $ref: "#/components/schemas/TagDocument" + example: + links: + self: "/api/index.php/v1/mokoog/tags/1" + data: + type: tags + id: "1" + attributes: + content_type: com_content + content_id: 42 + og_title: "My Article Title" + og_description: "A brief description for social sharing." + og_image: "images/mokoog/og-banner.jpg" + og_type: article + seo_title: "My Article | Example Site" + meta_description: "A brief meta description for search engines." + robots: "index, follow" + canonical_url: "https://example.com/my-article" + language: "*" + published: 1 + created: "2026-06-01T12:00:00+00:00" + modified: "2026-06-15T08:30:00+00:00" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + + patch: + operationId: updateTag + summary: Update an OG tag + description: Partially updates an existing OG tag. Only supplied fields are changed. + tags: [Tags] + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/TagUpdateRequest" + example: + og_title: "Updated Title" + og_description: "Updated social description." + published: 0 + responses: + "200": + description: The updated tag + content: + application/vnd.api+json: + schema: + $ref: "#/components/schemas/TagDocument" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + + delete: + operationId: deleteTag + summary: Delete an OG tag + tags: [Tags] + responses: + "204": + description: Tag deleted successfully + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + + /mokoog/lookup/{content_type}/{content_id}: + get: + operationId: lookupTag + summary: Look up an OG tag by content type and content ID + description: | + Resolves an OG tag by its `content_type` and `content_id` pair and + returns the full tag resource. This is a convenience endpoint that + avoids the caller needing to know the internal tag ID. + tags: [Tags] + parameters: + - name: content_type + in: path + required: true + description: | + The content type identifier (e.g. `com_content`, `menu`, + `com_mokoshop`). Must match the pattern `[a-z][a-z0-9_.]*`. + schema: + type: string + pattern: "^[a-z][a-z0-9_.]*$" + example: com_content + - name: content_id + in: path + required: true + description: The content item ID + schema: + type: integer + minimum: 1 + example: 42 + responses: + "200": + description: The matching OG tag resource + content: + application/vnd.api+json: + schema: + $ref: "#/components/schemas/TagDocument" + "400": + description: Missing or invalid content_type / content_id + content: + application/vnd.api+json: + schema: + $ref: "#/components/schemas/ErrorResponse" + example: + errors: + - title: Bad Request + status: 400 + detail: "content_type and content_id are required" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + description: No OG tag found for the given content_type and content_id + content: + application/vnd.api+json: + schema: + $ref: "#/components/schemas/ErrorResponse" + example: + errors: + - title: Not Found + status: 404 + detail: "OG tag not found for com_content:42" + +components: + securitySchemes: + apiToken: + type: apiKey + name: X-Joomla-Token + in: header + description: | + Joomla API token. Can also be passed as the `api-token` query + parameter. Generate a token from the Joomla administrator panel + under Users > Manage > [user] > Joomla API Token tab. + + parameters: + TagId: + name: id + in: path + required: true + description: The OG tag record ID + schema: + type: integer + minimum: 1 + example: 1 + + schemas: + TagAttributes: + type: object + description: Full set of OG tag attributes returned by the API + properties: + content_type: + type: string + description: | + Content type identifier (e.g. `com_content`, `menu`, + `com_mokoshop`). Must match `[a-z][a-z0-9_.]*`. + pattern: "^[a-z][a-z0-9_.]*$" + maxLength: 100 + example: com_content + content_id: + type: integer + description: The ID of the associated content item + minimum: 1 + example: 42 + og_title: + type: string + description: Open Graph title (`og:title`) + maxLength: 255 + example: "My Article Title" + og_description: + type: string + description: Open Graph description (`og:description`) + example: "A brief description for social sharing." + og_image: + type: string + description: Relative path to the Open Graph image (`og:image`) + maxLength: 512 + example: "images/mokoog/og-banner.jpg" + og_type: + type: string + description: Open Graph type (`og:type`) + default: article + enum: + - article + - website + - product + - profile + - book + - music.song + - music.album + - video.movie + - video.episode + - video.other + example: article + seo_title: + type: string + description: SEO page title (used in `` tag) + maxLength: 70 + example: "My Article | Example Site" + meta_description: + type: string + description: Meta description for search engines + maxLength: 200 + example: "A brief meta description for search engines." + robots: + type: string + description: | + Comma-separated robots directives. Valid directives: `index`, + `noindex`, `follow`, `nofollow`, `none`, `noarchive`, + `nosnippet`, `noimageindex`, `max-snippet`, `max-image-preview`. + maxLength: 100 + example: "index, follow" + canonical_url: + type: string + format: uri + description: Canonical URL for the page + maxLength: 512 + example: "https://example.com/my-article" + language: + type: string + description: Joomla language tag (`*` for all languages) + maxLength: 7 + default: "*" + example: "*" + published: + type: integer + description: Published state (1 = published, 0 = unpublished) + enum: [0, 1] + default: 1 + example: 1 + created: + type: string + format: date-time + description: Record creation timestamp (read-only) + readOnly: true + example: "2026-06-01T12:00:00+00:00" + modified: + type: string + format: date-time + description: Last modification timestamp (read-only) + readOnly: true + example: "2026-06-15T08:30:00+00:00" + + TagResource: + type: object + description: A single OG tag in JSON:API resource format + required: [type, id, attributes] + properties: + type: + type: string + enum: [tags] + example: tags + id: + type: string + description: The record ID as a string (per JSON:API spec) + example: "1" + attributes: + $ref: "#/components/schemas/TagAttributes" + + TagDocument: + type: object + description: JSON:API document containing a single tag resource + properties: + links: + type: object + properties: + self: + type: string + example: "/api/index.php/v1/mokoog/tags/1" + data: + $ref: "#/components/schemas/TagResource" + + TagCollection: + type: object + description: JSON:API document containing a collection of tag resources + properties: + links: + type: object + properties: + self: + type: string + example: "/api/index.php/v1/mokoog/tags" + data: + type: array + items: + $ref: "#/components/schemas/TagResource" + meta: + type: object + properties: + total-pages: + type: integer + description: Total number of pages available + example: 1 + + TagCreateRequest: + type: object + description: Request body for creating a new OG tag + required: + - content_type + - content_id + properties: + content_type: + type: string + pattern: "^[a-z][a-z0-9_.]*$" + maxLength: 100 + example: com_content + content_id: + type: integer + minimum: 1 + example: 42 + og_title: + type: string + maxLength: 255 + og_description: + type: string + og_image: + type: string + maxLength: 512 + og_type: + type: string + default: article + enum: + - article + - website + - product + - profile + - book + - music.song + - music.album + - video.movie + - video.episode + - video.other + og_video: + type: string + format: uri + description: Open Graph video URL (`og:video`) + maxLength: 512 + seo_title: + type: string + maxLength: 70 + meta_description: + type: string + maxLength: 200 + robots: + type: string + maxLength: 100 + canonical_url: + type: string + format: uri + maxLength: 512 + language: + type: string + maxLength: 7 + default: "*" + published: + type: integer + enum: [0, 1] + default: 1 + + TagUpdateRequest: + type: object + description: | + Request body for updating an OG tag. All fields are optional; only + supplied fields are modified. + properties: + og_title: + type: string + maxLength: 255 + og_description: + type: string + og_image: + type: string + maxLength: 512 + og_type: + type: string + enum: + - article + - website + - product + - profile + - book + - music.song + - music.album + - video.movie + - video.episode + - video.other + og_video: + type: string + format: uri + maxLength: 512 + seo_title: + type: string + maxLength: 70 + meta_description: + type: string + maxLength: 200 + robots: + type: string + maxLength: 100 + canonical_url: + type: string + format: uri + maxLength: 512 + language: + type: string + maxLength: 7 + published: + type: integer + enum: [0, 1] + + ErrorResponse: + type: object + description: JSON:API error response + properties: + errors: + type: array + items: + type: object + properties: + title: + type: string + example: Not Found + status: + type: integer + example: 404 + detail: + type: string + example: "Item not found." + + responses: + BadRequest: + description: Invalid request data + content: + application/vnd.api+json: + schema: + $ref: "#/components/schemas/ErrorResponse" + example: + errors: + - title: Bad Request + status: 400 + detail: "Content type is required." + + Unauthorized: + description: Missing or invalid API token + content: + application/vnd.api+json: + schema: + $ref: "#/components/schemas/ErrorResponse" + example: + errors: + - title: Forbidden + status: 403 + detail: "You are not authorised to access this resource." + + NotFound: + description: Resource not found + content: + application/vnd.api+json: + schema: + $ref: "#/components/schemas/ErrorResponse" + example: + errors: + - title: Not Found + status: 404 + detail: "Item not found."