From ec874890ca772a77c845efe768483fb9f5caec7d Mon Sep 17 00:00:00 2001 From: Stephan Meijer Date: Wed, 12 Jul 2023 12:44:24 +0200 Subject: [PATCH 1/8] chore: don't include body for options requests (#126) --- .github/actions/smoke-test/dist/index.js | 13 +++++++------ openapi/scripts/smoke-test.ts | 15 ++++++++------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/.github/actions/smoke-test/dist/index.js b/.github/actions/smoke-test/dist/index.js index 253e14d..8cdc9ad 100755 --- a/.github/actions/smoke-test/dist/index.js +++ b/.github/actions/smoke-test/dist/index.js @@ -83972,12 +83972,6 @@ async function request(operation, type, params = {}) { if (isPublicApi(operation)) { headers['origin'] = 'https://magicbell-smoke-test.example.com'; } - if (type === 'preflight') { - headers['access-control-request-method'] = operation.method; - headers['access-control-request-headers'] = - 'content-type, origin, x-magicbell-api-key, x-magicbell-user-email, x-magicbell-user-hmac'; - headers['origin'] = 'https://magicbell-smoke-test.example.com'; - } const config = { baseURL: serverUrl, method, @@ -83992,6 +83986,13 @@ async function request(operation, type, params = {}) { }, params, }; + if (type === 'preflight') { + config.headers['access-control-request-method'] = operation.method; + config.headers['access-control-request-headers'] = + 'content-type, origin, x-magicbell-api-key, x-magicbell-user-email, x-magicbell-user-hmac'; + config.headers['origin'] = 'https://magicbell-smoke-test.example.com'; + delete config.data; + } const response = await axios_default().request(config).catch((e) => { console.log(`ERROR: request ${config.method} ${config.url} resulted in a network error:`, { error: { type: e.constructor.name, name: e.name, message: e.message }, diff --git a/openapi/scripts/smoke-test.ts b/openapi/scripts/smoke-test.ts index 7ebe05a..4fb2887 100644 --- a/openapi/scripts/smoke-test.ts +++ b/openapi/scripts/smoke-test.ts @@ -101,13 +101,6 @@ async function request( headers['origin'] = 'https://magicbell-smoke-test.example.com'; } - if (type === 'preflight') { - headers['access-control-request-method'] = operation.method; - headers['access-control-request-headers'] = - 'content-type, origin, x-magicbell-api-key, x-magicbell-user-email, x-magicbell-user-hmac'; - headers['origin'] = 'https://magicbell-smoke-test.example.com'; - } - const config: AxiosRequestConfig = { baseURL: serverUrl, method, @@ -123,6 +116,14 @@ async function request( params, }; + if (type === 'preflight') { + config.headers['access-control-request-method'] = operation.method; + config.headers['access-control-request-headers'] = + 'content-type, origin, x-magicbell-api-key, x-magicbell-user-email, x-magicbell-user-hmac'; + config.headers['origin'] = 'https://magicbell-smoke-test.example.com'; + delete config.data; + } + const response = await axios.request(config).catch((e) => { console.log(`ERROR: request ${config.method} ${config.url} resulted in a network error:`, { error: { type: e.constructor.name, name: e.name, message: e.message }, From 5864cd7bcc2d35db764edd42f412e04cfb8fadee Mon Sep 17 00:00:00 2001 From: Stephan Meijer Date: Wed, 12 Jul 2023 13:23:02 +0200 Subject: [PATCH 2/8] chore: log curl request for failed smoke tests (#127) --- .github/actions/smoke-test/dist/index.js | 30 ++++++++++++++++---- openapi/scripts/smoke-test.ts | 35 ++++++++++++++++++++---- 2 files changed, 53 insertions(+), 12 deletions(-) diff --git a/.github/actions/smoke-test/dist/index.js b/.github/actions/smoke-test/dist/index.js index 8cdc9ad..dfca63e 100755 --- a/.github/actions/smoke-test/dist/index.js +++ b/.github/actions/smoke-test/dist/index.js @@ -83950,12 +83950,23 @@ function getOperations(document) { } return methods; } +const headerMaskMap = { + 'X-MAGICBELL-API-KEY': '$MAGICBELL_API_KEY', + 'X-MAGICBELL-API-SECRET': '$MAGICBELL_API_SECRET', + 'X-MAGICBELL-USER-EMAIL': '$MAGICBELL_USER_EMAIL', + 'X-MAGICBELL-USER-EXTERNAL-ID': '$MAGICBELL_USER_EXTERNAL_ID', + 'X-MAGICBELL-USER-HMAC': '$MAGICBELL_USER_HMAC', +}; function toCurl({ method, baseURL, url, data, headers }) { return [ `curl -X ${method.toUpperCase()}`, `${baseURL}/${url.replace(/^\//, '')}`, Object.entries(headers) - .map(([key, value]) => `-H '${key}: ${value}'`) + .map(([key, value]) => { + // only replace the value with an env key if the header has a value, otherwise we can't debug missing headers. + value = value ? headerMaskMap[key.toUpperCase()] || value : value; + return `-H "${key}: ${value}"`; + }) .join(' '), data && `-d '${JSON.stringify(data)}'`, ].join(' '); @@ -84003,7 +84014,7 @@ async function request(operation, type, params = {}) { }); const duration = Date.now() - start; const error = response.data?.errors?.[0]?.message; - return Object.assign(response, { duration, config: config, error }); + return Object.assign(response, { duration, config, error }); } function getByJsonPointer(obj, path) { return path @@ -84069,16 +84080,18 @@ function createTests(operations) { list.parentTask = suites.tasks[suites.tasks.length - 1]; list.add({ title: 'HTTP 401: request without authentication headers return 401 unauthorized', - task: async () => { + task: async function () { const res = await request(operation, 'no-headers'); + this.requestConfig = res.config; expect(res.status, res.error).equal(401); expect(res.duration).lessThan(5000); }, }); list.add({ title: 'HTTP 401: request with invalid auth headers returns 401 unauthorized', - task: async () => { + task: async function () { const res = await request(operation, 'invalid-auth'); + this.requestConfig = res.config; expect(res.status, res.error).equal(401); expect(res.duration).lessThan(5000); }, @@ -84095,9 +84108,10 @@ function createTests(operations) { list.add({ title: `HTTP ${code}: request with valid api key and payload returns expected response`, skip: () => shouldSkip, - task: async () => { + task: async function () { operation.path = getUrl(path); const res = await request(operation, 'authenticated'); + this.requestConfig = res.config; expect(res.status, res.error).equal(code); expect(res.duration).lessThan(5000); if (isPublicApi(operation)) { @@ -84128,9 +84142,10 @@ function createTests(operations) { list.add({ title: `HTTP 200: options request returns cors headers`, skip: () => shouldSkip, - task: async () => { + task: async function () { operation.path = getUrl(path); const res = await request(operation, 'preflight'); + this.requestConfig = res.config; expect(res.status, res.error).equal(200); expect(res.duration).lessThan(5000); // check cors headers @@ -84212,6 +84227,9 @@ async function smoke_test_main() { console.log(`${chalk.red('✖')} ${err.task.listr.parentTask.title}`); console.log(` ${chalk.red('✖')} ${err.task.title}`); console.log(` error: ${err.message}`); + if ('requestConfig' in err.task) { + console.log(` request: ${toCurl(err.task.requestConfig)}`); + } } process.exit(1); } diff --git a/openapi/scripts/smoke-test.ts b/openapi/scripts/smoke-test.ts index 4fb2887..6e6f14b 100644 --- a/openapi/scripts/smoke-test.ts +++ b/openapi/scripts/smoke-test.ts @@ -70,12 +70,24 @@ function getOperations(document: OpenAPIV3.Document) { return methods; } +const headerMaskMap = { + 'X-MAGICBELL-API-KEY': '$MAGICBELL_API_KEY', + 'X-MAGICBELL-API-SECRET': '$MAGICBELL_API_SECRET', + 'X-MAGICBELL-USER-EMAIL': '$MAGICBELL_USER_EMAIL', + 'X-MAGICBELL-USER-EXTERNAL-ID': '$MAGICBELL_USER_EXTERNAL_ID', + 'X-MAGICBELL-USER-HMAC': '$MAGICBELL_USER_HMAC', +}; + function toCurl({ method, baseURL, url, data, headers }: AxiosRequestConfig) { return [ `curl -X ${method.toUpperCase()}`, `${baseURL}/${url.replace(/^\//, '')}`, Object.entries(headers) - .map(([key, value]) => `-H '${key}: ${value}'`) + .map(([key, value]) => { + // only replace the value with an env key if the header has a value, otherwise we can't debug missing headers. + value = value ? headerMaskMap[key.toUpperCase()] || value : value; + return `-H "${key}: ${value}"`; + }) .join(' '), data && `-d '${JSON.stringify(data)}'`, ].join(' '); @@ -136,7 +148,7 @@ async function request( const duration = Date.now() - start; const error = response.data?.errors?.[0]?.message; - return Object.assign(response, { duration, config: config, error }); + return Object.assign(response, { duration, config, error }); } function getByJsonPointer(obj: Record, path: string) { @@ -217,8 +229,10 @@ function createTests(operations: Operation[]) { list.add({ title: 'HTTP 401: request without authentication headers return 401 unauthorized', - task: async () => { + task: async function () { const res = await request(operation, 'no-headers'); + this.requestConfig = res.config; + expect(res.status, res.error).equal(401); expect(res.duration).lessThan(5000); }, @@ -226,8 +240,10 @@ function createTests(operations: Operation[]) { list.add({ title: 'HTTP 401: request with invalid auth headers returns 401 unauthorized', - task: async () => { + task: async function () { const res = await request(operation, 'invalid-auth'); + this.requestConfig = res.config; + expect(res.status, res.error).equal(401); expect(res.duration).lessThan(5000); }, @@ -247,10 +263,12 @@ function createTests(operations: Operation[]) { list.add({ title: `HTTP ${code}: request with valid api key and payload returns expected response`, skip: () => shouldSkip, - task: async () => { + task: async function () { operation.path = getUrl(path); const res = await request(operation, 'authenticated'); + this.requestConfig = res.config; + expect(res.status, res.error).equal(code); expect(res.duration).lessThan(5000); @@ -290,9 +308,11 @@ function createTests(operations: Operation[]) { list.add({ title: `HTTP 200: options request returns cors headers`, skip: () => shouldSkip, - task: async () => { + task: async function () { operation.path = getUrl(path); const res = await request(operation, 'preflight'); + this.requestConfig = res.config; + expect(res.status, res.error).equal(200); expect(res.duration).lessThan(5000); @@ -412,6 +432,9 @@ async function main() { console.log(`${chalk.red('✖')} ${err.task.listr.parentTask.title}`); console.log(` ${chalk.red('✖')} ${err.task.title}`); console.log(` error: ${err.message}`); + if ('requestConfig' in err.task) { + console.log(` request: ${toCurl(err.task.requestConfig)}`); + } } process.exit(1); From cbd0b983928b9c492931f57006158e9d1af01f84 Mon Sep 17 00:00:00 2001 From: Stephan Meijer Date: Thu, 20 Jul 2023 12:55:40 +0200 Subject: [PATCH 3/8] spec: fix response schemas & add missing examples (#130) --- openapi/package.json | 1 + openapi/scripts/validate-openapi.ts | 69 +- openapi/spec/openapi.json | 970 ++++++++++++---------------- yarn.lock | 44 +- 4 files changed, 529 insertions(+), 555 deletions(-) diff --git a/openapi/package.json b/openapi/package.json index aa02277..1a5a864 100644 --- a/openapi/package.json +++ b/openapi/package.json @@ -32,6 +32,7 @@ "ajv-formats": "^2.1.1", "ast-types": "^0.14.2", "axios": "^0.27.2", + "better-ajv-errors": "^1.2.0", "chai": "^4.3.6", "dotenv": "^16.0.2", "eslint": "^8.23.1", diff --git a/openapi/scripts/validate-openapi.ts b/openapi/scripts/validate-openapi.ts index 76ed7c0..268f0f3 100644 --- a/openapi/scripts/validate-openapi.ts +++ b/openapi/scripts/validate-openapi.ts @@ -4,6 +4,7 @@ import 'zx/globals'; import swagger from '@apidevtools/swagger-parser'; import Ajv from 'ajv'; import addFormats from 'ajv-formats'; +import betterAjvErrors from 'better-ajv-errors'; import jsonpath from 'jsonpath'; import { OpenAPIV3 } from 'openapi-types'; @@ -15,7 +16,7 @@ if (showHelp) { process.exit(0); } -const ajv = new Ajv(); +const ajv = new Ajv({ allErrors: true }); addFormats(ajv); const spec = await fs.readJSON(path.resolve(specFile)); @@ -79,21 +80,63 @@ function assertPathItemHasSummary() { } } -function assertExamplesMatchSchema() { +function assertRequestExamplesMatchSchema() { for (const [_path, pathObj] of Object.entries(api.paths)) { for (const [_method, operation] of Object.entries(pathObj)) { if (!operation.requestBody || !('content' in operation.requestBody)) continue; const content = operation.requestBody.content?.['application/json']; - if (!content.example) continue; + if (!content.example) { + errors.push(`Request example for ${operation.operationId} is missing.`); + continue; + } const valid = ajv.validate(content.schema, content.example); if (!valid) { - errors.push( - `Example for ${operation.operationId} does not match schema, ${ajv.errors - ?.map((e) => `field '${e.keyword}' ${e.message} (path ${e.instancePath})`) - .join(', ')}`, - ); + const ajvErrors = betterAjvErrors(content.schema, content.example, ajv.errors, { format: 'js' }); + for (const { error } of ajvErrors) { + errors.push(`Request example for ${operation.operationId} does not match schema. ${error.trim()}`); + } + } + } + } +} + +function assertResponseExamplesMatchSchema() { + for (const [_path, pathObj] of Object.entries(api.paths)) { + for (const [_method, operation] of Object.entries(pathObj)) { + if (!operation.responses) continue; + + for (const [statusCode, response] of Object.entries(operation.responses)) { + const content = (response as OpenAPIV3.ResponseObject).content?.['application/json']; + + // some status codes don't have content + if (!content && ['204', '404'].includes(statusCode)) continue; + + if (!content) { + errors.push(`Response for ${operation.operationId} (${statusCode}) is missing content.`); + continue; + } + + if (!content.example) { + errors.push(`Response for ${operation.operationId} (${statusCode}) is missing example.`); + continue; + } + + if (!content.schema) { + errors.push(`Response for ${operation.operationId} (${statusCode}) is missing schema.`); + continue; + } + + const valid = ajv.validate(content.schema, content.example); + if (!valid) { + const ajvErrors = betterAjvErrors(content.schema, content.example, ajv.errors, { format: 'js' }); + for (const { error } of ajvErrors) { + errors.push( + `Response example for ${operation.operationId} (${statusCode}) does not match schema. ${error.trim()}`, + ); + } + } } } } @@ -117,11 +160,17 @@ function assertPathsMatchConvention() { assertPathItemHasSummary(); assertUniqueOperationIds(); assertOperationIdsMatchConvention(); -assertExamplesMatchSchema(); +assertRequestExamplesMatchSchema(); +assertResponseExamplesMatchSchema(); assertPathsMatchConvention(); for (const error of errors) { - console.log(chalk.redBright(error)); + console.log(error); +} + +if (errors.length) { + console.log(''); + console.log(chalk.redBright(`Found ${errors.length} errors.`)); } process.exit(errors.length ? 1 : 0); diff --git a/openapi/spec/openapi.json b/openapi/spec/openapi.json index 2e2929a..bcbeeed 100644 --- a/openapi/spec/openapi.json +++ b/openapi/spec/openapi.json @@ -40,6 +40,35 @@ } } ] + }, + "example": { + "per_page": 15, + "current_page": 1, + "broadcasts": [{ + "id": "d5f4f217-9475-4e47-85a3-b395d8134e8f", + "title": "Pickup Instructions – In-Store", + "category": "order_ready", + "topic": "order:33098", + "recipients": [ + { "external_id": "customer_1" }, + { + "email": "person@example.com", + "last_name": "Doe", + "first_name": "Person", + "phone_numbers": ["+31612345678"] + } + ], + "status": { + "errors": [], + "status": "processed", + "summary": { + "total": 2, + "failures": 0 + } + }, + "sent_at": "2023-07-14T07:48:29.098493Z", + "created_at": "2023-07-14T07:48:29.098493Z" + }] } } } @@ -65,12 +94,39 @@ "application/json": { "schema": { "type": "object", - "required": ["broadcasts"], + "required": ["broadcast"], "properties": { "broadcast": { "$ref": "#/components/schemas/Broadcast" } } + }, + "example": { + "broadcast": { + "id": "d5f4f217-9475-4e47-85a3-b395d8134e8f", + "title": "Pickup Instructions – In-Store", + "category": "order_ready", + "topic": "order:33098", + "recipients": [ + { "external_id": "customer_1" }, + { + "email": "person@example.com", + "last_name": "Doe", + "first_name": "Person", + "phone_numbers": ["+31612345678"] + } + ], + "status": { + "errors": [], + "status": "processed", + "summary": { + "total": 2, + "failures": 0 + } + }, + "sent_at": "2023-07-14T07:48:29.098493Z", + "created_at": "2023-07-14T07:48:29.098493Z" + } } } } @@ -136,6 +192,72 @@ } } ] + }, + "example": { + "per_page": 100, + "current_page": 1, + "notifications": [ + { + "id": "4e2e236a-4538-4466-a9d6-30489e910b1f", + "title": "Your order is ready for pickup", + "category": null, + "topic": "order:33098", + "created_at": "2023-07-14T07:48:29.356351Z", + "updated_at": "2023-07-14T07:48:29.767895Z", + "sent_at": "2023-07-14T07:48:29.098493Z", + "recipient": { + "user": { + "id": "501db41e-8780-4173-976d-169899f14f93", + "email": "person@example.com", + "external_id": null, + "first_name": "Person", + "last_name": "Doe" + } + }, + "read_at": null, + "seen_at": null, + "status": "unseen", + "archived_at": null, + "discarded_at": null, + "deliveries": [ + { + "id": "14bf943c-3fb5-4bc3-9dc4-6ed6ab716d58", + "channel": "sms", + "status": "sent", + "scheduled_at": "2023-07-14T07:48:29.560412Z", + "title": "Your order is ready for pickup" + }, + { + "id": "802aaa0e-579b-403e-a872-3bb3e114eb5d", + "channel": "email", + "status": "sent", + "scheduled_at": "2023-07-14T07:48:29.560412Z", + "title": "Your order is ready for pickup" + }, + { + "id": "57178bac-58d7-474c-9d2d-ca8c441fefad", + "channel": "web_push", + "status": "sent", + "scheduled_at": "2023-07-14T07:48:29.560412Z", + "title": "Your order is ready for pickup" + }, + { + "id": "48ecd2dd-afda-438d-921d-3010c74c2b35", + "channel": "mobile_push", + "status": "sent", + "scheduled_at": "2023-07-14T07:48:29.560412Z", + "title": "Your order is ready for pickup" + }, + { + "id": "7d4d820f-5e74-4af6-877a-bdf3f725c754", + "channel": "in_app", + "status": "sent", + "scheduled_at": "2023-07-14T07:48:29.560412Z", + "title": "Your order is ready for pickup" + } + ] + } + ] } } } @@ -225,33 +347,7 @@ } } }, - "422": { - "description": "Invalid data", - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": false, - "properties": { - "errors": { - "type": "array", - "description": "List of validation errors" - } - } - }, - "example": { - "errors": [ - { - "message": "Param 'notification.title' is missing" - }, - { - "message": "Param 'notification.recipients' is missing" - } - ] - } - } - } - } + "422": { "$ref": "#/components/responses/UnprocessableEntity" } } }, "get": { @@ -450,20 +546,7 @@ } } }, - "404": { - "description": "The notification is not found", - "content": { - "application/json": { - "example": { - "errors": [ - { - "message": "A notification with the the given id doesn't exist" - } - ] - } - } - } - } + "404": { "$ref" : "#/components/responses/NotFound" } } }, "delete": { @@ -483,21 +566,8 @@ { "$ref": "#/components/parameters/notification_id" } ], "responses": { - "204": { "description": "User notification delete successfully" }, - "404": { - "description": "User notification not found", - "content": { - "application/json": { - "example": { - "errors": [ - { - "message": "A notification with the the given id doesn't exist" - } - ] - } - } - } - } + "204": { "$ref": "#/components/responses/NoContent" }, + "404": { "$ref" : "#/components/responses/NotFound" } } } }, @@ -519,23 +589,8 @@ { "$ref": "#/components/parameters/notification_id" } ], "responses": { - "204": { - "description": "User notification marked as read" - }, - "404": { - "description": "The notification is not found", - "content": { - "application/json": { - "example": { - "errors": [ - { - "message": "A notification_read with the the given id doesn't exist" - } - ] - } - } - } - } + "204": { "$ref": "#/components/responses/NoContent" }, + "404": { "$ref" : "#/components/responses/NotFound" } } } }, @@ -557,23 +612,8 @@ { "$ref": "#/components/parameters/notification_id" } ], "responses": { - "204": { - "description": "User notification marked as unread" - }, - "404": { - "description": "The notification is not found", - "content": { - "application/json": { - "example": { - "errors": [ - { - "message": "A notification_read with the the given id doesn't exist" - } - ] - } - } - } - } + "204": { "$ref": "#/components/responses/NoContent" }, + "404": { "$ref" : "#/components/responses/NotFound" } } } }, @@ -595,23 +635,8 @@ { "$ref": "#/components/parameters/notification_id" } ], "responses": { - "204": { - "description": "User notification marked as archived" - }, - "404": { - "description": "The notification is not found", - "content": { - "application/json": { - "example": { - "errors": [ - { - "message": "A notification_archive with the the given id doesn't exist" - } - ] - } - } - } - } + "204": { "$ref": "#/components/responses/NoContent" }, + "404": { "$ref" : "#/components/responses/NotFound" } } }, "delete": { @@ -631,23 +656,8 @@ { "$ref": "#/components/parameters/notification_id" } ], "responses": { - "204": { - "description": "User notification marked as unarchived" - }, - "404": { - "description": "The notification is not found", - "content": { - "application/json": { - "example": { - "errors": [ - { - "message": "A notification_archive with the the given id doesn't exist" - } - ] - } - } - } - } + "204": { "$ref": "#/components/responses/NoContent" }, + "404": { "$ref" : "#/components/responses/NotFound" } } } }, @@ -673,9 +683,7 @@ { "$ref": "#/components/parameters/topics_query_param" } ], "responses": { - "204": { - "description": "All notifications of the user marked as read" - } + "204": { "$ref": "#/components/responses/NoContent" } } } }, @@ -701,9 +709,7 @@ { "$ref": "#/components/parameters/topics_query_param" } ], "responses": { - "204": { - "description": "All notifications of the user marked as seen" - } + "204": { "$ref": "#/components/responses/NoContent" } } } }, @@ -762,20 +768,7 @@ } } }, - "422": { - "description": "Validation error", - "content": { - "application/json": { - "example": { - "errors": [ - { - "message": "Param 'user' must have an external id or an email so MagicBell can uniquely identify the user" - } - ] - } - } - } - } + "422": { "$ref": "#/components/responses/UnprocessableEntity" } } }, "get": { @@ -858,10 +851,10 @@ ] }, "example": { - "current_page": "1", - "total_pages": "1", - "total": "1", - "per_page": "100", + "current_page": 1, + "total_pages": 1, + "total": 1, + "per_page": 100, "users": [ { "id": "7fb3ce9f-a866-4dff-8ce8-2f64f7c5ed4c", @@ -892,20 +885,6 @@ } } } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "example": { - "errors": [ - { - "message": "Param 'user' must have an external id or an email so MagicBell can uniquely identify the user" - } - ] - } - } - } } } } @@ -944,20 +923,7 @@ } } }, - "404": { - "description": "User not found", - "content": { - "application/json": { - "example": { - "errors": [ - { - "message": "A user with the the given id doesn't exist for the project" - } - ] - } - } - } - } + "404": { "$ref" : "#/components/responses/NotFound" } } }, "put": { @@ -1009,34 +975,8 @@ } } }, - "422": { - "description": "Validation error", - "content": { - "application/json": { - "example": { - "errors": [ - { - "message": "Param 'notification_preferences.categories' is missing" - } - ] - } - } - } - }, - "404": { - "description": "User not found", - "content": { - "application/json": { - "example": { - "errors": [ - { - "message": "A user with the the given id doesn't exist" - } - ] - } - } - } - } + "422": { "$ref": "#/components/responses/UnprocessableEntity" }, + "404": { "$ref" : "#/components/responses/NotFound" } } }, "delete": { @@ -1056,21 +996,8 @@ } ], "responses": { - "204": { "description": "OK" }, - "404": { - "description": "User not found", - "content": { - "application/json": { - "example": { - "errors": [ - { - "message": "A user with the the given id doesn't exist" - } - ] - } - } - } - } + "204": { "$ref": "#/components/responses/NoContent" }, + "404": { "$ref" : "#/components/responses/NotFound" } } } }, @@ -1135,6 +1062,72 @@ } } ] + }, + "example": { + "per_page": 15, + "current_page": 1, + "notifications": [ + { + "id": "d1131e8f-c60b-4fa4-abc7-2ca3f19cddb4", + "title": "You have a new message", + "category": null, + "topic": null, + "created_at": "2022-12-22T10:36:13.792718Z", + "updated_at": "2022-12-22T10:36:13.942176Z", + "sent_at": "2022-12-22T10:36:13.672902Z", + "recipient": { + "user": { + "id": "0d46ed50-ae34-488b-a32f-ad8887e22aa0", + "email": "person@example.com", + "external_id": null, + "first_name": "Person", + "last_name": "Doe" + } + }, + "read_at": null, + "seen_at": null, + "status": "unseen", + "archived_at": null, + "discarded_at": null, + "deliveries": [ + { + "id": "54980355-f813-43aa-a4e2-8e744adb32e6", + "channel": "sms", + "status": "sent", + "scheduled_at": "2022-12-22T10:36:13.846195Z", + "title": "You have a new message" + }, + { + "id": "90f9fcd1-21fa-4bd6-a076-33b2787cba1f", + "channel": "email", + "status": "sent", + "scheduled_at": "2022-12-22T10:36:13.846195Z", + "title": "You have a new message" + }, + { + "id": "f9f28331-4c03-48c8-b2b7-276a2250a4df", + "channel": "web_push", + "status": "sent", + "scheduled_at": "2022-12-22T10:36:13.846195Z", + "title": "You have a new message" + }, + { + "id": "7d5f136a-1f83-4d41-b8b6-cca1793113a2", + "channel": "mobile_push", + "status": "sent", + "scheduled_at": "2022-12-22T10:36:13.846195Z", + "title": "You have a new message" + }, + { + "id": "7bd247da-4a2d-47d9-947e-4416b63da63a", + "channel": "in_app", + "status": "sent", + "scheduled_at": "2022-12-22T10:36:13.846195Z", + "title": "You have a new message" + } + ] + } + ] } } } @@ -1202,34 +1195,8 @@ } } }, - "422": { - "description": "Validation error", - "content": { - "application/json": { - "example": { - "errors": [ - { - "message": "Param 'notification_preferences.categories' is missing" - } - ] - } - } - } - }, - "404": { - "description": "User not found", - "content": { - "application/json": { - "example": { - "errors": [ - { - "message": "A user with the the given id doesn't exist" - } - ] - } - } - } - } + "422": { "$ref": "#/components/responses/UnprocessableEntity" }, + "404": { "$ref" : "#/components/responses/NotFound" } } }, "delete": { @@ -1248,21 +1215,8 @@ } ], "responses": { - "204": { "description": "OK" }, - "404": { - "description": "User not found", - "content": { - "application/json": { - "example": { - "errors": [ - { - "message": "A user with the the given id doesn't exist" - } - ] - } - } - } - } + "204": { "$ref": "#/components/responses/NoContent" }, + "404": { "$ref" : "#/components/responses/NotFound" } } } }, @@ -1326,34 +1280,8 @@ } } }, - "422": { - "description": "Validation error", - "content": { - "application/json": { - "example": { - "errors": [ - { - "message": "Param 'notification_preferences.categories' is missing" - } - ] - } - } - } - }, - "404": { - "description": "User not found", - "content": { - "application/json": { - "example": { - "errors": [ - { - "message": "A user with the the given id doesn't exist" - } - ] - } - } - } - } + "422": { "$ref": "#/components/responses/UnprocessableEntity" }, + "404": { "$ref" : "#/components/responses/NotFound" } } }, "delete": { @@ -1371,22 +1299,9 @@ "schema": { "type": "string", "minimum": 1 } } ], - "responses": { - "204": { "description": "OK" }, - "404": { - "description": "User not found", - "content": { - "application/json": { - "example": { - "errors": [ - { - "message": "A user with the the given id doesn't exist" - } - ] - } - } - } - } + "responses": { + "204": { "$ref": "#/components/responses/NoContent" }, + "404": { "$ref": "#/components/responses/NotFound" } } } }, @@ -1446,29 +1361,7 @@ } } }, - "422": { - "description": "Invalid data", - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": false, - "properties": { - "errors": { - "type": "array", - "description": "List of validation errors" - } - } - }, - "example": { - "errors": [ - { "message": "Param 'push_subscription.device_token' is missing" }, - { "message": "Param 'push_subscription.platform' is missing" } - ] - } - } - } - } + "422": { "$ref": "#/components/responses/UnprocessableEntity" } } }, "get": { @@ -1493,8 +1386,6 @@ "example": { "current_page": 1, "per_page": 10, - "total_pages": 1, - "total_count": 1, "push_subscriptions": [ { "id": "f80a7574-b309-4395-bb26-c503fe697d52", @@ -1502,30 +1393,14 @@ "device_token": "x4doKe98yEZ21Kum2Qq39M3b8jkhonuIupobyFnL0wJMSWAZ8zoTp2dyHgV", "platform": "ios", "created_at": "2021-03-01T12:00:00.000Z", - "discarded_at": "" + "discarded_at": null } ] } } } }, - "401": { - "description": "User not found", - "content": { - "application/json": { - "example": { - "errors": [ - { - "code": "user_not_found", - "message": "User not found", - "suggestion": "Please provide the correct 'X-MAGICBELL-USER-EMAIL' or 'X-MAGICBELL-USER-EXTERNAL-ID' header to identify the user.", - "help_link": "https://magicbell.com/docs/rest-api/authentication" - } - ] - } - } - } - } + "401": { "$ref": "#/components/responses/Unauthorized" } } } }, @@ -1543,21 +1418,8 @@ { "$ref": "#/components/parameters/device_token_path_param" } ], "responses": { - "204": { "description": "Subscription delete successfully" }, - "404": { - "description": "The device token was not registered", - "content": { - "application/json": { - "example": { - "errors": [ - { - "message": "No subscription with the provided device token was found" - } - ] - } - } - } - } + "204": { "$ref": "#/components/responses/NoContent" }, + "404": { "$ref": "#/components/responses/NotFound" } } } }, @@ -1585,8 +1447,6 @@ "example": { "current_page": 1, "per_page": 10, - "total_pages": 1, - "total_count": 1, "push_subscriptions": [ { "id": "f80a7574-b309-4395-bb26-c503fe697d52", @@ -1594,7 +1454,7 @@ "device_token": "x4doKe98yEZ21Kum2Qq39M3b8jkhonuIupobyFnL0wJMSWAZ8zoTp2dyHgV", "platform": "ios", "created_at": "2021-03-01T12:00:00.000Z", - "discarded_at": "" + "discarded_at": null } ] } @@ -1617,8 +1477,8 @@ "description": "Delete a user's push subscriptions. Identifies the user by the user's ID and the push subscription by the subscription's ID. ", "operationId": "users-push-subscriptions-delete", "responses": { - "204": { "description": "Subscription delete successfully" }, - "404": { "description": "User or subscription not found" } + "204": { "$ref": "#/components/responses/NoContent" }, + "404": { "$ref" : "#/components/responses/NotFound" } } } }, @@ -1775,21 +1635,7 @@ } } }, - "404": { - "description": "User not found", - "content": { - "application/json": { - "example": { - "errors": [ - { - "code": "no_user_with_provided_email", - "message": "A user with the email was not found in project with this API key" - } - ] - } - } - } - } + "404": { "$ref" : "#/components/responses/NotFound" } } } }, @@ -1937,33 +1783,7 @@ } } }, - "422": { - "description": "Invalid data", - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": false, - "properties": { - "errors": { - "type": "array", - "description": "List of validation errors" - } - } - }, - "example": { - "errors": [ - { - "message": "Param 'subscription.topic' is missing" - }, - { - "message": "Param 'subscription.categories' is missing" - } - ] - } - } - } - } + "422": { "$ref": "#/components/responses/UnprocessableEntity" } } } }, @@ -2019,44 +1839,8 @@ } } }, - "404": { - "description": "Topic subscription not found", - "content": { - "application/json": { - "example": { - "errors": [ - { - "message": "Couldn't find a user's subscription matching the criteria" - } - ] - } - } - } - }, - "422": { - "description": "Invalid data", - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": false, - "properties": { - "errors": { - "type": "array", - "description": "List of validation errors" - } - } - }, - "example": { - "errors": [ - { - "message": "Param 'subscription.categories' is missing" - } - ] - } - } - } - } + "404": { "$ref" : "#/components/responses/NotFound" }, + "422": { "$ref": "#/components/responses/UnprocessableEntity" } } } }, @@ -2096,30 +1880,7 @@ } } }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": false, - "properties": { - "errors": { - "type": "array", - "description": "List of validation errors" - } - } - }, - "example": { - "errors": [ - { - "message": "Couldn't find topic: comments" - } - ] - } - } - } - } + "404": { "$ref" : "#/components/responses/NotFound" } } }, "delete": { @@ -2137,6 +1898,7 @@ "content": { "application/json": { "schema": { + "type": "object", "properties": { "categories": { "type": "array", @@ -2153,26 +1915,22 @@ } } } + }, + "example":{ + "categories": [ + { + "slug": "comments" + }, { + "slug": "likes" + } + ] } } } }, "responses": { - "204": { "description": "Topic subscription delete successfully" }, - "404": { - "description": "Topic subscription not found", - "content": { - "application/json": { - "example": { - "errors": [ - { - "message": "Couldn't find a user's subscription matching the criteria" - } - ] - } - } - } - } + "204": { "$ref": "#/components/responses/NoContent" }, + "422": { "$ref": "#/components/responses/UnprocessableEntity" } } } }, @@ -2250,30 +2008,7 @@ } } }, - "422": { - "description": "Invalid data", - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": false, - "properties": { - "errors": { - "type": "array", - "description": "List of validation errors" - } - } - }, - "example": { - "errors": [ - { - "message": "Param 'import.users' must be filled" - } - ] - } - } - } - } + "422": { "$ref": "#/components/responses/UnprocessableEntity" } } } }, @@ -2340,20 +2075,7 @@ } } }, - "404": { - "description": "The import is not found", - "content": { - "application/json": { - "example": { - "errors": [ - { - "message": "An import with the the given id doesn't exist" - } - ] - } - } - } - } + "404": { "$ref" : "#/components/responses/NotFound" } } } }, @@ -2376,6 +2098,29 @@ "application/json": { "schema": { "$ref": "#/components/schemas/Metrics" + }, + "example": { + "metrics": [ + [ 0, 0, 0 ], + [ 2, 2, 2 ], + [ 0, 0, 0 ] + ], + "labels": [ + "2023-07-18T00:00:00Z", + "2023-07-19T00:00:00Z", + "2023-07-20T00:00:00Z" + ], + "schema": { + "metric": [ + { "entity": "broadcast", "operation": "count" }, + { "entity": "notification", "operation": "count" }, + { "entity": "user", "operation": "count" } + ], + "label": { + "name": "day", + "type": "timestamp" + } + } } } } @@ -2401,6 +2146,27 @@ "application/json": { "schema": { "$ref": "#/components/schemas/Metrics" + }, + "example": { + "metrics": [ + [ 37, 37, 31 ], + [ 243, 298, 49 ] + ], + "labels": [ + "member_invited", + "new_message" + ], + "schema": { + "metric": [ + { "entity": "broadcast", "operation": "count" }, + { "entity": "notification", "operation": "count" }, + { "entity": "user", "operation": "count" } + ], + "label": { + "name": "category", + "type": "string" + } + } } } } @@ -2426,6 +2192,27 @@ "application/json": { "schema": { "$ref": "#/components/schemas/Metrics" + }, + "example": { + "metrics": [ + [ 84, 123, 53], + [ 7, 12, 12 ] + ], + "labels": [ + "thread.23", + "thread.48" + ], + "schema": { + "metric": [ + { "entity": "broadcast", "operation": "count" }, + { "entity": "notification", "operation": "count" }, + { "entity": "user", "operation": "count" } + ], + "label": { + "name": "topic", + "type": "string" + } + } } } } @@ -2637,7 +2424,99 @@ "schema": { "type": "string" } } }, + "responses": { + "Unauthorized": { + "description": "Authentication credentials were missing or incorrect.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorArray" + }, + "example": { + "errors": [ + { + "message": "Authentication credentials were missing or incorrect." + } + ] + } + } + } + }, + "NotFound": { + "description": "The specified resource was not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorArray" + }, + "example": { + "errors": [ + { + "message": "A resource with the the given id doesn't exist" + } + ] + } + } + } + }, + "NoContent": { + "description": "The request was successful and the response body is empty" + }, + "UnprocessableEntity": { + "description": "The request was unacceptable due to invalid or missing data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorArray" + }, + "example": { + "errors": [ + { + "message": "The request was unacceptable due to invalid or missing data." + } + ] + } + } + } + } + }, "schemas": { + "Error": { + "type": "object", + "additionalProperties": true, + "required": ["message"], + "properties": { + "message": { + "nullable": false, + "type": "string", + "description": "The error message" + }, + "code": { + "type": "string", + "description": "The error code" + }, + "suggestion": { + "type": "string", + "description": "A suggestion to fix the error" + }, + "help_link": { + "type": "string", + "description": "A link to the api documentation for the called endpoint" + } + } + }, + "ErrorArray": { + "type": "object", + "additionalProperties": false, + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Error" + } + } + } + }, "CategoryChannels": { "type": "object", "additionalProperties": false, @@ -3315,9 +3194,7 @@ "type": "object", "additionalProperties": false, "properties": { - "total": { "type": "integer" }, "per_page": { "type": "integer" }, - "total_pages": { "type": "integer" }, "current_page": { "type": "integer" }, "push_subscriptions": { "type": "array", @@ -3433,7 +3310,7 @@ }, "status": { "type": "string", - "enum": ["processing"] + "enum": ["enqueued", "processing", "processed"] }, "summary": { "type": "object", @@ -3479,20 +3356,25 @@ "properties": { "users": { "type": "array", - "description": "User objects that could not be imported successfully", - "additionalProperties": false, - "properties": { - "email": { - "type": "string", - "description": "The identifying email of the user if supplied in the import request." - }, - "external_id": { - "type": "string", - "description": "The identifying external_id of the user if supplied in the import request." - }, - "errors": { - "type": "object", - "description": "The errors object reflecting the structure of the user oject" + "items": { + "type": "object", + "description": "User objects that could not be imported successfully", + "additionalProperties": false, + "properties": { + "email": { + "nullable": true, + "type": "string", + "description": "The identifying email of the user if supplied in the import request." + }, + "external_id": { + "nullable": true, + "type": "string", + "description": "The identifying external_id of the user if supplied in the import request." + }, + "errors": { + "type": "object", + "description": "The errors object reflecting the structure of the user oject" + } } } } diff --git a/yarn.lock b/yarn.lock index 18d4c5f..d1c6579 100644 --- a/yarn.lock +++ b/yarn.lock @@ -165,6 +165,13 @@ dependencies: "@babel/highlight" "^7.18.6" +"@babel/code-frame@^7.16.0": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" + integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== + dependencies: + "@babel/highlight" "^7.22.5" + "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.4": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.4.tgz#457ffe647c480dff59c2be092fc3acf71195c87f" @@ -408,6 +415,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== +"@babel/helper-validator-identifier@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" + integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== + "@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" @@ -441,6 +453,15 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" + integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== + dependencies: + "@babel/helper-validator-identifier" "^7.22.5" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.12.7", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" @@ -1789,6 +1810,11 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== +"@humanwhocodes/momoa@^2.0.2": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@humanwhocodes/momoa/-/momoa-2.0.4.tgz#8b9e7a629651d15009c3587d07a222deeb829385" + integrity sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA== + "@humanwhocodes/object-schema@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" @@ -4208,6 +4234,17 @@ base64-js@1.0.2: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.0.2.tgz#474211c95e6cf2a547db461e4f6778b51d08fa65" integrity sha512-ZXBDPMt/v/8fsIqn+Z5VwrhdR6jVka0bYobHdGia0Nxi7BJ9i/Uvml3AocHIBtIIBhZjBw5MR0aR4ROs/8+SNg== +better-ajv-errors@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/better-ajv-errors/-/better-ajv-errors-1.2.0.tgz#6412d58fa4d460ff6ccbd9e65c5fef9781cc5286" + integrity sha512-UW+IsFycygIo7bclP9h5ugkNH8EjCSgqyFB/yQ4Hqqa1OEYDtb0uFIkYE0b6+CjkgJYVM5UKI/pJPxjYe9EZlA== + dependencies: + "@babel/code-frame" "^7.16.0" + "@humanwhocodes/momoa" "^2.0.2" + chalk "^4.1.2" + jsonpointer "^5.0.0" + leven "^3.1.0 < 4" + better-path-resolve@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/better-path-resolve/-/better-path-resolve-1.0.0.tgz#13a35a1104cdd48a7b74bf8758f96a1ee613f99d" @@ -7856,6 +7893,11 @@ jsonpath@^1.1.1: static-eval "2.0.2" underscore "1.12.1" +jsonpointer@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559" + integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== + "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz#76b3e6e6cece5c69d49a5792c3d01bd1a0cdc7ea" @@ -7929,7 +7971,7 @@ lazy-cache@^1.0.3: resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" integrity sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ== -leven@^3.1.0: +leven@^3.1.0, "leven@^3.1.0 < 4": version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== From 96da57b85430ee5c3a5a22846ae4de4fea723887 Mon Sep 17 00:00:00 2001 From: Stephan Meijer Date: Thu, 20 Jul 2023 15:34:31 +0200 Subject: [PATCH 4/8] chore: fix colors of code snippets and borders (#131) --- docs/src/components/code/HighlightedCode.tsx | 2 +- docs/src/components/code/HighlightedCodeHeader.tsx | 4 +--- docs/src/components/tabs/TabsHeader.tsx | 2 +- docs/tailwind.config.js | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/src/components/code/HighlightedCode.tsx b/docs/src/components/code/HighlightedCode.tsx index de87014..5d054fa 100644 --- a/docs/src/components/code/HighlightedCode.tsx +++ b/docs/src/components/code/HighlightedCode.tsx @@ -70,7 +70,7 @@ export default function HighlightedCode({ ) : null}
       
