From 63dd11581c8f2b36bf7c262bb7bf12d400d38f01 Mon Sep 17 00:00:00 2001 From: Pasha Zayko Date: Wed, 3 Sep 2025 17:50:53 -0400 Subject: [PATCH 1/3] Updating spec to match code changes Adding new fields available in the database record Adjusting auth.id call format to match the rest of the spec file --- specs/Url-Shortener.json | 118 +++++++++++++----- src/urlShortener/TypeScript/package-lock.json | 4 +- src/urlShortener/TypeScript/package.json | 2 +- 3 files changed, 91 insertions(+), 33 deletions(-) diff --git a/specs/Url-Shortener.json b/specs/Url-Shortener.json index fa8a0c0..6c555ac 100644 --- a/specs/Url-Shortener.json +++ b/specs/Url-Shortener.json @@ -33,6 +33,41 @@ } }, "schemas": { + "Auth.Id": { + "title": "Auth IDs required to authenticate", + "description": "Object containing IDs necessary to perform user authentication in the EntraID.", + "examples": [ + { + "appId": "2a7e1b3c-4d5f-4a8b-9e6a-1c2b7f3d8e4a", + "tenantId": "9c1e7a2b-5d3f-4a8b-2c6e-7f1a3d9e8b5c" + } + ], + "properties": { + "appId": { + "description": "Application ID that should be used in Access Tokens as the audience and the endpoint necessary for auth code flows.", + "type": "string", + "format": "uuid", + "maxLength": 36, + "minLength": 36, + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", + "readOnly": true + }, + "tenantId": { + "description": "Tenant ID necessary for authority host URL configuration and UI customization.", + "type": "string", + "format": "uuid", + "maxLength": 36, + "minLength": 36, + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", + "readOnly": true + } + }, + "type": "object", + "required": [ + "appId", + "tenantId" + ] + }, "Core.HealthReport": { "title": "Core System - Health Report", "description": "Health report that indicates if a service is down or not that the URL Shortener relies on.", @@ -73,9 +108,14 @@ "sourceUrl": "https://www.example.com/vanityUrl", "createdBy": "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45", "customUserAgentMatcher": null, + "expectedBrokenScanStatusCode": null, "domainConfigId": "9a1e7b2c-5d3f-4a8b-9c2e-6f1a3d7e8b54", + "enabled": true, + "enableLinkBrokenScan": false, "updatedBy": "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45", "targetUrl": "https://www.example.com/very-long-and-complex-url-with_plenty-of-special-characters-and-non-repeated-words", + "notEnabledAfter": "2025-12-31T23:59:59Z", + "notEnabledBefore": null, "targetUrlCustom": null, "targetUrlMobile": null, "targetUrlNodeJs": null, @@ -116,6 +156,13 @@ ], "maxLength": 512 }, + "expectedBrokenScanStatusCode": { + "description": "Status code expected by the scheduled scan results.", + "type": [ + "integer", + "null" + ] + }, "domainConfigId": { "description": "Object ID of the domain configuration that this redirect record is associated with.", "type": "string", @@ -125,6 +172,14 @@ "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", "readOnly": true }, + "enabled": { + "description": " Flag that indicates if the created URL is enabled (`true`) or not (`false`).", + "type": "boolean" + }, + "enableLinkBrokenScan": { + "description": "Flag that enables (`true`) or disables (`false`) scans happening against the target to check if broken.", + "type": "boolean" + }, "updatedBy": { "description": "Object ID of the last user who updated this record.", "type": "string", @@ -138,6 +193,22 @@ "type": "string", "format": "uri" }, + "notEnabledAfter": { + "description": "Date that when present, indicates when the link expires.", + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "notEnabledBefore": { + "description": "Date that when present, indicates when the link goes live.", + "type": [ + "string", + "null" + ], + "format": "date-time" + }, "targetUrlCustom": { "description": "Target URL for custom defined and matching user agent clients.", "type": [ @@ -193,9 +264,14 @@ "sourceUrl", "createdBy", "customUserAgentMatcher", + "expectedBrokenScanStatusCode", "domainConfigId", + "enabled", + "enableLinkBrokenScan", "updatedBy", "targetUrl", + "notEnabledAfter", + "notEnabledBefore", "targetUrlCustom", "targetUrlMobile", "targetUrlNodeJs", @@ -410,48 +486,25 @@ }, "description": "Create vanity or numeric short version of your desired URL. Share it digitally, as QR code, or print it.", "title": "SHI URL Shortener", - "version": "0.0.2" + "version": "0.0.3" }, "openapi": "3.1.1", "paths": { "/Api/Auth/Id": { "get": { + "summary": "Retrieves the IDs required to authenticate.", "description": "Provides the Tenant ID and the Application ID of the service principal that access tokens need to be issued against. This is also useful for configuring public clients to be able to authenticate to for auth code flows.", "operationId": "/Api/Auth/Id/Get", "responses": { "200": { + "description": "OK", "content": { "application/json": { "schema": { - "properties": { - "appId": { - "description": "Application ID that should be used in Access Tokens as the audience and the endpoint necessary for auth code flows.", - "type": "string", - "format": "uuid", - "maxLength": 36, - "minLength": 36, - "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", - "readOnly": true - }, - "tenantId": { - "description": "Tenant ID necessary for authority host URL configuration and UI customization.", - "type": "string", - "format": "uuid", - "maxLength": 36, - "minLength": 36, - "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", - "readOnly": true - } - }, - "type": "object", - "required": [ - "appId", - "tenantId" - ] + "$ref": "#/components/schemas/Auth.Id" } } - }, - "description": "OK" + } }, "500": { "$ref": "#/components/responses/500" @@ -460,8 +513,7 @@ "tags": [ "Core" ], - "security": [], - "summary": "Retrieves the IDs required to authenticate." + "security": [] } }, "/Api/Core/Health": { @@ -682,12 +734,18 @@ "Complete and proper payload": { "description": "Request where all required fields are present and valid.", "value": { + "id": "7e2b1c8a-4f3d-4e2a-9b1a-2c6e8d7f1a23", "sourceUrl": "https://www.example.com/long-and-complex-url", "createdBy": "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45", "customUserAgentMatcher": null, + "expectedBrokenScanStatusCode": null, "domainConfigId": "9a1e7b2c-5d3f-4a8b-9c2e-6f1a3d7e8b54", + "enabled": true, + "enableLinkBrokenScan": false, "updatedBy": "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45", "targetUrl": "https://www.example.com/target", + "notEnabledAfter": null, + "notEnabledBefore": null, "targetUrlCustom": null, "targetUrlMobile": null, "targetUrlNodeJs": null, diff --git a/src/urlShortener/TypeScript/package-lock.json b/src/urlShortener/TypeScript/package-lock.json index 77eaaf0..e0253d6 100644 --- a/src/urlShortener/TypeScript/package-lock.json +++ b/src/urlShortener/TypeScript/package-lock.json @@ -1,12 +1,12 @@ { "name": "@shi-corp/sdk-url-shortener", - "version": "0.0.3", + "version": "0.0.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@shi-corp/sdk-url-shortener", - "version": "0.0.3", + "version": "0.0.4", "license": "MIT", "dependencies": { "@microsoft/kiota-authentication-azure": "~1.0.0-preview.97", diff --git a/src/urlShortener/TypeScript/package.json b/src/urlShortener/TypeScript/package.json index 158d60f..915ee32 100644 --- a/src/urlShortener/TypeScript/package.json +++ b/src/urlShortener/TypeScript/package.json @@ -1,6 +1,6 @@ { "name": "@shi-corp/sdk-url-shortener", - "version": "0.0.3", + "version": "0.0.4", "type": "module", "main": "bin/index.js", "description": "SDK client used to interface with the URL Shortener application.", From f64f0214ec03a9a09ba8e0d559800fb04584efa7 Mon Sep 17 00:00:00 2001 From: Pasha Zayko Date: Thu, 9 Oct 2025 16:34:24 -0400 Subject: [PATCH 2/3] Adding missing features to be spec-complete Enhancing definitions with example sections too fully match OpenApi spec v3.1 --- specs/Url-Shortener.json | 570 +++++++++++++++++++++++++++++++++++---- 1 file changed, 514 insertions(+), 56 deletions(-) diff --git a/specs/Url-Shortener.json b/specs/Url-Shortener.json index 6c555ac..7853def 100644 --- a/specs/Url-Shortener.json +++ b/specs/Url-Shortener.json @@ -50,7 +50,10 @@ "maxLength": 36, "minLength": 36, "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", - "readOnly": true + "readOnly": true, + "examples": [ + "2a7e1b3c-4d5f-4a8b-9e6a-1c2b7f3d8e4a" + ] }, "tenantId": { "description": "Tenant ID necessary for authority host URL configuration and UI customization.", @@ -59,7 +62,10 @@ "maxLength": 36, "minLength": 36, "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", - "readOnly": true + "readOnly": true, + "examples": [ + "9c1e7a2b-5d3f-4a8b-2c6e-7f1a3d9e8b5c" + ] } }, "type": "object", @@ -81,15 +87,24 @@ "properties": { "authClient": { "description": "Flag that indicates if the client side authentication validation is working or not.", - "type": "boolean" + "type": "boolean", + "examples": [ + true + ] }, "authServer": { "description": "Flag that indicates if the server side authentication is working or not.", - "type": "boolean" + "type": "boolean", + "examples": [ + true + ] }, "database": { "description": "Flag that indicates if the ORM (Database) system is down (`false`) or not (`true`). False indicate the service is not working, true indicates the service is working.", - "type": "boolean" + "type": "boolean", + "examples": [ + false + ] } }, "type": "object", @@ -132,12 +147,18 @@ "maxLength": 36, "minLength": 36, "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", - "readOnly": true + "readOnly": true, + "examples": [ + "7e2b1c8a-4f3d-4e2a-9b1a-2c6e8d7f1a23" + ] }, "sourceUrl": { "description": "The codified version of the full URL that a user would be navigating to. It has to be unique in the table as it will be used during lookup.", "type": "string", - "format": "uri" + "format": "uri", + "examples": [ + "https://www.example.com/vanityUrl" + ] }, "createdBy": { "description": "Object ID of the user who initially creates this record.", @@ -146,7 +167,10 @@ "maxLength": 36, "minLength": 36, "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", - "readOnly": true + "readOnly": true, + "examples": [ + "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45" + ] }, "customUserAgentMatcher": { "description": "Custom user agent matcher to be used with the custom target url. Custom target url is non-operable if this is not set. String max char count is 512.", @@ -154,13 +178,19 @@ "string", "null" ], - "maxLength": 512 + "maxLength": 512, + "examples": [ + null + ] }, "expectedBrokenScanStatusCode": { "description": "Status code expected by the scheduled scan results.", "type": [ "integer", "null" + ], + "examples": [ + 200 ] }, "domainConfigId": { @@ -170,15 +200,24 @@ "maxLength": 36, "minLength": 36, "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", - "readOnly": true + "readOnly": true, + "examples": [ + "9a1e7b2c-5d3f-4a8b-9c2e-6f1a3d7e8b54" + ] }, "enabled": { "description": " Flag that indicates if the created URL is enabled (`true`) or not (`false`).", - "type": "boolean" + "type": "boolean", + "examples": [ + true + ] }, "enableLinkBrokenScan": { "description": "Flag that enables (`true`) or disables (`false`) scans happening against the target to check if broken.", - "type": "boolean" + "type": "boolean", + "examples": [ + false + ] }, "updatedBy": { "description": "Object ID of the last user who updated this record.", @@ -186,12 +225,18 @@ "format": "uuid", "maxLength": 36, "minLength": 36, - "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", + "examples": [ + "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45" + ] }, "targetUrl": { "description": "Destination that the client will be redirected to if the source is matched. This is the default URL that will be used when no user agent is matched.", "type": "string", - "format": "uri" + "format": "uri", + "examples": [ + "https://www.example.com/very-long-and-complex-url-with_plenty-of-special-characters-and-non-repeated-words" + ] }, "notEnabledAfter": { "description": "Date that when present, indicates when the link expires.", @@ -199,7 +244,10 @@ "string", "null" ], - "format": "date-time" + "format": "date-time", + "examples": [ + "2025-12-31T23:59:59Z" + ] }, "notEnabledBefore": { "description": "Date that when present, indicates when the link goes live.", @@ -207,7 +255,10 @@ "string", "null" ], - "format": "date-time" + "format": "date-time", + "examples": [ + null + ] }, "targetUrlCustom": { "description": "Target URL for custom defined and matching user agent clients.", @@ -215,7 +266,10 @@ "string", "null" ], - "format": "uri" + "format": "uri", + "examples": [ + null + ] }, "targetUrlMobile": { "description": "Target URL for various mobile operating systems.", @@ -223,7 +277,10 @@ "string", "null" ], - "format": "uri" + "format": "uri", + "examples": [ + null + ] }, "targetUrlNodeJs": { "description": "Target URL for Node.JS clients.", @@ -231,7 +288,10 @@ "string", "null" ], - "format": "uri" + "format": "uri", + "examples": [ + null + ] }, "targetUrlPowershell": { "description": "Target URL for PowerShell clients.", @@ -239,7 +299,10 @@ "string", "null" ], - "format": "uri" + "format": "uri", + "examples": [ + null + ] }, "targetUrlPython": { "description": "Target URL for Python clients.", @@ -247,7 +310,10 @@ "string", "null" ], - "format": "uri" + "format": "uri", + "examples": [ + null + ] }, "type": { "description": "Type of the redirect. Used to calculate the status code to be sent to the caller.", @@ -255,6 +321,9 @@ "enum": [ "temporary", "permanent" + ], + "examples": [ + "temporary" ] } }, @@ -298,11 +367,17 @@ "maxLength": 36, "minLength": 36, "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", - "readOnly": true + "readOnly": true, + "examples": [ + "d4f5e6a7-b8c9-4d0e-9f1a-2b3c4d5e6f70" + ] }, "name": { "description": "Term or component that is banned in any of the vanity URLs that users can create.", - "type": "string" + "type": "string", + "examples": [ + "admin" + ] }, "type": { "description": "Specifies how the name ban operates: `rootSegment` operates matches like `https://example.com//*`, `global` operates matches like `https://example.com/**`.", @@ -310,6 +385,9 @@ "enum": [ "rootSegment", "global" + ], + "examples": [ + "rootSegment" ] } }, @@ -341,27 +419,42 @@ "maxLength": 36, "minLength": 36, "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", - "readOnly": true + "readOnly": true, + "examples": [ + "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d" + ] }, "allowHttp": { "description": "Flag that indicates if the domain accepts unencrypted traffic (`true`) or not (`false`).", - "type": "boolean" + "type": "boolean", + "examples": [ + false + ] }, "count": { "description": "Current increment for the id number if the domain is in ID number mode. Always incremented even if not in ID mode so as to be able to swap to ID mode if requested with no risk for collision.", "type": "integer", "format": "int32", "minimum": 0, - "readOnly": true + "readOnly": true, + "examples": [ + 1 + ] }, "hidden": { "description": "Flag that indicates if the domain name is hidden from the user interface. The domain will always be visible on the domain name admin config page. This flag is useful for hiding names that are behind load balancers from end users to reduce confusion. Can also be used to `enable` or `disable` end user interaction in the UI with the specified names.", - "type": "boolean" + "type": "boolean", + "examples": [ + false + ] }, "hostName": { "description": "String to be used in matching the host name. Subdomains need to have their own record to be usable.", "type": "string", - "format": "hostname" + "format": "hostname", + "examples": [ + "example.com" + ] }, "type": { "description": "Flag that indicates the type of shortened URL that can be generated on the domain: `vanity` allows any user structure as long as it is unique. Example: `https://example.com/myUrl`, `idNumber` locks out user control of the vanity URL and hard codes it to an ID number. Example: `https://example.com/managedLink?id=123`.", @@ -369,6 +462,9 @@ "enum": [ "vanity", "idNumber" + ], + "examples": [ + "vanity" ] } }, @@ -404,7 +500,10 @@ "maxLength": 36, "minLength": 36, "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", - "readOnly": true + "readOnly": true, + "examples": [ + "2e1c4b7a-8d3f-4e2b-9a1c-7f5d2b3e6a9c" + ] }, "assignmentTargetType": { "description": "Type of object that this RBAC assignment is for.", @@ -413,13 +512,19 @@ "RedirectConfig", "DomainName", "BannedName" + ], + "examples": [ + "RedirectConfig" ] }, "createdAt": { "description": "Time stamp of when the assignment was created.", "type": "string", "format": "date-time", - "readOnly": true + "readOnly": true, + "examples": [ + "2025-08-20T12:00:00Z" + ] }, "targetId": { "description": "Unique ID of the system that this assignment describes.", @@ -427,7 +532,10 @@ "format": "uuid", "maxLength": 36, "minLength": 36, - "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", + "examples": [ + "5a7e2c1b-3d4f-4a8b-9e6a-2c1b7f3d8e4a" + ] }, "principalId": { "description": "Object ID of the principal that this assignment targets.", @@ -435,7 +543,10 @@ "format": "uuid", "maxLength": 36, "minLength": 36, - "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", + "examples": [ + "9b2e7a1c-4d3f-5a8b-2c6e-1a7f3d9e8b5c" + ] }, "principalType": { "description": "Flag used to optimize principal processing.", @@ -443,6 +554,9 @@ "enum": [ "user", "group" + ], + "examples": [ + "user" ] }, "role": { @@ -451,7 +565,10 @@ "format": "uuid", "maxLength": 36, "minLength": 36, - "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", + "examples": [ + "b8e2c7a1-4d3f-4a9b-8e6a-2c1b7f3d8e4a" + ] } }, "type": "object", @@ -486,7 +603,7 @@ }, "description": "Create vanity or numeric short version of your desired URL. Share it digitally, as QR code, or print it.", "title": "SHI URL Shortener", - "version": "0.0.3" + "version": "0.0.4" }, "openapi": "3.1.1", "paths": { @@ -500,6 +617,16 @@ "description": "OK", "content": { "application/json": { + "examples": { + "Response with Data": { + "summary": "Response with available IDs", + "description": "Example response containing both the Tenant ID and the Application ID.", + "value": { + "appId": "2a7e1b3c-4d5f-4a8b-9e6a-1c2b7f3d8e4a", + "tenantId": "9c1e7a2b-5d3f-4a8b-2c6e-7f1a3d9e8b5c" + } + } + }, "schema": { "$ref": "#/components/schemas/Auth.Id" } @@ -572,7 +699,10 @@ "description": "Filter by the `sourceUrl` field.", "schema": { "type": "string", - "format": "uri" + "format": "uri", + "examples": [ + "https://www.example.com/vanityUrl" + ] } }, { @@ -584,7 +714,10 @@ "format": "uuid", "maxLength": 36, "minLength": 36, - "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", + "examples": [ + "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45" + ] } }, { @@ -593,7 +726,10 @@ "description": "Filter by the `customUserAgentMatcher` field.", "schema": { "type": "string", - "maxLength": 512 + "maxLength": 512, + "examples": [ + "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1" + ] } }, { @@ -605,7 +741,10 @@ "format": "uuid", "maxLength": 36, "minLength": 36, - "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", + "examples": [ + "9a1e7b2c-5d3f-4a8b-9c2e-6f1a3d7e8b54" + ] } }, { @@ -617,7 +756,10 @@ "format": "uuid", "maxLength": 36, "minLength": 36, - "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", + "examples": [ + "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45" + ] } }, { @@ -626,7 +768,10 @@ "description": "Filter by the `targetUrl` field.", "schema": { "type": "string", - "format": "uri" + "format": "uri", + "examples": [ + "https://www.example.com/very-long-and-complex-url-with_plenty-of-special-characters-and-non-repeated-words" + ] } }, { @@ -635,7 +780,10 @@ "description": "Filter by the `targetUrlCustom` field.", "schema": { "type": "string", - "format": "uri" + "format": "uri", + "examples": [ + "https://www.example.com/custom-target?customUserAgentDefinition" + ] } }, { @@ -644,7 +792,10 @@ "description": "Filter by the `targetUrlMobile` field.", "schema": { "type": "string", - "format": "uri" + "format": "uri", + "examples": [ + "https://www.example.com/custom-target?customMMobileDefinition" + ] } }, { @@ -653,7 +804,10 @@ "description": "Filter by the `targetUrlNodeJs` field.", "schema": { "type": "string", - "format": "uri" + "format": "uri", + "examples": [ + "https://www.example.com/custom-target?customNodeJsDefinition" + ] } }, { @@ -662,7 +816,10 @@ "description": "Filter by the `targetUrlPowershell` field.", "schema": { "type": "string", - "format": "uri" + "format": "uri", + "examples": [ + "https://www.example.com/custom-target?customPowerShellDefinition" + ] } }, { @@ -671,7 +828,10 @@ "description": "Filter by the `targetUrlPython` field.", "schema": { "type": "string", - "format": "uri" + "format": "uri", + "examples": [ + "https://www.example.com/custom-target?customPythonDefinition" + ] } }, { @@ -683,6 +843,9 @@ "enum": [ "temporary", "permanent" + ], + "examples": [ + "temporary" ] } } @@ -692,6 +855,39 @@ "description": "OK", "content": { "application/json": { + "examples": { + "No records available": { + "summary": "Example of the empty list", + "description": "Response when no records available in the database or match based on the provided filters.", + "value": [] + }, + "Records available": { + "summary": "Example of the response with data", + "description": "Response when one or more records available in the result set.", + "value": [ + { + "id": "7e2b1c8a-4f3d-4e2a-9b1a-2c6e8d7f1a23", + "sourceUrl": "https://www.example.com/vanityUrl", + "createdBy": "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45", + "customUserAgentMatcher": null, + "expectedBrokenScanStatusCode": null, + "domainConfigId": "9a1e7b2c-5d3f-4a8b-9c2e-6f1a3d7e8b54", + "enabled": true, + "enableLinkBrokenScan": false, + "updatedBy": "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45", + "targetUrl": "https://www.example.com/very-long-and-complex-url-with_plenty-of-special-characters-and-non-repeated-words", + "notEnabledAfter": "2025-12-31T23:59:59Z", + "notEnabledBefore": null, + "targetUrlCustom": null, + "targetUrlMobile": null, + "targetUrlNodeJs": null, + "targetUrlPowershell": null, + "targetUrlPython": null, + "type": "temporary" + } + ] + } + }, "schema": { "type": "array", "items": { @@ -722,6 +918,7 @@ "application/json": { "examples": { "Missing a required field": { + "summary": "Example of payload with missing fields", "description": "Payload is missing one or more of the required fields.", "value": { "sourceUrl": "https://www.example.com/long-and-complex-url", @@ -732,9 +929,9 @@ } }, "Complete and proper payload": { + "summary": "Example of payload with all necessary fields", "description": "Request where all required fields are present and valid.", "value": { - "id": "7e2b1c8a-4f3d-4e2a-9b1a-2c6e8d7f1a23", "sourceUrl": "https://www.example.com/long-and-complex-url", "createdBy": "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45", "customUserAgentMatcher": null, @@ -822,8 +1019,23 @@ "description": "OK", "content": { "application/json": { + "examples": { + "Name is available": { + "summary": "Example response when the name is available", + "description": "Response when the requested name is not taken and can be used.", + "value": true + }, + "Name is taken": { + "summary": "Example response when the name is not available", + "description": "Response when the requested name is already in use and cannot be used.", + "value": false + } + }, "schema": { - "type": "boolean" + "type": "boolean", + "examples": [ + true + ] } } } @@ -852,6 +1064,32 @@ "description": "OK", "content": { "application/json": { + "examples": { + "Response with record data": { + "summary": "Single record with data", + "description": "Response with all record information.", + "value": { + "id": "7e2b1c8a-4f3d-4e2a-9b1a-2c6e8d7f1a23", + "sourceUrl": "https://www.example.com/long-and-complex-url", + "createdBy": "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45", + "customUserAgentMatcher": null, + "expectedBrokenScanStatusCode": null, + "domainConfigId": "9a1e7b2c-5d3f-4a8b-9c2e-6f1a3d7e8b54", + "enabled": true, + "enableLinkBrokenScan": false, + "updatedBy": "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45", + "targetUrl": "https://www.example.com/target", + "notEnabledAfter": null, + "notEnabledBefore": null, + "targetUrlCustom": null, + "targetUrlMobile": null, + "targetUrlNodeJs": null, + "targetUrlPowershell": null, + "targetUrlPython": null, + "type": "temporary" + } + } + }, "schema": { "$ref": "#/components/schemas/Redirect.UrlConfiguration" } @@ -902,6 +1140,32 @@ "description": "OK", "content": { "application/json": { + "examples": { + "Response with updated record data": { + "summary": "Single record with updated data", + "description": "Response with all record information after the update.", + "value": { + "id": "7e2b1c8a-4f3d-4e2a-9b1a-2c6e8d7f1a23", + "sourceUrl": "https://www.example.com/long-and-complex-url", + "createdBy": "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45", + "customUserAgentMatcher": null, + "expectedBrokenScanStatusCode": null, + "domainConfigId": "9a1e7b2c-5d3f-4a8b-9c2e-6f1a3d7e8b54", + "enabled": true, + "enableLinkBrokenScan": false, + "updatedBy": "3c5a9b7e-2d4f-4c1b-8e6a-7f2b3d1c9e45", + "targetUrl": "https://www.example.com/target-updated", + "notEnabledAfter": null, + "notEnabledBefore": null, + "targetUrlCustom": null, + "targetUrlMobile": null, + "targetUrlNodeJs": "https://www.example.com/target-nodejs", + "targetUrlPowershell": null, + "targetUrlPython": null, + "type": "permanent" + } + } + }, "schema": { "$ref": "#/components/schemas/Redirect.UrlConfiguration" } @@ -952,7 +1216,10 @@ "in": "query", "description": "Filter by the `name` field.", "schema": { - "type": "string" + "type": "string", + "examples": [ + "admin" + ] } }, { @@ -964,6 +1231,9 @@ "enum": [ "rootSegment", "global" + ], + "examples": [ + "rootSegment" ] } } @@ -973,6 +1243,24 @@ "description": "OK", "content": { "application/json": { + "examples": { + "No records available": { + "summary": "Example of the empty list", + "description": "Response when no records available in the database or match based on the supplied filters.", + "value": [] + }, + "Records available": { + "summary": "Example of the response with data", + "description": "Response when one or more records available in the result set.", + "value": [ + { + "id": "5f1e7b2c-3d4f-4a8b-9c2e-6f1a3d7e8b55", + "name": "admin", + "type": "rootSegment" + } + ] + } + }, "schema": { "type": "array", "items": { @@ -1027,6 +1315,17 @@ "description": "OK", "content": { "application/json": { + "examples": { + "Created record data": { + "summary": "Single record with data", + "description": "Response with record information after creation.", + "value": { + "id": "5f1e7b2c-3d4f-4a8b-9c2e-6f1a3d7e8b55", + "name": "admin", + "type": "rootSegment" + } + } + }, "schema": { "$ref": "#/components/schemas/Redirect.BannedTerm" } @@ -1057,6 +1356,17 @@ "description": "OK", "content": { "application/json": { + "examples": { + "Record data": { + "summary": "Single record with data", + "description": "Response with current record information.", + "value": { + "id": "5f1e7b2c-3d4f-4a8b-9c2e-6f1a3d7e8b55", + "name": "admin", + "type": "rootSegment" + } + } + }, "schema": { "$ref": "#/components/schemas/Redirect.BannedTerm" } @@ -1107,7 +1417,10 @@ "in": "query", "description": "Filter by the `allowHttp` field.", "schema": { - "type": "boolean" + "type": "boolean", + "examples": [ + true + ] } }, { @@ -1117,7 +1430,10 @@ "schema": { "type": "integer", "format": "int32", - "minimum": 0 + "minimum": 0, + "examples": [ + 5 + ] } }, { @@ -1125,7 +1441,10 @@ "in": "query", "description": "Filter by the `hidden` field.", "schema": { - "type": "boolean" + "type": "boolean", + "examples": [ + false + ] } }, { @@ -1133,7 +1452,10 @@ "in": "query", "description": "Filter by the `hostName` field.", "schema": { - "type": "string" + "type": "string", + "examples": [ + "example.com" + ] } }, { @@ -1145,6 +1467,9 @@ "enum": [ "vanity", "idNumber" + ], + "examples": [ + "idNumber" ] } } @@ -1154,6 +1479,27 @@ "description": "OK", "content": { "application/json": { + "examples": { + "No records available": { + "summary": "Example of the empty list", + "description": "Response when no records available in the database or match based on the supplied filters.", + "value": [] + }, + "Records available": { + "summary": "Example of the response with data", + "description": "Response when one or more records available in the result set.", + "value": [ + { + "id": "5f1e7b2c-3d4f-4a8b-9c2e-6f1a3d7e8b55", + "allowHttp": true, + "count": 10, + "hidden": false, + "hostName": "example.com", + "type": "vanity" + } + ] + } + }, "schema": { "type": "array", "items": { @@ -1212,6 +1558,20 @@ "description": "OK", "content": { "application/json": { + "examples": { + "Created record data": { + "summary": "Single record with data", + "description": "Response with record information after creation.", + "value": { + "id": "5f1e7b2c-3d4f-4a8b-9c2e-6f1a3d7e8b55", + "allowHttp": false, + "count": 1, + "hidden": false, + "hostName": "example.com", + "type": "vanity" + } + } + }, "schema": { "$ref": "#/components/schemas/Redirect.DomainName" } @@ -1242,6 +1602,20 @@ "description": "OK", "content": { "application/json": { + "examples": { + "Record data": { + "summary": "Single record with data", + "description": "Response with current record information.", + "value": { + "id": "5f1e7b2c-3d4f-4a8b-9c2e-6f1a3d7e8b55", + "allowHttp": false, + "count": 1, + "hidden": false, + "hostName": "example.com", + "type": "vanity" + } + } + }, "schema": { "$ref": "#/components/schemas/Redirect.DomainName" } @@ -1291,6 +1665,20 @@ "description": "OK", "content": { "application/json": { + "examples": { + "Response with updated record data": { + "summary": "Single record with updated information", + "description": "Response with all record information after the update.", + "value": { + "id": "5f1e7b2c-3d4f-4a8b-9c2e-6f1a3d7e8b55", + "allowHttp": false, + "count": 1, + "hidden": false, + "hostName": "example.com", + "type": "idNumber" + } + } + }, "schema": { "$ref": "#/components/schemas/Redirect.DomainName" } @@ -1346,6 +1734,9 @@ "RedirectConfig", "DomainName", "BannedName" + ], + "examples": [ + "RedirectConfig" ] } }, @@ -1355,7 +1746,10 @@ "description": "Filter by the `createdAt` field.", "schema": { "type": "string", - "format": "date-time" + "format": "date-time", + "examples": [ + "2025-10-05T00:00:00.000Z" + ] } }, { @@ -1367,7 +1761,10 @@ "format": "uuid", "maxLength": 36, "minLength": 36, - "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", + "examples": [ + "5a7e2c1b-3d4f-4a8b-9e6a-2c1b7f3d8e4a" + ] } }, { @@ -1379,7 +1776,10 @@ "format": "uuid", "maxLength": 36, "minLength": 36, - "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", + "examples": [ + "9b2e7a1c-4d3f-5a8b-2c6e-1a7f3d9e8b5c" + ] } }, { @@ -1391,6 +1791,9 @@ "enum": [ "user", "group" + ], + "examples": [ + "user" ] } }, @@ -1403,7 +1806,10 @@ "format": "uuid", "maxLength": 36, "minLength": 36, - "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$" + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$", + "examples": [ + "b8e2c7a1-4d3f-4a9b-8e6a-2c1b7f3d8e4a" + ] } } ], @@ -1412,6 +1818,28 @@ "description": "OK", "content": { "application/json": { + "examples": { + "No records available": { + "summary": "Example of the empty list", + "description": "Response when no records available in the database or match based on the supplied filters.", + "value": [] + }, + "Records available": { + "summary": "Example of the response with data", + "description": "Response when one or more records available in the result set.", + "value": [ + { + "id": "6f2a1b7e-3d4c-4a8b-9e6a-2c1b7f3d8e4a", + "assignmentTargetType": "RedirectConfig", + "createdAt": "2025-10-05T12:34:56.789Z", + "targetId": "5a7e2c1b-3d4f-4a8b-9e6a-2c1b7f3d8e4a", + "principalId": "9b2e7a1c-4d3f-5a8b-2c6e-1a7f3d9e8b5c", + "principalType": "user", + "role": "b8e2c7a1-4d3f-4a9b-8e6a-2c1b7f3d8e4a" + } + ] + } + }, "schema": { "type": "array", "items": { @@ -1471,6 +1899,21 @@ "description": "OK", "content": { "application/json": { + "examples": { + "Created record data": { + "summary": "Single record with data", + "description": "Response with record information after creation.", + "value": { + "id": "6f2a1b7e-3d4c-4a8b-9e6a-2c1b7f3d8e4a", + "assignmentTargetType": "RedirectConfig", + "createdAt": "2025-10-05T12:34:56.789Z", + "targetId": "8b2e7a1c-4d3f-5a8b-0c6e-1a7f3d9e8b5c", + "principalId": "c7e1a2b3-4d5f-4a8b-9e6a-2c1b7f3d8e4a", + "principalType": "user", + "role": "9e1c7b2a-5d3f-4a8b-9c2e-6f1a3d7e8b54" + } + } + }, "schema": { "$ref": "#/components/schemas/Redirect.RbacAssignment" } @@ -1501,6 +1944,21 @@ "description": "OK", "content": { "application/json": { + "examples": { + "Record data": { + "summary": "Single record with data", + "description": "Response with current record information.", + "value": { + "id": "6f2a1b7e-3d4c-4a8b-9e6a-2c1b7f3d8e4a", + "assignmentTargetType": "RedirectConfig", + "createdAt": "2025-10-05T12:34:56.789Z", + "targetId": "5a7e2c1b-3d4f-4a8b-9e6a-2c1b7f3d8e4a", + "principalId": "9b2e7a1c-4d3f-5a8b-2c6e-1a7f3d9e8b5c", + "principalType": "user", + "role": "b8e2c7a1-4d3f-4a9b-8e6a-2c1b7f3d8e4a" + } + } + }, "schema": { "$ref": "#/components/schemas/Redirect.RbacAssignment" } From 6cb7760e7ca8a4046991b781165743293c1fe4b5 Mon Sep 17 00:00:00 2001 From: Pasha Zayko Date: Thu, 9 Oct 2025 16:37:27 -0400 Subject: [PATCH 3/3] Fixing typo Fixing typo --- specs/Url-Shortener.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/Url-Shortener.json b/specs/Url-Shortener.json index 7853def..c62baf7 100644 --- a/specs/Url-Shortener.json +++ b/specs/Url-Shortener.json @@ -794,7 +794,7 @@ "type": "string", "format": "uri", "examples": [ - "https://www.example.com/custom-target?customMMobileDefinition" + "https://www.example.com/custom-target?customMobileDefinition" ] } },