{title}
diff --git a/docs/src/components/tabs/TabsHeader.tsx b/docs/src/components/tabs/TabsHeader.tsx index 86f6675..4d3dccf 100644 --- a/docs/src/components/tabs/TabsHeader.tsx +++ b/docs/src/components/tabs/TabsHeader.tsx @@ -20,7 +20,7 @@ export default function TabsHeader({ tabs, children, currentTabIndex }: Props) { return (
{tabs.map((tab, index) => children(tab, index))}
diff --git a/docs/tailwind.config.js b/docs/tailwind.config.js index ec04caf..0ed2a06 100644 --- a/docs/tailwind.config.js +++ b/docs/tailwind.config.js @@ -32,7 +32,7 @@ module.exports = { app2: '#1E212F', bgDefault: '#23283B', bgHover: '#21283E', - outlineDark: '#3F3566', + outlineDark: '#354061', darkGradientStart: '#23283d40', darkGradientEnd: '#1e212f0d', borderMuted: '#354061', From d7e9976b171ecd7e54d7219512d84c0e8fd488cb Mon Sep 17 00:00:00 2001 From: Stephan Meijer Date: Thu, 20 Jul 2023 16:44:33 +0200 Subject: [PATCH 5/8] chore: compact example --- openapi/spec/openapi.json | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/openapi/spec/openapi.json b/openapi/spec/openapi.json index bcbeeed..ee6ef57 100644 --- a/openapi/spec/openapi.json +++ b/openapi/spec/openapi.json @@ -1917,13 +1917,7 @@ } }, "example":{ - "categories": [ - { - "slug": "comments" - }, { - "slug": "likes" - } - ] + "categories": [{ "slug": "comments" }] } } } From 223c0250ac76f9e456f4a7454c8b38dc872b3ab0 Mon Sep 17 00:00:00 2001 From: Stephan Meijer Date: Thu, 20 Jul 2023 17:14:46 +0200 Subject: [PATCH 6/8] chore: add changeset (#124) --- .changeset/sixty-bats-unite.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/sixty-bats-unite.md diff --git a/.changeset/sixty-bats-unite.md b/.changeset/sixty-bats-unite.md new file mode 100644 index 0000000..6cc0362 --- /dev/null +++ b/.changeset/sixty-bats-unite.md @@ -0,0 +1,7 @@ +--- +"openapi": minor +--- + +Add endpoint to the list the device tokens registered for push notifications of a specific user. + +- `GET /push_subscriptions` From 8b4f8390e4cf370b46b314729b5c107b3e835424 Mon Sep 17 00:00:00 2001 From: Stephan Meijer Date: Thu, 20 Jul 2023 17:17:30 +0200 Subject: [PATCH 7/8] chore: update changeset --- .changeset/mighty-dancers-pay.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.changeset/mighty-dancers-pay.md b/.changeset/mighty-dancers-pay.md index 3a4a75e..2e03e83 100644 --- a/.changeset/mighty-dancers-pay.md +++ b/.changeset/mighty-dancers-pay.md @@ -2,4 +2,6 @@ "openapi": minor --- -add `/users/{user_id}/notifications` +Add endpoint to list notifications of a specific user + +- `GET /users/{user_id}/notifications` From d908ba7de5dab2cac8550b2bc3d8c41f51ee08d9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 20 Jul 2023 17:19:11 +0200 Subject: [PATCH 8/8] chore: version packages (#96) --- .changeset/forty-jeans-cough.md | 9 ------- .changeset/lazy-kings-shave.md | 9 ------- .changeset/mighty-dancers-pay.md | 7 ----- .changeset/nasty-deers-jog.md | 5 ---- .changeset/pretty-chairs-fly.md | 8 ------ .changeset/sixty-bats-unite.md | 7 ----- .changeset/small-penguins-film.md | 8 ------ .changeset/warm-baboons-repair.md | 9 ------- openapi/CHANGELOG.md | 44 +++++++++++++++++++++++++++++++ openapi/package.json | 2 +- 10 files changed, 45 insertions(+), 63 deletions(-) delete mode 100644 .changeset/forty-jeans-cough.md delete mode 100644 .changeset/lazy-kings-shave.md delete mode 100644 .changeset/mighty-dancers-pay.md delete mode 100644 .changeset/nasty-deers-jog.md delete mode 100644 .changeset/pretty-chairs-fly.md delete mode 100644 .changeset/sixty-bats-unite.md delete mode 100644 .changeset/small-penguins-film.md delete mode 100644 .changeset/warm-baboons-repair.md diff --git a/.changeset/forty-jeans-cough.md b/.changeset/forty-jeans-cough.md deleted file mode 100644 index 4cd2f2d..0000000 --- a/.changeset/forty-jeans-cough.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"openapi": minor ---- - -Remove the `total` and `total_pages` properties from the response of the following requests: - -- `GET /broadcasts`. -- `GET /broadcasts/{broadcast-id}/notifications`. -- `GET /users`. diff --git a/.changeset/lazy-kings-shave.md b/.changeset/lazy-kings-shave.md deleted file mode 100644 index 38320dd..0000000 --- a/.changeset/lazy-kings-shave.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"openapi": minor ---- - -Remove the beta flags from the broadcasts apis. This includes: - -- `GET /broadcasts` -- `GET /broadcasts/{broadcast_id}` -- `GET /broadcasts/{broadcast_id}/notifications` diff --git a/.changeset/mighty-dancers-pay.md b/.changeset/mighty-dancers-pay.md deleted file mode 100644 index 2e03e83..0000000 --- a/.changeset/mighty-dancers-pay.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"openapi": minor ---- - -Add endpoint to list notifications of a specific user - -- `GET /users/{user_id}/notifications` diff --git a/.changeset/nasty-deers-jog.md b/.changeset/nasty-deers-jog.md deleted file mode 100644 index 0e7a68f..0000000 --- a/.changeset/nasty-deers-jog.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"openapi": patch ---- - -rename operation from users-fetch to users-get to match convention. diff --git a/.changeset/pretty-chairs-fly.md b/.changeset/pretty-chairs-fly.md deleted file mode 100644 index 764fea3..0000000 --- a/.changeset/pretty-chairs-fly.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"openapi": minor ---- - -Remove the beta flags from the import apis. This includes: - -- `POST /imports` -- `GET /imports/{import_id}` diff --git a/.changeset/sixty-bats-unite.md b/.changeset/sixty-bats-unite.md deleted file mode 100644 index 6cc0362..0000000 --- a/.changeset/sixty-bats-unite.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"openapi": minor ---- - -Add endpoint to the list the device tokens registered for push notifications of a specific user. - -- `GET /push_subscriptions` diff --git a/.changeset/small-penguins-film.md b/.changeset/small-penguins-film.md deleted file mode 100644 index 797dae4..0000000 --- a/.changeset/small-penguins-film.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"openapi": minor ---- - -Remove the beta flags from the users push-subscriptions apis. This includes: - -- `GET /users/{user_id}/push_subscriptions` -- `DELETE /users/{user_id}/push_subscriptions/{subscription_id}` diff --git a/.changeset/warm-baboons-repair.md b/.changeset/warm-baboons-repair.md deleted file mode 100644 index 2e49213..0000000 --- a/.changeset/warm-baboons-repair.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"openapi": minor ---- - -add metrics endpoints: - -- `GET /metrics` -- `GET /metrics/categories` -- `GET /metrics/topics` diff --git a/openapi/CHANGELOG.md b/openapi/CHANGELOG.md index 98f0ebc..3cf205c 100644 --- a/openapi/CHANGELOG.md +++ b/openapi/CHANGELOG.md @@ -1,5 +1,49 @@ # openapi +## 1.2.0 + +### Minor Changes + +- [#97](https://github.com/magicbell-io/public/pull/97) [`6fc587f`](https://github.com/magicbell-io/public/commit/6fc587f9b96864799f32fdf4a62fa3f07ab626fa) Thanks [@smeijer](https://github.com/smeijer)! - Remove the `total` and `total_pages` properties from the response of the following requests: + + - `GET /broadcasts`. + - `GET /broadcasts/{broadcast-id}/notifications`. + - `GET /users`. + +- [#114](https://github.com/magicbell-io/public/pull/114) [`0a379c8`](https://github.com/magicbell-io/public/commit/0a379c8c18ebd1ab867c14249c5a6707e7b90c18) Thanks [@smeijer](https://github.com/smeijer)! - Remove the beta flags from the broadcasts apis. This includes: + + - `GET /broadcasts` + - `GET /broadcasts/{broadcast_id}` + - `GET /broadcasts/{broadcast_id}/notifications` + +- [`621fd33`](https://github.com/magicbell-io/public/commit/621fd33336eb93a7e9dbb6dfb6514fe4cd98811c) Thanks [@smeijer](https://github.com/smeijer)! - Add endpoint to list notifications of a specific user + + - `GET /users/{user_id}/notifications` + +- [#115](https://github.com/magicbell-io/public/pull/115) [`a4ed9c4`](https://github.com/magicbell-io/public/commit/a4ed9c4dc8bdb94d81d0c2737bb75ca5336e1efb) Thanks [@smeijer](https://github.com/smeijer)! - Remove the beta flags from the import apis. This includes: + + - `POST /imports` + - `GET /imports/{import_id}` + +- [`223c025`](https://github.com/magicbell-io/public/commit/223c0250ac76f9e456f4a7454c8b38dc872b3ab0) Thanks [@smeijer](https://github.com/smeijer)! - Add endpoint to the list the device tokens registered for push notifications of a specific user. + + - `GET /push_subscriptions` + +- [#116](https://github.com/magicbell-io/public/pull/116) [`7f45ed2`](https://github.com/magicbell-io/public/commit/7f45ed2b1e2eac308a287f5d4aeefff4a6e37dcc) Thanks [@smeijer](https://github.com/smeijer)! - Remove the beta flags from the users push-subscriptions apis. This includes: + + - `GET /users/{user_id}/push_subscriptions` + - `DELETE /users/{user_id}/push_subscriptions/{subscription_id}` + +- [`9def653`](https://github.com/magicbell-io/public/commit/9def6536358f86cc87899ace03329b0806dc6ee2) Thanks [@smeijer](https://github.com/smeijer)! - add metrics endpoints: + + - `GET /metrics` + - `GET /metrics/categories` + - `GET /metrics/topics` + +### Patch Changes + +- [#95](https://github.com/magicbell-io/public/pull/95) [`0089911`](https://github.com/magicbell-io/public/commit/0089911c9fb708011370d778df119a6951a1bc79) Thanks [@smeijer](https://github.com/smeijer)! - rename operation from users-fetch to users-get to match convention. + ## 1.1.0 ### Minor Changes diff --git a/openapi/package.json b/openapi/package.json index 1a5a864..2728177 100644 --- a/openapi/package.json +++ b/openapi/package.json @@ -1,6 +1,6 @@ { "name": "openapi", - "version": "1.1.0", + "version": "1.2.0", "description": "An OpenAPI specification for the MagicBell API", "repository": "https://github.com/magicbell-io/public/tree/main/openapi", "author": "Stephan Meijer ",