From 78bb61d20108c7cc37ab67484cceb96a51a8d2c3 Mon Sep 17 00:00:00 2001 From: Stephan Meijer Date: Thu, 11 Jul 2024 15:31:49 +0200 Subject: [PATCH] feat: add `@magicbell/user-client` package (#310) --- .changeset/angry-sloths-flow.md | 5 + .eslintrc.cjs | 8 + example/package.json | 1 + jest.config.cjs | 30 +- jest.setup.ts | 4 +- package.json | 5 +- packages/playground/package.json | 4 +- packages/react-headless/package.json | 4 +- packages/react/package.json | 4 +- packages/react/src/lib/color.ts | 2 +- packages/user-client/.gitignore | 2 + packages/user-client/LICENSE | 194 ++++ packages/user-client/README.md | 866 ++++++++++++++++++ packages/user-client/liblab.config.json | 46 + packages/user-client/package.json | 51 ++ packages/user-client/scripts/build.ts | 150 +++ packages/user-client/src/BaseService.ts | 46 + packages/user-client/src/hooks/Hook.ts | 28 + packages/user-client/src/http/Environment.ts | 3 + packages/user-client/src/http/HTTPClient.ts | 12 + packages/user-client/src/http/HTTPLibrary.ts | 121 +++ .../user-client/src/http/QuerySerializer.ts | 40 + .../user-client/src/http/errors/BadGateway.ts | 11 + .../user-client/src/http/errors/BadRequest.ts | 11 + .../user-client/src/http/errors/Conflict.ts | 11 + .../src/http/errors/ExpectationFailed.ts | 11 + .../src/http/errors/FailedDependency.ts | 11 + .../user-client/src/http/errors/Forbidden.ts | 11 + .../src/http/errors/GatewayTimeout.ts | 11 + packages/user-client/src/http/errors/Gone.ts | 11 + .../http/errors/HttpVersionNotSupported.ts | 11 + .../src/http/errors/InternalServerError.ts | 11 + .../src/http/errors/LengthRequired.ts | 11 + .../user-client/src/http/errors/Locked.ts | 11 + .../src/http/errors/LoopDetected.ts | 11 + .../src/http/errors/MethodNotAllowed.ts | 14 + .../src/http/errors/MisdirectedRequest.ts | 11 + .../errors/NetworkAuthenticationRequired.ts | 11 + .../src/http/errors/NotAcceptable.ts | 11 + .../src/http/errors/NotExtended.ts | 11 + .../user-client/src/http/errors/NotFound.ts | 11 + .../src/http/errors/NotImplemented.ts | 11 + .../src/http/errors/PayloadTooLarge.ts | 14 + .../src/http/errors/PaymentRequired.ts | 11 + .../src/http/errors/PreconditionFailed.ts | 11 + .../src/http/errors/PreconditionRequired.ts | 11 + .../errors/ProxyAuthenticationRequired.ts | 14 + .../src/http/errors/RangeNotSatisfiable.ts | 11 + .../errors/RequestHeaderFieldsTooLarge.ts | 11 + .../src/http/errors/RequestTimeout.ts | 11 + .../src/http/errors/ServiceUnavailable.ts | 14 + .../user-client/src/http/errors/TooEarly.ts | 11 + .../src/http/errors/TooManyRequests.ts | 14 + .../src/http/errors/Unauthorized.ts | 14 + .../http/errors/UnavailableForLegalReasons.ts | 11 + .../src/http/errors/UnprocessableEntity.ts | 11 + .../src/http/errors/UnsufficientStorage.ts | 11 + .../src/http/errors/UnsupportedMediaType.ts | 11 + .../src/http/errors/UpgradeRequired.ts | 11 + .../user-client/src/http/errors/UriTooLong.ts | 11 + .../src/http/errors/VariantAlsoNegotiates.ts | 11 + packages/user-client/src/http/errors/base.ts | 52 ++ packages/user-client/src/http/errors/index.ts | 83 ++ .../user-client/src/http/httpExceptions.ts | 135 +++ packages/user-client/src/index.ts | 45 + packages/user-client/src/models.ts | 29 + packages/user-client/src/services/README.md | 781 ++++++++++++++++ .../src/services/channels/Channels.ts | 374 ++++++++ .../src/services/channels/index.ts | 20 + .../src/services/channels/models/ApnsToken.ts | 6 + .../channels/models/ApnsTokenWithMetadata.ts | 14 + .../models/ArrayWithMetadataOfApnsToken.ts | 16 + .../models/ArrayWithMetadataOfFcmToken.ts | 16 + .../models/ArrayWithMetadataOfSlackToken.ts | 16 + .../models/ArrayWithMetadataOfTeamsToken.ts | 16 + .../models/ArrayWithMetadataOfWebPushToken.ts | 16 + .../services/channels/models/DiscardResult.ts | 4 + .../src/services/channels/models/FcmToken.ts | 6 + .../channels/models/FcmTokenWithMetadata.ts | 14 + .../models/SaveMobilePushApnsTokenRequest.ts | 6 + .../models/SaveMobilePushFcmTokenRequest.ts | 6 + .../channels/models/SaveSlackTokenRequest.ts | 12 + .../channels/models/SaveTeamsTokenRequest.ts | 1 + .../models/SaveWebPushTokenRequest.ts | 8 + .../services/channels/models/SlackToken.ts | 12 + .../channels/models/SlackTokenWithMetadata.ts | 14 + .../services/channels/models/TeamsToken.ts | 1 + .../channels/models/TeamsTokenWithMetadata.ts | 14 + .../services/channels/models/WebPushToken.ts | 8 + .../models/WebPushTokenWithMetadata.ts | 14 + .../src/services/integrations/Integrations.ts | 96 ++ .../src/services/integrations/index.ts | 8 + .../models/FinishSlackInstallationRequest.ts | 5 + .../models/SaveSlackInstallationRequest.ts | 36 + .../SaveTemplatesInstallationRequest.ts | 5 + .../integrations/models/SlackInstallation.ts | 36 + .../models/StartSlackInstallationRequest.ts | 6 + .../models/StartWebPushInstallationRequest.ts | 1 + .../models/TemplatesInstallation.ts | 5 + .../WebPushStartInstallationResponse.ts | 4 + packages/user-client/tsconfig.build.json | 14 + tsconfig.json | 7 +- tsconfig.test.json | 16 + yarn.lock | 97 +- 104 files changed, 4128 insertions(+), 20 deletions(-) create mode 100644 .changeset/angry-sloths-flow.md create mode 100644 packages/user-client/.gitignore create mode 100644 packages/user-client/LICENSE create mode 100644 packages/user-client/README.md create mode 100644 packages/user-client/liblab.config.json create mode 100644 packages/user-client/package.json create mode 100644 packages/user-client/scripts/build.ts create mode 100644 packages/user-client/src/BaseService.ts create mode 100644 packages/user-client/src/hooks/Hook.ts create mode 100644 packages/user-client/src/http/Environment.ts create mode 100644 packages/user-client/src/http/HTTPClient.ts create mode 100644 packages/user-client/src/http/HTTPLibrary.ts create mode 100644 packages/user-client/src/http/QuerySerializer.ts create mode 100644 packages/user-client/src/http/errors/BadGateway.ts create mode 100644 packages/user-client/src/http/errors/BadRequest.ts create mode 100644 packages/user-client/src/http/errors/Conflict.ts create mode 100644 packages/user-client/src/http/errors/ExpectationFailed.ts create mode 100644 packages/user-client/src/http/errors/FailedDependency.ts create mode 100644 packages/user-client/src/http/errors/Forbidden.ts create mode 100644 packages/user-client/src/http/errors/GatewayTimeout.ts create mode 100644 packages/user-client/src/http/errors/Gone.ts create mode 100644 packages/user-client/src/http/errors/HttpVersionNotSupported.ts create mode 100644 packages/user-client/src/http/errors/InternalServerError.ts create mode 100644 packages/user-client/src/http/errors/LengthRequired.ts create mode 100644 packages/user-client/src/http/errors/Locked.ts create mode 100644 packages/user-client/src/http/errors/LoopDetected.ts create mode 100644 packages/user-client/src/http/errors/MethodNotAllowed.ts create mode 100644 packages/user-client/src/http/errors/MisdirectedRequest.ts create mode 100644 packages/user-client/src/http/errors/NetworkAuthenticationRequired.ts create mode 100644 packages/user-client/src/http/errors/NotAcceptable.ts create mode 100644 packages/user-client/src/http/errors/NotExtended.ts create mode 100644 packages/user-client/src/http/errors/NotFound.ts create mode 100644 packages/user-client/src/http/errors/NotImplemented.ts create mode 100644 packages/user-client/src/http/errors/PayloadTooLarge.ts create mode 100644 packages/user-client/src/http/errors/PaymentRequired.ts create mode 100644 packages/user-client/src/http/errors/PreconditionFailed.ts create mode 100644 packages/user-client/src/http/errors/PreconditionRequired.ts create mode 100644 packages/user-client/src/http/errors/ProxyAuthenticationRequired.ts create mode 100644 packages/user-client/src/http/errors/RangeNotSatisfiable.ts create mode 100644 packages/user-client/src/http/errors/RequestHeaderFieldsTooLarge.ts create mode 100644 packages/user-client/src/http/errors/RequestTimeout.ts create mode 100644 packages/user-client/src/http/errors/ServiceUnavailable.ts create mode 100644 packages/user-client/src/http/errors/TooEarly.ts create mode 100644 packages/user-client/src/http/errors/TooManyRequests.ts create mode 100644 packages/user-client/src/http/errors/Unauthorized.ts create mode 100644 packages/user-client/src/http/errors/UnavailableForLegalReasons.ts create mode 100644 packages/user-client/src/http/errors/UnprocessableEntity.ts create mode 100644 packages/user-client/src/http/errors/UnsufficientStorage.ts create mode 100644 packages/user-client/src/http/errors/UnsupportedMediaType.ts create mode 100644 packages/user-client/src/http/errors/UpgradeRequired.ts create mode 100644 packages/user-client/src/http/errors/UriTooLong.ts create mode 100644 packages/user-client/src/http/errors/VariantAlsoNegotiates.ts create mode 100644 packages/user-client/src/http/errors/base.ts create mode 100644 packages/user-client/src/http/errors/index.ts create mode 100644 packages/user-client/src/http/httpExceptions.ts create mode 100644 packages/user-client/src/index.ts create mode 100644 packages/user-client/src/models.ts create mode 100644 packages/user-client/src/services/README.md create mode 100644 packages/user-client/src/services/channels/Channels.ts create mode 100644 packages/user-client/src/services/channels/index.ts create mode 100644 packages/user-client/src/services/channels/models/ApnsToken.ts create mode 100644 packages/user-client/src/services/channels/models/ApnsTokenWithMetadata.ts create mode 100644 packages/user-client/src/services/channels/models/ArrayWithMetadataOfApnsToken.ts create mode 100644 packages/user-client/src/services/channels/models/ArrayWithMetadataOfFcmToken.ts create mode 100644 packages/user-client/src/services/channels/models/ArrayWithMetadataOfSlackToken.ts create mode 100644 packages/user-client/src/services/channels/models/ArrayWithMetadataOfTeamsToken.ts create mode 100644 packages/user-client/src/services/channels/models/ArrayWithMetadataOfWebPushToken.ts create mode 100644 packages/user-client/src/services/channels/models/DiscardResult.ts create mode 100644 packages/user-client/src/services/channels/models/FcmToken.ts create mode 100644 packages/user-client/src/services/channels/models/FcmTokenWithMetadata.ts create mode 100644 packages/user-client/src/services/channels/models/SaveMobilePushApnsTokenRequest.ts create mode 100644 packages/user-client/src/services/channels/models/SaveMobilePushFcmTokenRequest.ts create mode 100644 packages/user-client/src/services/channels/models/SaveSlackTokenRequest.ts create mode 100644 packages/user-client/src/services/channels/models/SaveTeamsTokenRequest.ts create mode 100644 packages/user-client/src/services/channels/models/SaveWebPushTokenRequest.ts create mode 100644 packages/user-client/src/services/channels/models/SlackToken.ts create mode 100644 packages/user-client/src/services/channels/models/SlackTokenWithMetadata.ts create mode 100644 packages/user-client/src/services/channels/models/TeamsToken.ts create mode 100644 packages/user-client/src/services/channels/models/TeamsTokenWithMetadata.ts create mode 100644 packages/user-client/src/services/channels/models/WebPushToken.ts create mode 100644 packages/user-client/src/services/channels/models/WebPushTokenWithMetadata.ts create mode 100644 packages/user-client/src/services/integrations/Integrations.ts create mode 100644 packages/user-client/src/services/integrations/index.ts create mode 100644 packages/user-client/src/services/integrations/models/FinishSlackInstallationRequest.ts create mode 100644 packages/user-client/src/services/integrations/models/SaveSlackInstallationRequest.ts create mode 100644 packages/user-client/src/services/integrations/models/SaveTemplatesInstallationRequest.ts create mode 100644 packages/user-client/src/services/integrations/models/SlackInstallation.ts create mode 100644 packages/user-client/src/services/integrations/models/StartSlackInstallationRequest.ts create mode 100644 packages/user-client/src/services/integrations/models/StartWebPushInstallationRequest.ts create mode 100644 packages/user-client/src/services/integrations/models/TemplatesInstallation.ts create mode 100644 packages/user-client/src/services/integrations/models/WebPushStartInstallationResponse.ts create mode 100644 packages/user-client/tsconfig.build.json create mode 100644 tsconfig.test.json diff --git a/.changeset/angry-sloths-flow.md b/.changeset/angry-sloths-flow.md new file mode 100644 index 000000000..f3176d4b9 --- /dev/null +++ b/.changeset/angry-sloths-flow.md @@ -0,0 +1,5 @@ +--- +'@magicbell/user-client': minor +--- + +An early release of `@magicbell/user-client`, a user facing client, to be used in web browsers, focussing on the MagicBell v2 api. diff --git a/.eslintrc.cjs b/.eslintrc.cjs index b3b3f0e59..a13765b2d 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -45,6 +45,14 @@ module.exports = { 'cypress/globals': true, } }, + { + files: './packages/user-client/**/*.{ts,tsx}', + rules: { + '@typescript-eslint/no-empty-interface': 'off', + '@typescript-eslint/no-unused-vars': 'off', + '@typescript-eslint/ban-types': 'off', + } + }, { files: ['**/*.vue'], extends: ['plugin:vue/vue3-recommended'], diff --git a/example/package.json b/example/package.json index 0f4acbf6c..dfafaaf23 100644 --- a/example/package.json +++ b/example/package.json @@ -21,6 +21,7 @@ "@magicbell/embeddable": "../packages/embeddable", "@magicbell/magicbell-react": "../packages/react", "@magicbell/react-headless": "../packages/react-headless", + "@magicbell/user-client": "../packages/user-client", "@magicbell/utils": "../packages/utils", "@magicbell/webpush": "../packages/webpush", "magicbell": "../packages/magicbell", diff --git a/jest.config.cjs b/jest.config.cjs index e795ce437..16e50eed6 100644 --- a/jest.config.cjs +++ b/jest.config.cjs @@ -6,15 +6,21 @@ const moduleNameMapper = Object.fromEntries( Object.entries(tsconf.compilerOptions.paths) .filter(x => x[1][0].startsWith('packages')) .map(x => [x[0], x[1][0].replace(/^packages/, '/packages')]) - ); +); + +// { '@magicbell/core': '/packages/core/src' } > [['@magicbell/core', '/packages/core']] +const packages = Object.entries(moduleNameMapper) + .map(([pkg, dir]) => [pkg, dir.split('/').slice(0, 3).join('/')]) + .sort(([a], [b]) => a.localeCompare(b)); /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ -module.exports = { +const commonConfig = { preset: 'ts-jest', testEnvironment: 'jest-environment-jsdom', transform: { - '^.+\\.ts?$': 'ts-jest', - '^.+\\.tsx?$': 'ts-jest', + '\\.[jt]sx?$': ['ts-jest', { + tsconfig: 'tsconfig.test.json' + }] }, modulePathIgnorePatterns: ['/packages/magicbell/dist', '/packages/playground', '/packages/embeddable/cypress'], globals: { @@ -23,9 +29,23 @@ module.exports = { __DEV__: false, }, setupFilesAfterEnv: [ - '/jest.setup.ts', + './jest.setup.ts', ], clearMocks: true, resetMocks: true, moduleNameMapper, }; + +/** @type {import('jest').Config} */ +module.exports = { + projects: packages.map(([name, dir]) => ({ + ...commonConfig, + displayName: name, + testEnvironment: name === '@magicbell/user-client' ? 'node' : "jest-environment-jsdom", + testMatch: [ + `${dir}/src/**/*.test.[jt]s?(x)"`, + `${dir}/test/**/*.[jt]s?(x)"`, + `${dir}/tests/**/*.spec.[jt]s?(x)"`, + ], + })) +}; diff --git a/jest.setup.ts b/jest.setup.ts index 9e6a781fc..775d22eda 100644 --- a/jest.setup.ts +++ b/jest.setup.ts @@ -17,7 +17,9 @@ if (!globalThis.EventSource) { // it's defined in vitest environment // eslint-disable-next-line no-undef -window.open = jest.fn(); +if (typeof window !== 'undefined') { + window.open = jest.fn(); +} beforeEach(() => { useConfig.setState({ lastFetchedAt: Date.now() }); diff --git a/package.json b/package.json index 133210e49..53f09fefb 100644 --- a/package.json +++ b/package.json @@ -82,11 +82,12 @@ "msw": "^1.3.2", "npm-run-all2": "^6.2.0", "prettier": "^2.8.8", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", "rimraf": "^5.0.7", "rollup-plugin-analyzer": "^4.0.0", "size-limit": "^8.2.6", + "sort-package-json": "^2.10.0", "tiny-glob": "^0.2.9", "ts-jest": "^29.2.0", "tslib": "^2.6.2", diff --git a/packages/playground/package.json b/packages/playground/package.json index ec6c7636a..cdbf89fa7 100644 --- a/packages/playground/package.json +++ b/packages/playground/package.json @@ -16,8 +16,8 @@ "glob": "^8.1.0", "next": "14.2.3", "next-runtime": "^2.4.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", "react-popper": "^2.3.0", "twind": "^0.16.19" }, diff --git a/packages/react-headless/package.json b/packages/react-headless/package.json index 09f27170b..41adae005 100644 --- a/packages/react-headless/package.json +++ b/packages/react-headless/package.json @@ -66,8 +66,8 @@ "@types/ramda": "^0.28.0", "@types/rosie": "^0.0.45", "babel-loader": "^8.3.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", "rosie": "^2.1.1", "sinon": "^13.0.2", "size-limit": "^8.2.6", diff --git a/packages/react/package.json b/packages/react/package.json index d17e567b6..a690702da 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -68,8 +68,8 @@ "@types/tinycolor2": "^1.4.2", "codecov": "^3.8.3", "faker": "^5.5.3", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", "rosie": "^2.1.1", "sinon": "^13.0.2", "size-limit": "^8.2.6", diff --git a/packages/react/src/lib/color.ts b/packages/react/src/lib/color.ts index 478d4ee36..89ed16ae0 100644 --- a/packages/react/src/lib/color.ts +++ b/packages/react/src/lib/color.ts @@ -8,7 +8,7 @@ import tinycolor from 'tinycolor2'; */ export function toRGBA(baseColor: string, alpha: number) { // tinycolor doesn't support css variables, so assume the variable is correct as is - if (baseColor.startsWith('var(')) return baseColor; + if (baseColor.startsWith('var(')) return baseColor; const color = tinycolor(baseColor); color.setAlpha(alpha); diff --git a/packages/user-client/.gitignore b/packages/user-client/.gitignore new file mode 100644 index 000000000..8313d20b7 --- /dev/null +++ b/packages/user-client/.gitignore @@ -0,0 +1,2 @@ +magicbell-client.swagger.json +output diff --git a/packages/user-client/LICENSE b/packages/user-client/LICENSE new file mode 100644 index 000000000..2b3bdb112 --- /dev/null +++ b/packages/user-client/LICENSE @@ -0,0 +1,194 @@ +LICENSE AGREEMENT + +By downloading or using this software made available by MagicBell, Inc. +("MagicBell") or any documentation that accompanies it (collectively, the +"Software"), you and the company or entity that you represent (collectively, +"you" or "your") are consenting to be bound by and are becoming a party to this +License Agreement (this "Agreement"). You hereby represent and warrant that you +are authorized and lawfully able to bind such company or entity that you +represent to this Agreement. If you do not have such authority or do not agree +to all of the terms of this Agreement, you may not download or use the Software. + +LICENSE GRANT. Subject to your compliance with and the terms and conditions of +this Agreement, MagicBell, Inc. ("MagicBell") hereby grants you a limited, +personal, non-exclusive, non-sublicensable, non-transferable license to (a) use, +install, and run the Software solely to create and display a customized user +interface in connection with your internal use of MagicBell’s embeddable +notification inbox product (the "MagicBell Product"); and (b) modify the +Software (the results thereof, "Modifications") solely to customize your user +interface in connection with your use of the MagicBell Product; (c) compile and +execute object code versions of the Software (including as incorporating your +Modifications) solely to use internally your customized MagicBell Product user +interface; and (d) make electronic copies of the Software and any Modifications +as required for backup or archival purposes. You acknowledge that your use of +any components provided with the Software that are licensed under an open source +software license ("Open Source Components") are not part of the Software +licensed hereunder and are subject to and governed solely by the terms of the +applicable license(s) for that software, and not by this Agreement. + +RESTRICTIONS. You are responsible for all activities that occur in connection +with the Software. Except as otherwise expressly authorized by MagicBell, you +may not directly or indirectly: (a) sublicense, sell, assign, distribute, +modify, make derivative works of, make any commercial use of, use on a timeshare +or service bureau basis, use for the benefit of a third party or otherwise +commercialize the Software (or any Modifications); (b) allow third parties to +access or use the source code of the Software (or any Modifications); use the +Software (or any Modifications) to create or facilitate the creation of, or +otherwise incorporate any portion of the Software (or any Modifications) in, +any product or service that is competitive with the MagicBell Product; (c) use +the Software (or any Modifications) to perform comparisons or other +"benchmarking" activities; (d) remove any proprietary notices or branding from +the Software; and/or (e) use the Software (or any Modifications) in violation +of any applicable laws or regulations or outside of the scope of the license +granted in Section 1. You shall ensure that there is no direct or indirect use +of, or sharing of, the Software (or any Modifications), or other information +based upon or derived from the Software (or any Modifications) to develop such +competitive products. Without derogating the generality of the foregoing, +development of any competitive product shall include having direct or indirect +access to, supervising, consulting or assisting in the development of, or +product any specifications, documentations, object code or source code for, all +or part of any competitive product. + +AUTHORIZED USERS. Subject to the rights granted to you under this Agreement, +you may permit your employees, contractors and agents to exercise the rights +granted herein in accordance with the Agreement solely on behalf of you to +provide services to you, provided that you are liable for all acts and omissions +thereof to the extent that any such acts and omissions, if performed by you, +would constitute a breach or, or otherwise giver right to liability to you, +under this Agreement. You represent and warrant that you shall not permit any +third party to access or use the Software (or any Modifications) except as +expressly permitted under this Agreement. + +OPEN SOURCE COMPONENTS. You and your authorized users shall not use any Open +Source Components in connection with the Software or any Modifications or in any +way that could subject the Software to an open source license. + +OWNERSHIP. As between MagicBell and you, MagicBell or its licensors shall own +and retain all proprietary rights, including all patent, copyright, trade +secret, trademark and other intellectual property rights, in and to the Software +and any Modifications and you hereby irrevocably transfer, convey and assign to +MagicBell all right, title, and interest in any Modifications regardless of +whether such Modifications are actually delivered to MagicBell. MagicBell shall +have the exclusive right to apply for or register any patents, mask work rights, +copyrights, and such other proprietary protections with respect thereto. You +acknowledge that the license granted under Section 2 of this Agreement does not +provide you with title or ownership to the Software or any such Modifications, +but only a right of limited use under the terms and conditions of this +Agreement. + +You represent and warrant to MagicBell that you have the full right, and have +obtained all consents, approvals, authorizations, permits and licenses required +or necessary, to allow you to grant to MagicBell the assignments and rights +provided for herein and otherwise fully perform this Agreement (and have written +enforceable agreements with all persons necessary to give you the rights to do +the foregoing). + +LIMITATIONS ON MODIFICATIONS TO SOFTWARE. Notwithstanding any provision in this +Agreement, Modifications may only be created and used by you as permitted by +this Agreement. You are solely responsible for all use of Modifications and, +without limiting anything herein, MagicBell will have no liability with respect +to Modifications. You will not assert against MagicBell, its affiliates or +their customers, direct or indirect, agents and contractors, in any way, any +intellectual property rights that you may obtain relating to any Modifications +for the Software. + +SUPPORT AND UPGRADES. If MagicBell provides you with any upgrades, patches, +enhancements or fixes for the Software that it makes generally available free +of charge in connection with the Software, then the items that are provided will +become part of the Software and subject to this Agreement. MagicBell shall have +no obligation, however, under this Agreement or otherwise to provide any +upgrades, patches, enhancements, fixes or any other support to you for the +Software. + +WARRANTY AND DISCLAIMER. USE OF THE SOFTWARE IS ENTIRELY AT YOUR OWN RISK. +MAGICBELL PROVIDES THE SOFTWARE "AS IS" AND "AS AVAILABLE" WITHOUT ANY WARRANTY +OF ANY KIND AND HEREBY DISCLAIMS, FOR ITSELF AND ITS LICENSORS AND SUPPLIERS, +ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING WITHOUT LIMITATION WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, PERFORMANCE, ACCURACY, +RELIABILITY, NON-INFRINGEMENT, AND WARRANTIES ARISING OUT OF COURSE OF +PERFORMANCE, COURSE OF DEALING OR USAGE IN TRADE. MAGICBELL DOES NOT WARRANT +THAT THE SOFTWARE IS ERROR-FREE, WILL FUNCTION WITHOUT INTERRUPTION, WILL MEET +ANY SPECIFIC NEED THAT YOU MAY HAVE, THAT ALL DEFECTS WILL BE CORRECTED OR THAT +IT IS SUFFICIENTLY DOCUMENTED TO BE USEABLE BY YOU. THIS DISCLAIMER OF WARRANTY +CONSTITUTES AN ESSENTIAL PART OF THE AGREEMENT. + +LIMITATION OF LIABILITY. NOTWITHSTANDING ANYTHING ELSE, UNDER NO CIRCUMSTANCES +SHALL MAGICBELL OR ITS LICENSORS OR SUPPLIERS, BE LIABLE TO YOU OR ANY OTHER +PERSON WITH RESPECT TO THE SUBJECT MATTER OF THIS AGREEMENT UNDER ANY CONTRACT, +TORT, NEGLIGENCE, STRICT LIABILITY, WARRANTY OR OTHER LEGAL OR EQUITABLE THEORY, +FOR ANY INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OF ANY KIND +INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, +LOSS OF DATA, COSTS OF PROCUREMENT OF SUBSTITUTE GOODS, SERVICES, TECHNOLOGY OR +RIGHTS, INTERRUPTION OF BUSINESS, ACCURACY OF RESULTS, COMPUTER FAILURE OR +MALFUNCTION, OR OTHER DAMAGES IN EXCESS OF ONE HUNDRED DOLLARS (US$100), EVEN IF +AWARE OF THE POSSIBILITY OF SUCH DAMAGES. + +BASIS OF BARGAIN. YOU AND MAGICBELL EACH RECOGNIZE AND AGREE THAT THE WARRANTY +DISCLAIMERS AND LIABILITY AND REMEDY LIMITATIONS IN THIS AGREEMENT ARE MATERIAL, +BARGAINED FOR BASES FOR THIS AGREEMENT AND THAT THEY HAVE BEEN TAKEN INTO +ACCOUNT AND REFLECTED IN DETERMINING THE CONSIDERATION TO BE GIVEN BY EACH PARTY +UNDER THIS AGREEMENT AND IN THE DECISION BY EACH PARTY TO ENTER INTO THIS +AGREEMENT. + +TERM AND TERMINATION. The term of this Agreement will begin when the Software +is downloaded or accessed and shall continue until terminated pursuant to this +section. Either party may terminate this Agreement and the licenses granted +herein at any time. MagicBell may terminate this Agreement and the licenses +granted in this Agreement immediately if you breach any provision of this +Agreement, including but not limited to, if you are or become a competitor of +the MagicBell or make or sell any competitive products, in additional to all +other available remedies. Upon termination, all of your rights and licenses +under this Agreement cease to exist, including, for the avoidance of doubt, any +rights in any Modifications you may have developed; you must cease exercise of +the licensed rights herein; you must destroy or remove from all hard drives, +networks, and storage media, all copies and extracts of the Software and all +Modifications in your possession or control; and this sentence, all remedies for +breach, and Sections 2 through 10 and 12 through 14 shall survive any +termination of this Agreement. Any continued use of the Software by you or +attempt by you to exercise any rights under this Agreement after this Agreement +has been terminated will be considered copyright infringement and you will be +subject to all applicable remedies. + +FEEDBACK. You agree that MagicBell may collect or you may provide to MagicBell +comments, feedback, suggestions or other information related to the Software, or +modifications, corrections, improvements, derivatives and extensions to the +Software (collectively, "Feedback"), and you hereby grant MagicBell the +perpetual and irrevocable right (and the perpetual and irrevocable right to +permit others) to use and fully exercise and exploit the Feedback in any manner +to improve, develop and otherwise exploit applications, services or technology, +and otherwise in connection with its business during and after the term of this +Agreement. + +GOVERNMENT RESTRICTED RIGHTS. All software, technology, and accompanying +documentation are deemed to be "commercial computer software" and "commercial +computer software documentation," respectively, pursuant to DFAR Section +227.7202 and FAR Section 12.212, as applicable. Any use, modification, +reproduction, release, performance, display, transfer or disclosure of the +Software and accompanying documentation by any agency, department or other +entity of any government, shall be governed solely by the terms of this +Agreement and shall be prohibited except to the extent expressly permitted by +the terms herein or in a writing signed by an authorized signatory on behalf of +MagicBell. No other rights are granted. + +MISCELLANEOUS. You may not assign or transfer this Agreement, by operation of +law or otherwise, or any of your rights under this Agreement (including any +license rights granted to you), to any third party without MagicBell’s prior +written consent, which consent will not be unreasonably withheld or delayed. + +MagicBell may assign this Agreement, without consent, including, but not limited +to, affiliates or any successor to all or substantially all of its business or +assets to which this Agreement relates, whether by merger, sale of assets, sale +of stock, reorganization or otherwise. Any attempted assignment or transfer in +violation of the foregoing will be null and void. This Agreement contains the +complete agreement between you and MagicBell regarding the Software and +supersedes all prior agreements and representations between you and MagicBell +regarding the Software. This Agreement may only be amended and any provision +may only be waived by a writing executed by both parties. You agree to promptly +provide MagicBell with all information and documentation that MagicBell requests +to verify your compliance with this Agreement. If any provision of this +Agreement is held to be invalid or unenforceable, it shall be reformed to the +limited extent necessary to make it enforceable. This Agreement shall be +governed by and construed in accordance with the laws of California, without +regard to its conflicts of laws provisions. California will have exclusive +jurisdiction and venue under this Agreement. The United Nations Convention on +Contracts for the International Sale of Goods does not apply to this Agreement. diff --git a/packages/user-client/README.md b/packages/user-client/README.md new file mode 100644 index 000000000..38b144a6f --- /dev/null +++ b/packages/user-client/README.md @@ -0,0 +1,866 @@ +# Client Typescript SDK 0.1.0 + +The Typescript SDK for Client. + +- API version: 0.1.0 +- SDK version: 0.1.0 + +## Table of Contents + +- [About the API](#requirements) +- [Installation](#installation) +- [Authentication](#authentication) + - [Access Token](#access-token) +- [API Endpoint Services](#api-endpoint-services) +- [API Models](#api-models) +- [Sample Usage](#sample-usage) +- [Client Services](#client-services) +- [License](#license) + +## About the API + +OpenAPI 3.1.0 Specification for MagicBell API. + +## Installation + +```sh +npm install user-client +``` + +## Authentication + +To see whether an endpoint needs a specific type of authentication check the endpoint's documentation. + +### Access Token + +The Client API uses access tokens as a form of authentication. You can set the access token when initializing the SDK through the constructor: + +``` +const sdk = new Client('YOUR_ACCESS_TOKEN') +``` + +Or through the `setAccessToken` method: + +``` +const sdk = new Client() +sdk.setAccessToken('YOUR_ACCESS_TOKEN') +``` + +You can also set it for each service individually: + +``` +const sdk = new Client() +sdk.channels.setAccessToken('YOUR_ACCESS_TOKEN') +``` + +## Sample Usage + +Here is a simple program demonstrating usage of this SDK. It can also be found in the `examples/src/index.ts` file in this directory. + +When running the sample make sure to use `npm install` to install all the dependencies. + +```Typescript +import { Client } from '@magicbell/user-client'; + + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + try { + const result = await sdk.channels + .getMobilePushApnsTokens(); + console.log(result); + } catch (err) { + const error = err as Error; + console.error(error.message); + } +})(); + + +``` + +# Client Services + +A list of all services and services methods. + +- Services + + - [Channels](#channels) + + - [Integrations](#integrations) + +- [All Methods](#all-methods) + +## Channels + +| Method | Description | +| :-------------------------------------------------------- | :---------- | +| [saveMobilePushApnsToken](#savemobilepushapnstoken) | | +| [getMobilePushApnsTokens](#getmobilepushapnstokens) | | +| [getMobilePushApnsToken](#getmobilepushapnstoken) | | +| [discardMobilePushApnsToken](#discardmobilepushapnstoken) | | +| [saveMobilePushFcmToken](#savemobilepushfcmtoken) | | +| [getMobilePushFcmTokens](#getmobilepushfcmtokens) | | +| [getMobilePushFcmToken](#getmobilepushfcmtoken) | | +| [discardMobilePushFcmToken](#discardmobilepushfcmtoken) | | +| [saveSlackToken](#saveslacktoken) | | +| [getSlackTokens](#getslacktokens) | | +| [getSlackToken](#getslacktoken) | | +| [discardSlackToken](#discardslacktoken) | | +| [saveTeamsToken](#saveteamstoken) | | +| [getTeamsTokens](#getteamstokens) | | +| [getTeamsToken](#getteamstoken) | | +| [discardTeamsToken](#discardteamstoken) | | +| [saveWebPushToken](#savewebpushtoken) | | +| [getWebPushTokens](#getwebpushtokens) | | +| [getWebPushToken](#getwebpushtoken) | | +| [discardWebPushToken](#discardwebpushtoken) | | + +## Integrations + +| Method | Description | +| :------------------------------------------------------ | :---------- | +| [saveSlackInstallation](#saveslackinstallation) | | +| [finishSlackInstallation](#finishslackinstallation) | | +| [startSlackInstallation](#startslackinstallation) | | +| [saveTemplatesInstallation](#savetemplatesinstallation) | | +| [startWebPushInstallation](#startwebpushinstallation) | | + +## All Methods + +### **saveMobilePushApnsToken** + +- HTTP Method: POST +- Endpoint: /channels/mobile_push/apns/tokens + +**Required Parameters** + +| input | object | Request body. | + +**Return Type** + +ApnsToken + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const input = { + device_token: + 'eH0fLhuiRj2Np7UQ-opXAm:APA91bGtC-wH4sgW1jWkMKIZf7FYkm_RTQb7Jid7DfSJnCgivGYoRzhLrGxpcIF6yPjmbzAr6CKF-6phZkBasFUUfZmfdgcqfA_ZlZdVk6pSnon3LGzMumCzEJE0zgWoo_RUmVUVJUAt', + installation_id: 'development', + }; + const result = await sdk.channels.saveMobilePushApnsToken(input); + console.log(result); +})(); + +``` + +### **getMobilePushApnsTokens** + +- HTTP Method: GET +- Endpoint: /channels/mobile_push/apns/tokens + +**Return Type** + +ArrayWithMetadataOfApnsToken + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.getMobilePushApnsTokens(); + console.log(result); +})(); + +``` + +### **getMobilePushApnsToken** + +- HTTP Method: GET +- Endpoint: /channels/mobile_push/apns/tokens/{token_id} + +**Required Parameters** + +| Name | Type | Description | +| :------ | :----- | :---------- | +| tokenId | string | | + +**Return Type** + +ApnsTokenWithMetadata + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.getMobilePushApnsToken('token_id'); + console.log(result); +})(); + +``` + +### **discardMobilePushApnsToken** + +- HTTP Method: DELETE +- Endpoint: /channels/mobile_push/apns/tokens/{token_id} + +**Required Parameters** + +| Name | Type | Description | +| :------ | :----- | :---------- | +| tokenId | string | | + +**Return Type** + +DiscardResult + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.discardMobilePushApnsToken('token_id'); + console.log(result); +})(); + +``` + +### **saveMobilePushFcmToken** + +- HTTP Method: POST +- Endpoint: /channels/mobile_push/fcm/tokens + +**Required Parameters** + +| input | object | Request body. | + +**Return Type** + +FcmToken + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const input = { + device_token: + 'eH0fLhuiRj2Np7UQ-opXAm:APA91bGtC-wH4sgW1jWkMKIZf7FYkm_RTQb7Jid7DfSJnCgivGYoRzhLrGxpcIF6yPjmbzAr6CKF-6phZkBasFUUfZmfdgcqfA_ZlZdVk6pSnon3LGzMumCzEJE0zgWoo_RUmVUVJUAt', + installation_id: 'development', + }; + const result = await sdk.channels.saveMobilePushFcmToken(input); + console.log(result); +})(); + +``` + +### **getMobilePushFcmTokens** + +- HTTP Method: GET +- Endpoint: /channels/mobile_push/fcm/tokens + +**Return Type** + +ArrayWithMetadataOfFcmToken + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.getMobilePushFcmTokens(); + console.log(result); +})(); + +``` + +### **getMobilePushFcmToken** + +- HTTP Method: GET +- Endpoint: /channels/mobile_push/fcm/tokens/{token_id} + +**Required Parameters** + +| Name | Type | Description | +| :------ | :----- | :---------- | +| tokenId | string | | + +**Return Type** + +FcmTokenWithMetadata + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.getMobilePushFcmToken('token_id'); + console.log(result); +})(); + +``` + +### **discardMobilePushFcmToken** + +- HTTP Method: DELETE +- Endpoint: /channels/mobile_push/fcm/tokens/{token_id} + +**Required Parameters** + +| Name | Type | Description | +| :------ | :----- | :---------- | +| tokenId | string | | + +**Return Type** + +DiscardResult + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.discardMobilePushFcmToken('token_id'); + console.log(result); +})(); + +``` + +### **saveSlackToken** + +- HTTP Method: POST +- Endpoint: /channels/slack/tokens + +**Required Parameters** + +| input | object | Request body. | + +**Return Type** + +SlackToken + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const input = { webhook: { url: 'https://example.com/webhook' } }; + const result = await sdk.channels.saveSlackToken(input); + console.log(result); +})(); + +``` + +### **getSlackTokens** + +- HTTP Method: GET +- Endpoint: /channels/slack/tokens + +**Return Type** + +ArrayWithMetadataOfSlackToken + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.getSlackTokens(); + console.log(result); +})(); + +``` + +### **getSlackToken** + +- HTTP Method: GET +- Endpoint: /channels/slack/tokens/{token_id} + +**Required Parameters** + +| Name | Type | Description | +| :------ | :----- | :---------- | +| tokenId | string | | + +**Return Type** + +SlackTokenWithMetadata + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.getSlackToken('token_id'); + console.log(result); +})(); + +``` + +### **discardSlackToken** + +- HTTP Method: DELETE +- Endpoint: /channels/slack/tokens/{token_id} + +**Required Parameters** + +| Name | Type | Description | +| :------ | :----- | :---------- | +| tokenId | string | | + +**Return Type** + +DiscardResult + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.discardSlackToken('token_id'); + console.log(result); +})(); + +``` + +### **saveTeamsToken** + +- HTTP Method: POST +- Endpoint: /channels/teams/tokens + +**Required Parameters** + +| input | object | Request body. | + +**Return Type** + +Returns a dict object. + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const input = {}; + const result = await sdk.channels.saveTeamsToken(input); + console.log(result); +})(); + +``` + +### **getTeamsTokens** + +- HTTP Method: GET +- Endpoint: /channels/teams/tokens + +**Return Type** + +ArrayWithMetadataOfTeamsToken + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.getTeamsTokens(); + console.log(result); +})(); + +``` + +### **getTeamsToken** + +- HTTP Method: GET +- Endpoint: /channels/teams/tokens/{token_id} + +**Required Parameters** + +| Name | Type | Description | +| :------ | :----- | :---------- | +| tokenId | string | | + +**Return Type** + +TeamsTokenWithMetadata + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.getTeamsToken('token_id'); + console.log(result); +})(); + +``` + +### **discardTeamsToken** + +- HTTP Method: DELETE +- Endpoint: /channels/teams/tokens/{token_id} + +**Required Parameters** + +| Name | Type | Description | +| :------ | :----- | :---------- | +| tokenId | string | | + +**Return Type** + +DiscardResult + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.discardTeamsToken('token_id'); + console.log(result); +})(); + +``` + +### **saveWebPushToken** + +- HTTP Method: POST +- Endpoint: /channels/web_push/tokens + +**Required Parameters** + +| input | object | Request body. | + +**Return Type** + +WebPushToken + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const input = { + endpoint: 'https://example.com/webhook', + keys: { auth: '', p256dh: '' }, + }; + const result = await sdk.channels.saveWebPushToken(input); + console.log(result); +})(); + +``` + +### **getWebPushTokens** + +- HTTP Method: GET +- Endpoint: /channels/web_push/tokens + +**Return Type** + +ArrayWithMetadataOfWebPushToken + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.getWebPushTokens(); + console.log(result); +})(); + +``` + +### **getWebPushToken** + +- HTTP Method: GET +- Endpoint: /channels/web_push/tokens/{token_id} + +**Required Parameters** + +| Name | Type | Description | +| :------ | :----- | :---------- | +| tokenId | string | | + +**Return Type** + +WebPushTokenWithMetadata + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.getWebPushToken('token_id'); + console.log(result); +})(); + +``` + +### **discardWebPushToken** + +- HTTP Method: DELETE +- Endpoint: /channels/web_push/tokens/{token_id} + +**Required Parameters** + +| Name | Type | Description | +| :------ | :----- | :---------- | +| tokenId | string | | + +**Return Type** + +DiscardResult + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.discardWebPushToken('token_id'); + console.log(result); +})(); + +``` + +### **saveSlackInstallation** + +- HTTP Method: POST +- Endpoint: /integrations/slack/installations + +**Required Parameters** + +| input | object | Request body. | + +**Return Type** + +SlackInstallation + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const input = { + access_token: 'xoxb-123456789012-1234567890123-12345678901234567890abcdef123456', + app_id: 'A12345678', + authed_user: { scope: 'identify,commands' }, + bot_user_id: 'U12345678', + enterprise_id: 'E12345678', + enterprise_name: 'Enterprise Grid, Inc.', + incoming_webhook: { + channel: 'C12345678', + channel_id: 'C12345678', + configuration_url: 'https://teamname.slack.com/services/B12345678', + url: 'https://hooks.slack.com/services/T12345678/B12345678/123456789012345678901234', + }, + scope: 'identify,commands,bot', + team: { name: 'Team Installing Your App' }, + team_id: 'T12345678', + team_name: 'Team Installing Your App', + }; + const result = await sdk.integrations.saveSlackInstallation(input); + console.log(result); +})(); + +``` + +### **finishSlackInstallation** + +- HTTP Method: POST +- Endpoint: /integrations/slack/installations/finish + +**Required Parameters** + +| input | object | Request body. | + +**Return Type** + +SlackInstallation + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const input = { app_id: '12345678901', code: 'string', redirect_url: 'string' }; + const result = await sdk.integrations.finishSlackInstallation(input); + console.log(result); +})(); + +``` + +### **startSlackInstallation** + +- HTTP Method: POST +- Endpoint: /integrations/slack/installations/start + +**Required Parameters** + +| input | object | Request body. | + +**Return Type** + +Returns a dict object. + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const input = { + app_id: '12345678901', + auth_url: 'https://example.com/auth', + extra_scopes: ['scope1', 'scope2'], + redirect_url: 'https://example.com/redirect', + }; + const result = await sdk.integrations.startSlackInstallation(input); + console.log(result); +})(); + +``` + +### **saveTemplatesInstallation** + +- HTTP Method: POST +- Endpoint: /integrations/templates/installations + +**Required Parameters** + +| input | object | Request body. | + +**Return Type** + +TemplatesInstallation + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const input = { + apns: { + app_id: 'com.example.myapp', + badge: 'unread', + certificate: 'MIICXQIBAAKBgQC3J2', + key_id: 'ABCD1234EF', + team_id: 'ABCD1234EF', + }, + }; + const result = await sdk.integrations.saveTemplatesInstallation(input); + console.log(result); +})(); + +``` + +### **startWebPushInstallation** + +- HTTP Method: POST +- Endpoint: /integrations/web_push/installations/start + +**Required Parameters** + +| input | object | Request body. | + +**Return Type** + +WebPushStartInstallationResponse + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const input = { + imports: [], + originalName: 'start_web_push_installation_request', + services: ['integrations'], + filePath: 'src/services/integrations/models', + modelName: 'StartWebPushInstallationRequest', + title: 'StartWebPushInstallationRequest', + }; + const result = await sdk.integrations.startWebPushInstallation(input); + console.log(result); +})(); + +``` + +## License + +See license in LICENSE. diff --git a/packages/user-client/liblab.config.json b/packages/user-client/liblab.config.json new file mode 100644 index 000000000..fdd327bea --- /dev/null +++ b/packages/user-client/liblab.config.json @@ -0,0 +1,46 @@ +{ + "sdkName": "client", + "apiVersion": "2.0.0", + "apiName": "magicbell-api", + "specFilePath": "./magicbell-client.swagger.json", + "languages": ["typescript"], + "auth": ["bearer"], + "customizations": { + "includeOptionalSnippetParameters": true, + "authentication": { + "access": { + "prefix": "Bearer" + } + }, + "devContainer": false, + "generateEnv": true, + "inferServiceNames": false, + "injectedModels": [], + "license": { + "type": "CUSTOM", + "url": "https://raw.githubusercontent.com/magicbell/magicbell-js/main/LICENSE" + }, + "responseHeaders": false, + "retry": { + "enabled": true, + "maxAttempts": 3, + "retryDelay": 150 + } + }, + "languageOptions": { + "typescript": { + "bundle": false, + "exportClassDefault": false, + "httpClient": "axios", + "npmName": "user-client", + "npmOrg": "magicbell", + "githubRepoName": "magicbell-js", + "ignoreFiles": [], + "sdkVersion": "0.1.0", + "liblabVersion": "1" + } + }, + "publishing": { + "githubOrg": "magicbell" + } +} diff --git a/packages/user-client/package.json b/packages/user-client/package.json new file mode 100644 index 000000000..b27478135 --- /dev/null +++ b/packages/user-client/package.json @@ -0,0 +1,51 @@ +{ + "name": "@magicbell/user-client", + "version": "0.1.0", + "description": "Client - OpenAPI 3.1.0 Specification for MagicBell API.", + "repository": { + "type": "git", + "url": "https://github.com/magicbell/magicbell-js.git", + "directory": "packages/user-client" + }, + "license": "SEE LICENSE IN LICENSE", + "author": "Client", + "exports": { + "require": "./dist/commonjs/index.js", + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/esm/index.js" + }, + "main": "./dist/commonjs/index.js", + "unpkg": "./dist/index.umd.js", + "module": "./dist/esm/index.js", + "source": "./src/index.ts", + "browser": "./dist/index.umd.js", + "types": "./dist/commonjs/index.d.ts", + "files": [ + "dist", + "README.md" + ], + "scripts": { + "build": "run-s build:*", + "build:cjs": "tsc --project tsconfig.build.json --module commonjs --outDir dist/commonjs", + "build:esm": "tsc --project tsconfig.build.json --module esnext --outDir dist/esm", + "codegen": "tsx scripts/build.ts", + "lint": "eslint --ext .ts,.js ./src/ --resolve-plugins-relative-to .", + "lint:ci": "eslint --ext .ts,.js ./src/ --resolve-plugins-relative-to . --cache --quiet", + "lint:fix": "eslint --ext .ts,.js ./src/ --resolve-plugins-relative-to . --cache --fix", + "rebuild": "rm -rf dist/ && tsc", + "test": "jest --detectOpenHandles", + "version": "tsc --version", + "watch": "rm -rf dist/ && tsc -w" + }, + "dependencies": { + "axios": "^0.28.0" + }, + "devDependencies": { + "@types/jest": "^29.5.10", + "@types/node": "^17.0.23", + "jest": "^29.7.0", + "nock": "^13.2.4", + "ts-jest": "^29.2.0", + "typescript": "^4.9.5" + } +} diff --git a/packages/user-client/scripts/build.ts b/packages/user-client/scripts/build.ts new file mode 100644 index 000000000..e4e5e6e79 --- /dev/null +++ b/packages/user-client/scripts/build.ts @@ -0,0 +1,150 @@ +import { execSync } from 'node:child_process'; +import * as fs from 'node:fs/promises'; +import * as path from 'node:path'; +import { parseArgs } from 'node:util'; + +import { rimraf } from 'rimraf'; +import { sortPackageJson } from 'sort-package-json'; + +async function move(oldPath: string, newPath: string) { + await rimraf(newPath); + await fs.mkdir(path.dirname(newPath), { recursive: true }); + await fs.rename(oldPath, newPath); +} + +function deleteAdminPaths(schema: any) { + schema.tags = schema.tags.filter((x: any) => x.name !== 'admin' && x.name !== 'user'); + + for (const path of Object.keys(schema.paths)) { + for (const method of Object.keys(schema.paths[path])) { + const operation = schema.paths[path][method]; + if (!('tags' in operation) || !Array.isArray(operation.tags)) continue; + + // delete operation if it's not for users + if (operation.tags.includes('user')) { + operation.tags = operation.tags.filter((x: any) => x !== 'user'); + } else { + delete schema.paths[path][method]; + // console.log(`delete ${path}/${method}`) + + // delete path if no operations left + if (Object.keys(schema.paths[path]).length > 0) continue; + delete schema.paths[path]; + // console.log(`delete ${path}`); + } + } + } + + return schema; +} + +function findAllRefs(obj: any, refs = new Set()) { + if (typeof obj !== 'object' || obj === null) return refs; + + if (Array.isArray(obj)) { + obj.forEach((item) => findAllRefs(item, refs)); + } else { + for (const key in obj) { + if (key === '$ref' && typeof obj[key] === 'string') { + refs.add(obj[key]); + } else { + findAllRefs(obj[key], refs); + } + } + } + + return refs; +} + +function deleteUnreferencedComponents(swagger: any) { + let refs = findAllRefs(swagger); + const components = swagger.components && swagger.components.schemas; + let initialCount, currentCount; + + do { + initialCount = Object.keys(components || {}).length; + + if (components) { + for (const schema in components) { + const refString = `#/components/schemas/${schema}`; + if (!refs.has(refString)) { + delete components[schema]; + // console.log(`delete ${refString}`) + } + } + } + + refs = findAllRefs(swagger); // Recalculate references after deletion + currentCount = Object.keys(components || {}).length; + } while (initialCount !== currentCount); +} + +async function readFileOrUrl(path: string): Promise { + if (/^http?s:\/\//.test(path)) { + return fetch(path, { headers: { 'content-type': 'application/json' } }).then((x) => x.text()); + } + + return fs.readFile(path, { encoding: 'utf-8' }); +} + +const { values: args } = parseArgs({ + options: { + spec: { type: 'string', short: 's' }, + }, +}); + +async function build(specfile = 'https://public.magicbell.com/specs/swagger.json') { + const liblabConfig = JSON.parse(await fs.readFile('./liblab.config.json', { encoding: 'utf-8' })); + let swaggerJSON = await readFileOrUrl(specfile); + const spec = JSON.parse(swaggerJSON); + + deleteAdminPaths(spec); + deleteUnreferencedComponents(spec); + + swaggerJSON = JSON.stringify(spec, null, 2); + + await fs.writeFile(liblabConfig.specFilePath, swaggerJSON); + execSync(`npx -y liblab@latest build -y`, { stdio: 'inherit' }); + await rimraf(liblabConfig.specFilePath); + + await move('output/typescript/src', './src'); + // tests are currently ignored, as they're not stable between rebuilds, liblab is looking into this + // await move('output/typescript/test', './test'); + await move('output/typescript/package.json', './package.json'); + await move('output/typescript/README.md', './README.md'); + await rimraf('output'); + + // patch package.json + let pkgJson = JSON.parse(await fs.readFile('./package.json', { encoding: 'utf-8' })); + pkgJson.scripts.codegen = 'tsx scripts/build.ts'; + + pkgJson.scripts['build'] = 'run-s build:*'; + pkgJson.scripts['build:cjs'] = 'tsc --project tsconfig.build.json --module commonjs --outDir dist/commonjs'; + pkgJson.scripts['build:esm'] = 'tsc --project tsconfig.build.json --module esnext --outDir dist/esm'; + + delete pkgJson.scripts['build:umd']; + delete pkgJson.scripts['build:all']; + delete pkgJson.scripts['prepublishOnly']; + + for (const key of Object.keys(pkgJson.devDependencies)) { + if (/eslint|prettier/.test(key)) { + delete pkgJson.devDependencies[key]; + } + } + + pkgJson.repository = { + type: 'git', + url: 'https://github.com/magicbell/magicbell-js.git', + directory: 'packages/user-client', + }; + + pkgJson = sortPackageJson(pkgJson); + await fs.writeFile('./package.json', JSON.stringify(pkgJson, null, 2) + '\n'); + + execSync(`yarn --cwd ../.. eslint --fix .`, { stdio: 'inherit' }); + execSync(`yarn --cwd ../.. manypkg fix`, { stdio: 'inherit' }); + execSync(`yarn --cwd ../..`, { stdio: 'inherit' }); + execSync(`yarn build`, { stdio: 'inherit' }); +} + +build(args.spec); diff --git a/packages/user-client/src/BaseService.ts b/packages/user-client/src/BaseService.ts new file mode 100644 index 000000000..b612717fc --- /dev/null +++ b/packages/user-client/src/BaseService.ts @@ -0,0 +1,46 @@ +import { Environment } from './http/Environment'; +import { Headers } from './http/HTTPClient'; +import HTTPLibrary from './http/HTTPLibrary'; + +export default class BaseService { + public baseUrl: string = Environment.DEFAULT; + + public httpClient = new HTTPLibrary(); + + private accessToken = ''; + + private accessTokenPrefix = 'Bearer'; + + setAccessToken(accessToken: string): void { + this.accessToken = accessToken; + } + + getAuthorizationHeader(): Headers { + const accessTokenAuth = { Authorization: `${this.accessTokenPrefix} ${this.accessToken}` }; + + return { ...accessTokenAuth }; + } + + setBaseUrl(url: string): void { + this.baseUrl = url; + } + + constructor(accessToken = '') { + this.setAccessToken(accessToken); + } + + static patternMatching(value: string, pattern: string, variableName: string): string { + if (!value) { + throw new Error(`${variableName} cannot be null or undefined`); + } + if (!value.match(new RegExp(pattern))) { + throw new Error(`Invalid value for ${variableName}: must match ${pattern}`); + } + return value; + } + + static urlEncode = (input: { [key: string]: any }): string => + Object.keys(input) + .map((key) => `${key}=${encodeURIComponent(input[key])}`) + .join('&'); +} diff --git a/packages/user-client/src/hooks/Hook.ts b/packages/user-client/src/hooks/Hook.ts new file mode 100644 index 000000000..443030bed --- /dev/null +++ b/packages/user-client/src/hooks/Hook.ts @@ -0,0 +1,28 @@ +export interface Request { + method: string; + url: string; + input?: object; + headers: object; +} + +export interface Response { + data: object; + headers: object; + status: number; +} + +export interface Exception extends Error { + title: string; + type?: string; + detail?: string; + instance?: string; + statusCode: number; +} + +export interface Hook { + beforeRequest(request: Request): Promise; + + afterResponse(request: Request, response: Response): Promise; + + onError(error: Exception): Promise; +} diff --git a/packages/user-client/src/http/Environment.ts b/packages/user-client/src/http/Environment.ts new file mode 100644 index 000000000..836b28fba --- /dev/null +++ b/packages/user-client/src/http/Environment.ts @@ -0,0 +1,3 @@ +export enum Environment { + DEFAULT = 'https://api.magicbell.com/v2', +} diff --git a/packages/user-client/src/http/HTTPClient.ts b/packages/user-client/src/http/HTTPClient.ts new file mode 100644 index 000000000..2a37df8eb --- /dev/null +++ b/packages/user-client/src/http/HTTPClient.ts @@ -0,0 +1,12 @@ +export interface Headers extends Record {} + +/** + * Defines the basic operations for an HTTP client. + */ +export default interface HTTPClient { + get(url: string, input: any, headers: Headers, retry?: boolean): Promise; + post(url: string, input: any, headers: Headers, retry?: boolean): Promise; + delete(url: string, input: any, headers: Headers, retry?: boolean): Promise; + put(url: string, input: any, headers: Headers, retry?: boolean): Promise; + patch(url: string, input: any, headers: Headers, retry?: boolean): Promise; +} diff --git a/packages/user-client/src/http/HTTPLibrary.ts b/packages/user-client/src/http/HTTPLibrary.ts new file mode 100644 index 000000000..ce1dc81a0 --- /dev/null +++ b/packages/user-client/src/http/HTTPLibrary.ts @@ -0,0 +1,121 @@ +import axios, { AxiosError } from 'axios'; + +import HTTPClient, { Headers } from './HTTPClient'; +import throwHttpError from './httpExceptions'; + +// Ignore TS errors when checking if we are running inside Deno or Bun +declare const Deno: any; +declare const Bun: any; + +export default class HTTPLibrary implements HTTPClient { + readonly retryAttempts: number = 3; + readonly retryDelayMs: number = 150; + + async get(url: string, input: any, headers: Headers, retry = false): Promise { + const request = () => + axios.get(url, { + headers: { ...headers, ...this.getUserAgentHeader() }, + data: Object.keys(input).length > 0 ? input : undefined, + }); + + const response = retry ? await this.retry(this.retryAttempts, request, this.retryDelayMs) : await request(); + return HTTPLibrary.handleResponse(response); + } + + async post(url: string, input: any, headers: Headers, retry = false): Promise { + const request = () => + axios.post(url, input, { + headers: { ...headers, ...this.getUserAgentHeader() }, + }); + + const response = retry ? await this.retry(this.retryAttempts, request, this.retryDelayMs) : await request(); + + return HTTPLibrary.handleResponse(response); + } + + async delete(url: string, input: any, headers: Headers, retry = false): Promise { + const request = () => + axios.delete(url, { + headers: { ...headers, ...this.getUserAgentHeader() }, + data: input, + }); + + const response = retry ? await this.retry(this.retryAttempts, request, this.retryDelayMs) : await request(); + + return HTTPLibrary.handleResponse(response); + } + + async put(url: string, input: any, headers: Headers, retry = false): Promise { + const request = () => + axios.put(url, input, { + headers: { ...headers, ...this.getUserAgentHeader() }, + }); + + const response = retry ? await this.retry(this.retryAttempts, request, this.retryDelayMs) : await request(); + + return HTTPLibrary.handleResponse(response); + } + + async patch(url: string, input: any, headers: Headers, retry = false): Promise { + const request = () => + axios.patch(url, input, { + headers: { ...headers, ...this.getUserAgentHeader() }, + }); + + const response = retry ? await this.retry(this.retryAttempts, request, this.retryDelayMs) : await request(); + + return HTTPLibrary.handleResponse(response); + } + + async retry(retries: number, callbackFn: () => any, delay: number): Promise { + let result: any; + + try { + result = await callbackFn(); + } catch (e: any) { + if ((e as AxiosError).isAxiosError) { + if (e.response) { + if (![500, 503, 504].includes(e.response.status)) { + return e.response; + } + } + } + if (retries > 1) { + // eslint-disable-next-line no-promise-executor-return + await new Promise((resolve) => setTimeout(resolve, delay)); + result = await this.retry(retries - 1, callbackFn, delay * 2); + } else { + throw e; + } + } + + return result; + } + + private static handleResponse(response: any) { + if (response.status >= 400) { + throwHttpError(response); + } + + return response; + } + + private getUserAgentHeader(): Headers { + const userAgentBase = 'Client/0.1.0'; + + let userAgent = ''; + if (typeof window !== 'undefined') { + return {}; + } else if (typeof process !== 'undefined') { + userAgent = `Node.js/${process.version} ${userAgentBase}`; + } else if (typeof Deno !== 'undefined') { + userAgent = `Deno/${Deno.version.deno} ${userAgentBase}`; + } else if (typeof Bun !== 'undefined') { + userAgent = `Bun/${Bun.version} ${userAgentBase}`; + } else { + userAgent = userAgentBase; + } + + return { 'User-Agent': userAgent }; + } +} diff --git a/packages/user-client/src/http/QuerySerializer.ts b/packages/user-client/src/http/QuerySerializer.ts new file mode 100644 index 000000000..2cdb72f58 --- /dev/null +++ b/packages/user-client/src/http/QuerySerializer.ts @@ -0,0 +1,40 @@ +export type Explode = boolean; +export type QueryStyles = 'form' | 'spaceDelimited' | 'pipeDelimited' | 'deepObject'; +export type PathStyles = 'simple' | 'label' | 'matrix'; + +const styleMethods: Record = { + simple: (value: unknown, explode: boolean) => { + // Check if the value is an array + if (Array.isArray(value)) { + return explode ? value.join(',') : value.join(); + } + + // Check if the value is an object + if (typeof value === 'object' && value !== null) { + if (explode) { + // Serialize object with exploded format: "key=value,key2=value2" + return Object.entries(value) + .map(([parameterName, parameterValue]) => `${parameterName}=${parameterValue}`) + .join(','); + } + // Serialize object with non-exploded format: "key,value,key2,value2" + return Object.entries(value) + .flatMap(([parameterName, parameterValue]) => [parameterName, parameterValue]) + .join(','); + } + + // For primitive values + return String(value); + }, +}; + +export function serializePath(style: PathStyles, explode: Explode, value: unknown, key?: string): string { + const method = styleMethods[style]; + if (!method) return ''; + // The `simple` and `label` styles do not require a `key` + if (!key) { + return method(value, explode); + } else { + return method(key, value, explode); + } +} diff --git a/packages/user-client/src/http/errors/BadGateway.ts b/packages/user-client/src/http/errors/BadGateway.ts new file mode 100644 index 000000000..604658808 --- /dev/null +++ b/packages/user-client/src/http/errors/BadGateway.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class BadGateway extends BaseHTTPError { + statusCode = 502; + + title = 'Bad Gateway'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/BadRequest.ts b/packages/user-client/src/http/errors/BadRequest.ts new file mode 100644 index 000000000..cefa85bf4 --- /dev/null +++ b/packages/user-client/src/http/errors/BadRequest.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class BadRequest extends BaseHTTPError { + statusCode = 400; + + title = 'Bad Request'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/Conflict.ts b/packages/user-client/src/http/errors/Conflict.ts new file mode 100644 index 000000000..d11577ee4 --- /dev/null +++ b/packages/user-client/src/http/errors/Conflict.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class Conflict extends BaseHTTPError { + statusCode = 409; + + title = 'Conflict'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/ExpectationFailed.ts b/packages/user-client/src/http/errors/ExpectationFailed.ts new file mode 100644 index 000000000..69704d5cd --- /dev/null +++ b/packages/user-client/src/http/errors/ExpectationFailed.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class ExpectationFailed extends BaseHTTPError { + statusCode = 417; + + title = 'Expectation Failed'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/FailedDependency.ts b/packages/user-client/src/http/errors/FailedDependency.ts new file mode 100644 index 000000000..3e753de3f --- /dev/null +++ b/packages/user-client/src/http/errors/FailedDependency.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class FailedDependency extends BaseHTTPError { + statusCode = 424; + + title = 'Failed Dependency'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/Forbidden.ts b/packages/user-client/src/http/errors/Forbidden.ts new file mode 100644 index 000000000..a6172ca5b --- /dev/null +++ b/packages/user-client/src/http/errors/Forbidden.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class Forbidden extends BaseHTTPError { + statusCode = 403; + + title = 'Forbidden'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/GatewayTimeout.ts b/packages/user-client/src/http/errors/GatewayTimeout.ts new file mode 100644 index 000000000..74b358567 --- /dev/null +++ b/packages/user-client/src/http/errors/GatewayTimeout.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class GatewayTimeout extends BaseHTTPError { + statusCode = 504; + + title = 'Gateway Timeout'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/Gone.ts b/packages/user-client/src/http/errors/Gone.ts new file mode 100644 index 000000000..83ddffbc5 --- /dev/null +++ b/packages/user-client/src/http/errors/Gone.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class Gone extends BaseHTTPError { + statusCode = 410; + + title = 'Gone'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/HttpVersionNotSupported.ts b/packages/user-client/src/http/errors/HttpVersionNotSupported.ts new file mode 100644 index 000000000..7c9619456 --- /dev/null +++ b/packages/user-client/src/http/errors/HttpVersionNotSupported.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class HttpVersionNotSupported extends BaseHTTPError { + statusCode = 505; + + title = 'HTTP Version Not Supported'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/InternalServerError.ts b/packages/user-client/src/http/errors/InternalServerError.ts new file mode 100644 index 000000000..b93f1fd35 --- /dev/null +++ b/packages/user-client/src/http/errors/InternalServerError.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class InternalServerError extends BaseHTTPError { + statusCode = 500; + + title = 'Internal Server Error'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/LengthRequired.ts b/packages/user-client/src/http/errors/LengthRequired.ts new file mode 100644 index 000000000..12be92a99 --- /dev/null +++ b/packages/user-client/src/http/errors/LengthRequired.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class LengthRequired extends BaseHTTPError { + statusCode = 411; + + title = 'LengthRequired'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/Locked.ts b/packages/user-client/src/http/errors/Locked.ts new file mode 100644 index 000000000..da78abb28 --- /dev/null +++ b/packages/user-client/src/http/errors/Locked.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class Locked extends BaseHTTPError { + statusCode = 423; + + title = 'Locked'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/LoopDetected.ts b/packages/user-client/src/http/errors/LoopDetected.ts new file mode 100644 index 000000000..b62c383a4 --- /dev/null +++ b/packages/user-client/src/http/errors/LoopDetected.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class LoopDetected extends BaseHTTPError { + statusCode = 508; + + title = 'Loop Detected'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/MethodNotAllowed.ts b/packages/user-client/src/http/errors/MethodNotAllowed.ts new file mode 100644 index 000000000..fa86ff5e6 --- /dev/null +++ b/packages/user-client/src/http/errors/MethodNotAllowed.ts @@ -0,0 +1,14 @@ +import { BaseHTTPError } from './base'; + +export default class MethodNotAllowed extends BaseHTTPError { + statusCode = 405; + + title = 'Method Not Allowed'; + + allow?: string[]; + + constructor(detail = '', allow?: string[]) { + super(detail); + this.allow = allow; + } +} diff --git a/packages/user-client/src/http/errors/MisdirectedRequest.ts b/packages/user-client/src/http/errors/MisdirectedRequest.ts new file mode 100644 index 000000000..1ceb1d5d5 --- /dev/null +++ b/packages/user-client/src/http/errors/MisdirectedRequest.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class MisdirectedRequest extends BaseHTTPError { + statusCode = 421; + + title = 'Misdirected Request'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/NetworkAuthenticationRequired.ts b/packages/user-client/src/http/errors/NetworkAuthenticationRequired.ts new file mode 100644 index 000000000..26a962745 --- /dev/null +++ b/packages/user-client/src/http/errors/NetworkAuthenticationRequired.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class NetworkAuthenticationRequired extends BaseHTTPError { + statusCode = 511; + + title = 'Network Authentication Required'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/NotAcceptable.ts b/packages/user-client/src/http/errors/NotAcceptable.ts new file mode 100644 index 000000000..408df59a9 --- /dev/null +++ b/packages/user-client/src/http/errors/NotAcceptable.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class NotAcceptable extends BaseHTTPError { + statusCode = 406; + + title = 'Not Acceptable'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/NotExtended.ts b/packages/user-client/src/http/errors/NotExtended.ts new file mode 100644 index 000000000..ec3ddd852 --- /dev/null +++ b/packages/user-client/src/http/errors/NotExtended.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class NotExtended extends BaseHTTPError { + statusCode = 510; + + title = 'Not Extended'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/NotFound.ts b/packages/user-client/src/http/errors/NotFound.ts new file mode 100644 index 000000000..fdf9198e2 --- /dev/null +++ b/packages/user-client/src/http/errors/NotFound.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class NotFound extends BaseHTTPError { + statusCode = 404; + + title = 'Not Found'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/NotImplemented.ts b/packages/user-client/src/http/errors/NotImplemented.ts new file mode 100644 index 000000000..a286272d2 --- /dev/null +++ b/packages/user-client/src/http/errors/NotImplemented.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class NotImplemented extends BaseHTTPError { + statusCode = 501; + + title = 'Not Implemented'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/PayloadTooLarge.ts b/packages/user-client/src/http/errors/PayloadTooLarge.ts new file mode 100644 index 000000000..98ddc60e5 --- /dev/null +++ b/packages/user-client/src/http/errors/PayloadTooLarge.ts @@ -0,0 +1,14 @@ +import { BaseHTTPError } from './base'; + +export default class PayloadTooLarge extends BaseHTTPError { + statusCode = 413; + + title = 'Payload Too Large'; + + retryAfter: number | null; + + constructor(detail = '', retryAfter: number | null = null) { + super(detail); + this.retryAfter = retryAfter; + } +} diff --git a/packages/user-client/src/http/errors/PaymentRequired.ts b/packages/user-client/src/http/errors/PaymentRequired.ts new file mode 100644 index 000000000..8951b7b51 --- /dev/null +++ b/packages/user-client/src/http/errors/PaymentRequired.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class PaymentRequired extends BaseHTTPError { + statusCode = 402; + + title = 'Payment Required'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/PreconditionFailed.ts b/packages/user-client/src/http/errors/PreconditionFailed.ts new file mode 100644 index 000000000..c7bee9104 --- /dev/null +++ b/packages/user-client/src/http/errors/PreconditionFailed.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class PreconditionFailed extends BaseHTTPError { + statusCode = 412; + + title = 'PreconditionFailed'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/PreconditionRequired.ts b/packages/user-client/src/http/errors/PreconditionRequired.ts new file mode 100644 index 000000000..983d50523 --- /dev/null +++ b/packages/user-client/src/http/errors/PreconditionRequired.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class PreconditionRequired extends BaseHTTPError { + statusCode = 428; + + title = 'Precondition Required'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/ProxyAuthenticationRequired.ts b/packages/user-client/src/http/errors/ProxyAuthenticationRequired.ts new file mode 100644 index 000000000..814545fac --- /dev/null +++ b/packages/user-client/src/http/errors/ProxyAuthenticationRequired.ts @@ -0,0 +1,14 @@ +import { AuthenticateChallenge, BaseHTTPError } from './base'; + +export default class ProxyAuthenticationRequired extends BaseHTTPError { + statusCode = 407; + + title = 'Proxy Authentication Required'; + + proxyAuthenticate?: AuthenticateChallenge; + + constructor(detail = '', proxyAuthenticate?: AuthenticateChallenge) { + super(detail); + this.proxyAuthenticate = proxyAuthenticate; + } +} diff --git a/packages/user-client/src/http/errors/RangeNotSatisfiable.ts b/packages/user-client/src/http/errors/RangeNotSatisfiable.ts new file mode 100644 index 000000000..8cb2efc1a --- /dev/null +++ b/packages/user-client/src/http/errors/RangeNotSatisfiable.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class RangeNotSatisfiable extends BaseHTTPError { + statusCode = 416; + + title = 'Range Not Satisfiable'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/RequestHeaderFieldsTooLarge.ts b/packages/user-client/src/http/errors/RequestHeaderFieldsTooLarge.ts new file mode 100644 index 000000000..16000c75b --- /dev/null +++ b/packages/user-client/src/http/errors/RequestHeaderFieldsTooLarge.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class RequestHeaderFieldsTooLarge extends BaseHTTPError { + statusCode = 431; + + title = 'Request Header Fields Too Large'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/RequestTimeout.ts b/packages/user-client/src/http/errors/RequestTimeout.ts new file mode 100644 index 000000000..68949280c --- /dev/null +++ b/packages/user-client/src/http/errors/RequestTimeout.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class RequestTimeout extends BaseHTTPError { + statusCode = 408; + + title = 'Request Timeout'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/ServiceUnavailable.ts b/packages/user-client/src/http/errors/ServiceUnavailable.ts new file mode 100644 index 000000000..b7ce5b201 --- /dev/null +++ b/packages/user-client/src/http/errors/ServiceUnavailable.ts @@ -0,0 +1,14 @@ +import { BaseHTTPError } from './base'; + +export default class ServiceUnavailable extends BaseHTTPError { + statusCode = 503; + + title = 'Service Unavailable'; + + retryAfter: number | null; + + constructor(detail = '', retryAfter: number | null = null) { + super(detail); + this.retryAfter = retryAfter; + } +} diff --git a/packages/user-client/src/http/errors/TooEarly.ts b/packages/user-client/src/http/errors/TooEarly.ts new file mode 100644 index 000000000..c870167ae --- /dev/null +++ b/packages/user-client/src/http/errors/TooEarly.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class TooEarly extends BaseHTTPError { + statusCode = 425; + + title = 'Too Early'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/TooManyRequests.ts b/packages/user-client/src/http/errors/TooManyRequests.ts new file mode 100644 index 000000000..ec7f76055 --- /dev/null +++ b/packages/user-client/src/http/errors/TooManyRequests.ts @@ -0,0 +1,14 @@ +import { BaseHTTPError } from './base'; + +export default class TooManyRequests extends BaseHTTPError { + statusCode = 429; + + title = 'Too Many Requests'; + + retryAfter: number | null; + + constructor(detail = '', retryAfter: number | null = null) { + super(detail); + this.retryAfter = retryAfter; + } +} diff --git a/packages/user-client/src/http/errors/Unauthorized.ts b/packages/user-client/src/http/errors/Unauthorized.ts new file mode 100644 index 000000000..0edb7d0a3 --- /dev/null +++ b/packages/user-client/src/http/errors/Unauthorized.ts @@ -0,0 +1,14 @@ +import { AuthenticateChallenge, BaseHTTPError } from './base'; + +export default class Unauthorized extends BaseHTTPError { + statusCode = 401; + + title = 'Unauthorized'; + + wwwAuthenticate?: AuthenticateChallenge; + + constructor(detail = '', wwwAuthenticate?: AuthenticateChallenge) { + super(detail); + this.wwwAuthenticate = wwwAuthenticate; + } +} diff --git a/packages/user-client/src/http/errors/UnavailableForLegalReasons.ts b/packages/user-client/src/http/errors/UnavailableForLegalReasons.ts new file mode 100644 index 000000000..27aef0e04 --- /dev/null +++ b/packages/user-client/src/http/errors/UnavailableForLegalReasons.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class UnavailableForLegalReasons extends BaseHTTPError { + statusCode = 451; + + title = 'Unavailable For Legal Reasons'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/UnprocessableEntity.ts b/packages/user-client/src/http/errors/UnprocessableEntity.ts new file mode 100644 index 000000000..9b0713cd1 --- /dev/null +++ b/packages/user-client/src/http/errors/UnprocessableEntity.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class UnprocessableEntity extends BaseHTTPError { + statusCode = 422; + + title = 'Unprocessable Entity'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/UnsufficientStorage.ts b/packages/user-client/src/http/errors/UnsufficientStorage.ts new file mode 100644 index 000000000..07179cf6c --- /dev/null +++ b/packages/user-client/src/http/errors/UnsufficientStorage.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class UnsufficientStorage extends BaseHTTPError { + statusCode = 507; + + title = 'Unsufficient Storage'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/UnsupportedMediaType.ts b/packages/user-client/src/http/errors/UnsupportedMediaType.ts new file mode 100644 index 000000000..1c8777725 --- /dev/null +++ b/packages/user-client/src/http/errors/UnsupportedMediaType.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class UnsupportedMediaType extends BaseHTTPError { + statusCode = 415; + + title = 'Unsupported Media Type'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/UpgradeRequired.ts b/packages/user-client/src/http/errors/UpgradeRequired.ts new file mode 100644 index 000000000..dee381703 --- /dev/null +++ b/packages/user-client/src/http/errors/UpgradeRequired.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class UpgradeRequired extends BaseHTTPError { + statusCode = 426; + + title = 'Upgrade Required'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/UriTooLong.ts b/packages/user-client/src/http/errors/UriTooLong.ts new file mode 100644 index 000000000..ce46ea64c --- /dev/null +++ b/packages/user-client/src/http/errors/UriTooLong.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class UriTooLong extends BaseHTTPError { + statusCode = 414; + + title = 'URI Too Long'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/VariantAlsoNegotiates.ts b/packages/user-client/src/http/errors/VariantAlsoNegotiates.ts new file mode 100644 index 000000000..275e9528c --- /dev/null +++ b/packages/user-client/src/http/errors/VariantAlsoNegotiates.ts @@ -0,0 +1,11 @@ +import { BaseHTTPError } from './base'; + +export default class VariantAlsoNegotiates extends BaseHTTPError { + statusCode = 506; + + title = 'Variant Also Negotiates'; + + constructor(detail = '') { + super(detail); + } +} diff --git a/packages/user-client/src/http/errors/base.ts b/packages/user-client/src/http/errors/base.ts new file mode 100644 index 000000000..d41d9c30e --- /dev/null +++ b/packages/user-client/src/http/errors/base.ts @@ -0,0 +1,52 @@ +export interface IHTTPError extends Error { + statusCode: number; +} + +export interface IHTTPErrorDescription extends IHTTPError { + type?: string; + title: string; + detail?: string; + instance?: string; +} + +export function isHTTPError(error: unknown): error is IHTTPError { + if (!error) { + return false; + } + return Number.isInteger((error as IHTTPError).statusCode); +} + +export function isHTTPIssue(error: unknown): error is IHTTPErrorDescription { + if (!error) { + return false; + } + return (error as IHTTPErrorDescription).title !== undefined && isHTTPError(error); +} + +export class BaseHTTPError extends Error implements IHTTPError { + public type?: string; + + public title = 'Internal Server Error'; + + public detail?: string; + + public instance?: string; + + public statusCode = 500; + + constructor(detail = '') { + super(detail || 'An Unknown HTTP Error Occurred'); + this.detail = detail; + this.stack = (new Error()).stack; + } +} + +export function isClientError(error: Error): boolean { + return isHTTPError(error); +} + +export function isServerError(e: Error): boolean { + return isHTTPError(e) && e.statusCode >= 500 && e.statusCode <= 599; +} + +export type AuthenticateChallenge = string | string[]; diff --git a/packages/user-client/src/http/errors/index.ts b/packages/user-client/src/http/errors/index.ts new file mode 100644 index 000000000..22607d349 --- /dev/null +++ b/packages/user-client/src/http/errors/index.ts @@ -0,0 +1,83 @@ +import BadGateway from './BadGateway'; +import BadRequest from './BadRequest'; +import { BaseHTTPError } from './base'; +import Conflict from './Conflict'; +import ExpectationFailed from './ExpectationFailed'; +import FailedDependency from './FailedDependency'; +import Forbidden from './Forbidden'; +import GatewayTimeout from './GatewayTimeout'; +import Gone from './Gone'; +import HttpVersionNotSupported from './HttpVersionNotSupported'; +import InternalServerError from './InternalServerError'; +import LengthRequired from './LengthRequired'; +import Locked from './Locked'; +import LoopDetected from './LoopDetected'; +import MethodNotAllowed from './MethodNotAllowed'; +import MisdirectedRequest from './MisdirectedRequest'; +import NetworkAuthenticationRequired from './NetworkAuthenticationRequired'; +import NotAcceptable from './NotAcceptable'; +import NotExtended from './NotExtended'; +import NotFound from './NotFound'; +import NotImplemented from './NotImplemented'; +import PayloadTooLarge from './PayloadTooLarge'; +import PaymentRequired from './PaymentRequired'; +import PreconditionFailed from './PreconditionFailed'; +import PreconditionRequired from './PreconditionRequired'; +import ProxyAuthenticationRequired from './ProxyAuthenticationRequired'; +import RangeNotSatisfiable from './RangeNotSatisfiable'; +import RequestHeaderFieldsTooLarge from './RequestHeaderFieldsTooLarge'; +import RequestTimeout from './RequestTimeout'; +import ServiceUnavailable from './ServiceUnavailable'; +import TooEarly from './TooEarly'; +import TooManyRequests from './TooManyRequests'; +import Unauthorized from './Unauthorized'; +import UnavailableForLegalReasons from './UnavailableForLegalReasons'; +import UnprocessableEntity from './UnprocessableEntity'; +import UnsufficientStorage from './UnsufficientStorage'; +import UnsupportedMediaType from './UnsupportedMediaType'; +import UpgradeRequired from './UpgradeRequired'; +import UriTooLong from './UriTooLong'; +import VariantAlsoNegotiates from './VariantAlsoNegotiates'; + +export { + BadGateway, + BadRequest, + BaseHTTPError, + Conflict, + ExpectationFailed, + FailedDependency, + Forbidden, + GatewayTimeout, + Gone, + HttpVersionNotSupported, + InternalServerError, + LengthRequired, + Locked, + LoopDetected, + MethodNotAllowed, + MisdirectedRequest, + NetworkAuthenticationRequired, + NotAcceptable, + NotExtended, + NotFound, + NotImplemented, + PayloadTooLarge, + PaymentRequired, + PreconditionFailed, + PreconditionRequired, + ProxyAuthenticationRequired, + RangeNotSatisfiable, + RequestHeaderFieldsTooLarge, + RequestTimeout, + ServiceUnavailable, + TooEarly, + TooManyRequests, + Unauthorized, + UnavailableForLegalReasons, + UnprocessableEntity, + UnsufficientStorage, + UnsupportedMediaType, + UpgradeRequired, + UriTooLong, + VariantAlsoNegotiates, +}; diff --git a/packages/user-client/src/http/httpExceptions.ts b/packages/user-client/src/http/httpExceptions.ts new file mode 100644 index 000000000..d36bbdaed --- /dev/null +++ b/packages/user-client/src/http/httpExceptions.ts @@ -0,0 +1,135 @@ +import { + BadGateway, + BadRequest, + BaseHTTPError, + Conflict, + ExpectationFailed, + FailedDependency, + Forbidden, + GatewayTimeout, + Gone, + HttpVersionNotSupported, + InternalServerError, + LengthRequired, + Locked, + LoopDetected, + MethodNotAllowed, + MisdirectedRequest, + NetworkAuthenticationRequired, + NotAcceptable, + NotExtended, + NotFound, + NotImplemented, + PayloadTooLarge, + PaymentRequired, + PreconditionFailed, + PreconditionRequired, + ProxyAuthenticationRequired, + RangeNotSatisfiable, + RequestHeaderFieldsTooLarge, + RequestTimeout, + ServiceUnavailable, + TooEarly, + TooManyRequests, + Unauthorized, + UnavailableForLegalReasons, + UnprocessableEntity, + UnsufficientStorage, + UnsupportedMediaType, + UpgradeRequired, + UriTooLong, + VariantAlsoNegotiates, +} from './errors'; + +interface HttpResponseWithError { + status: number; + headers: any; + data?: any; +} + +interface NumberToClass { + [key: number]: any; +} + +const statusCodeToErrorFunction: NumberToClass = { + 400: BadRequest, + 401: Unauthorized, + 402: PaymentRequired, + 403: Forbidden, + 404: NotFound, + 405: MethodNotAllowed, + 406: NotAcceptable, + 407: ProxyAuthenticationRequired, + 408: RequestTimeout, + 409: Conflict, + 410: Gone, + 411: LengthRequired, + 412: PreconditionFailed, + 413: PayloadTooLarge, + 414: UriTooLong, + 415: UnsupportedMediaType, + 416: RangeNotSatisfiable, + 417: ExpectationFailed, + 421: MisdirectedRequest, + 422: UnprocessableEntity, + 423: Locked, + 424: FailedDependency, + 425: TooEarly, + 426: UpgradeRequired, + 428: PreconditionRequired, + 429: TooManyRequests, + 431: RequestHeaderFieldsTooLarge, + 451: UnavailableForLegalReasons, + 500: InternalServerError, + 501: NotImplemented, + 502: BadGateway, + 503: ServiceUnavailable, + 504: GatewayTimeout, + 505: HttpVersionNotSupported, + 506: VariantAlsoNegotiates, + 507: UnsufficientStorage, + 508: LoopDetected, + 510: NotExtended, + 511: NetworkAuthenticationRequired, +}; + +/** + * @summary This function will throw an error. + * + * @param {HttpResponseWithError} response - the response from a request, must contain a status and data fields + * @throws {Error} - an http error + */ +export default function throwHttpError(response: HttpResponseWithError): never { + let error: BaseHTTPError = new BaseHTTPError(response.data); + switch (response.status) { + case 401: + error = new Unauthorized(response.data, response.headers['WWW-Authenticate']); + break; + case 405: + // this indicates a bug in the spec if it allows a method that the server rejects + error = new MethodNotAllowed(response.data, response.headers.allowed); + break; + case 407: + error = new ProxyAuthenticationRequired(response.data, response.headers['Proxy-Authenticate']); + break; + case 413: + error = new PayloadTooLarge(response.data, response.headers['Retry-After']); + break; + case 429: + error = new TooManyRequests(response.data, response.headers['Retry-After']); + break; + case 503: + error = new ServiceUnavailable(response.data, response.headers['Retry-After']); + break; + default: + if (response.status in statusCodeToErrorFunction) { + error = new statusCodeToErrorFunction[response.status](response.data); + } else { + const error = new BaseHTTPError(response.data); + error.statusCode = response.status; + error.title = 'unknown error'; + } + } + + throw error; +} diff --git a/packages/user-client/src/index.ts b/packages/user-client/src/index.ts new file mode 100644 index 000000000..1bcfffe85 --- /dev/null +++ b/packages/user-client/src/index.ts @@ -0,0 +1,45 @@ +import { ChannelsService } from './services/channels/Channels'; +import { IntegrationsService } from './services/integrations/Integrations'; + +export * from './models'; +export * as ChannelsModels from './services/channels'; +export * as IntegrationsModels from './services/integrations'; + +type Config = { + accessToken?: string; +}; + +export * from './http/errors'; + +/** + * OpenAPI 3.1.0 Specification for MagicBell API. + */ +export class Client { + public channels: ChannelsService; + public integrations: IntegrationsService; + + constructor({ accessToken = '' }: Config) { + this.channels = new ChannelsService(accessToken); + this.integrations = new IntegrationsService(accessToken); + } + + /** + * Sets the baseUrl that the SDK will use for its requests. + * @param {string} url + */ + setBaseUrl(url: string): void { + this.channels.setBaseUrl(url); + this.integrations.setBaseUrl(url); + } + + /** + * Sets the access token used to authenticate. + * @param {string} accessToken + */ + setAccessToken(accessToken: string) { + this.channels.setAccessToken(accessToken); + this.integrations.setAccessToken(accessToken); + } +} + +// c029837e0e474b76bc487506e8799df5e3335891efe4fb02bda7a1441840310c diff --git a/packages/user-client/src/models.ts b/packages/user-client/src/models.ts new file mode 100644 index 000000000..35949a088 --- /dev/null +++ b/packages/user-client/src/models.ts @@ -0,0 +1,29 @@ +export type { ApnsToken } from './services/channels/models/ApnsToken'; +export type { ApnsTokenWithMetadata } from './services/channels/models/ApnsTokenWithMetadata'; +export type { ArrayWithMetadataOfApnsToken } from './services/channels/models/ArrayWithMetadataOfApnsToken'; +export type { ArrayWithMetadataOfFcmToken } from './services/channels/models/ArrayWithMetadataOfFcmToken'; +export type { ArrayWithMetadataOfSlackToken } from './services/channels/models/ArrayWithMetadataOfSlackToken'; +export type { ArrayWithMetadataOfTeamsToken } from './services/channels/models/ArrayWithMetadataOfTeamsToken'; +export type { ArrayWithMetadataOfWebPushToken } from './services/channels/models/ArrayWithMetadataOfWebPushToken'; +export type { DiscardResult } from './services/channels/models/DiscardResult'; +export type { FcmToken } from './services/channels/models/FcmToken'; +export type { FcmTokenWithMetadata } from './services/channels/models/FcmTokenWithMetadata'; +export type { SaveMobilePushApnsTokenRequest } from './services/channels/models/SaveMobilePushApnsTokenRequest'; +export type { SaveMobilePushFcmTokenRequest } from './services/channels/models/SaveMobilePushFcmTokenRequest'; +export type { SaveSlackTokenRequest } from './services/channels/models/SaveSlackTokenRequest'; +export type { SaveTeamsTokenRequest } from './services/channels/models/SaveTeamsTokenRequest'; +export type { SaveWebPushTokenRequest } from './services/channels/models/SaveWebPushTokenRequest'; +export type { SlackToken } from './services/channels/models/SlackToken'; +export type { SlackTokenWithMetadata } from './services/channels/models/SlackTokenWithMetadata'; +export type { TeamsToken } from './services/channels/models/TeamsToken'; +export type { TeamsTokenWithMetadata } from './services/channels/models/TeamsTokenWithMetadata'; +export type { WebPushToken } from './services/channels/models/WebPushToken'; +export type { WebPushTokenWithMetadata } from './services/channels/models/WebPushTokenWithMetadata'; +export type { FinishSlackInstallationRequest } from './services/integrations/models/FinishSlackInstallationRequest'; +export type { SaveSlackInstallationRequest } from './services/integrations/models/SaveSlackInstallationRequest'; +export type { SaveTemplatesInstallationRequest } from './services/integrations/models/SaveTemplatesInstallationRequest'; +export type { SlackInstallation } from './services/integrations/models/SlackInstallation'; +export type { StartSlackInstallationRequest } from './services/integrations/models/StartSlackInstallationRequest'; +export type { StartWebPushInstallationRequest } from './services/integrations/models/StartWebPushInstallationRequest'; +export type { TemplatesInstallation } from './services/integrations/models/TemplatesInstallation'; +export type { WebPushStartInstallationResponse } from './services/integrations/models/WebPushStartInstallationResponse'; diff --git a/packages/user-client/src/services/README.md b/packages/user-client/src/services/README.md new file mode 100644 index 000000000..1f7eda874 --- /dev/null +++ b/packages/user-client/src/services/README.md @@ -0,0 +1,781 @@ +# Client Services + +A list of all services and services methods. + +- Services + + - [Channels](#channels) + + - [Integrations](#integrations) + +- [All Methods](#all-methods) + +## Channels + +| Method | Description | +| :-------------------------------------------------------- | :---------- | +| [saveMobilePushApnsToken](#savemobilepushapnstoken) | | +| [getMobilePushApnsTokens](#getmobilepushapnstokens) | | +| [getMobilePushApnsToken](#getmobilepushapnstoken) | | +| [discardMobilePushApnsToken](#discardmobilepushapnstoken) | | +| [saveMobilePushFcmToken](#savemobilepushfcmtoken) | | +| [getMobilePushFcmTokens](#getmobilepushfcmtokens) | | +| [getMobilePushFcmToken](#getmobilepushfcmtoken) | | +| [discardMobilePushFcmToken](#discardmobilepushfcmtoken) | | +| [saveSlackToken](#saveslacktoken) | | +| [getSlackTokens](#getslacktokens) | | +| [getSlackToken](#getslacktoken) | | +| [discardSlackToken](#discardslacktoken) | | +| [saveTeamsToken](#saveteamstoken) | | +| [getTeamsTokens](#getteamstokens) | | +| [getTeamsToken](#getteamstoken) | | +| [discardTeamsToken](#discardteamstoken) | | +| [saveWebPushToken](#savewebpushtoken) | | +| [getWebPushTokens](#getwebpushtokens) | | +| [getWebPushToken](#getwebpushtoken) | | +| [discardWebPushToken](#discardwebpushtoken) | | + +## Integrations + +| Method | Description | +| :------------------------------------------------------ | :---------- | +| [saveSlackInstallation](#saveslackinstallation) | | +| [finishSlackInstallation](#finishslackinstallation) | | +| [startSlackInstallation](#startslackinstallation) | | +| [saveTemplatesInstallation](#savetemplatesinstallation) | | +| [startWebPushInstallation](#startwebpushinstallation) | | + +## All Methods + +### **saveMobilePushApnsToken** + +- HTTP Method: POST +- Endpoint: /channels/mobile_push/apns/tokens + +**Required Parameters** + +| input | object | Request body. | + +**Return Type** + +ApnsToken + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const input = { + device_token: + 'eH0fLhuiRj2Np7UQ-opXAm:APA91bGtC-wH4sgW1jWkMKIZf7FYkm_RTQb7Jid7DfSJnCgivGYoRzhLrGxpcIF6yPjmbzAr6CKF-6phZkBasFUUfZmfdgcqfA_ZlZdVk6pSnon3LGzMumCzEJE0zgWoo_RUmVUVJUAt', + installation_id: 'development', + }; + const result = await sdk.channels.saveMobilePushApnsToken(input); + console.log(result); +})(); + +``` + +### **getMobilePushApnsTokens** + +- HTTP Method: GET +- Endpoint: /channels/mobile_push/apns/tokens + +**Return Type** + +ArrayWithMetadataOfApnsToken + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.getMobilePushApnsTokens(); + console.log(result); +})(); + +``` + +### **getMobilePushApnsToken** + +- HTTP Method: GET +- Endpoint: /channels/mobile_push/apns/tokens/{token_id} + +**Required Parameters** + +| Name | Type | Description | +| :------ | :----- | :---------- | +| tokenId | string | | + +**Return Type** + +ApnsTokenWithMetadata + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.getMobilePushApnsToken('token_id'); + console.log(result); +})(); + +``` + +### **discardMobilePushApnsToken** + +- HTTP Method: DELETE +- Endpoint: /channels/mobile_push/apns/tokens/{token_id} + +**Required Parameters** + +| Name | Type | Description | +| :------ | :----- | :---------- | +| tokenId | string | | + +**Return Type** + +DiscardResult + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.discardMobilePushApnsToken('token_id'); + console.log(result); +})(); + +``` + +### **saveMobilePushFcmToken** + +- HTTP Method: POST +- Endpoint: /channels/mobile_push/fcm/tokens + +**Required Parameters** + +| input | object | Request body. | + +**Return Type** + +FcmToken + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const input = { + device_token: + 'eH0fLhuiRj2Np7UQ-opXAm:APA91bGtC-wH4sgW1jWkMKIZf7FYkm_RTQb7Jid7DfSJnCgivGYoRzhLrGxpcIF6yPjmbzAr6CKF-6phZkBasFUUfZmfdgcqfA_ZlZdVk6pSnon3LGzMumCzEJE0zgWoo_RUmVUVJUAt', + installation_id: 'development', + }; + const result = await sdk.channels.saveMobilePushFcmToken(input); + console.log(result); +})(); + +``` + +### **getMobilePushFcmTokens** + +- HTTP Method: GET +- Endpoint: /channels/mobile_push/fcm/tokens + +**Return Type** + +ArrayWithMetadataOfFcmToken + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.getMobilePushFcmTokens(); + console.log(result); +})(); + +``` + +### **getMobilePushFcmToken** + +- HTTP Method: GET +- Endpoint: /channels/mobile_push/fcm/tokens/{token_id} + +**Required Parameters** + +| Name | Type | Description | +| :------ | :----- | :---------- | +| tokenId | string | | + +**Return Type** + +FcmTokenWithMetadata + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.getMobilePushFcmToken('token_id'); + console.log(result); +})(); + +``` + +### **discardMobilePushFcmToken** + +- HTTP Method: DELETE +- Endpoint: /channels/mobile_push/fcm/tokens/{token_id} + +**Required Parameters** + +| Name | Type | Description | +| :------ | :----- | :---------- | +| tokenId | string | | + +**Return Type** + +DiscardResult + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.discardMobilePushFcmToken('token_id'); + console.log(result); +})(); + +``` + +### **saveSlackToken** + +- HTTP Method: POST +- Endpoint: /channels/slack/tokens + +**Required Parameters** + +| input | object | Request body. | + +**Return Type** + +SlackToken + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const input = { webhook: { url: 'https://example.com/webhook' } }; + const result = await sdk.channels.saveSlackToken(input); + console.log(result); +})(); + +``` + +### **getSlackTokens** + +- HTTP Method: GET +- Endpoint: /channels/slack/tokens + +**Return Type** + +ArrayWithMetadataOfSlackToken + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.getSlackTokens(); + console.log(result); +})(); + +``` + +### **getSlackToken** + +- HTTP Method: GET +- Endpoint: /channels/slack/tokens/{token_id} + +**Required Parameters** + +| Name | Type | Description | +| :------ | :----- | :---------- | +| tokenId | string | | + +**Return Type** + +SlackTokenWithMetadata + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.getSlackToken('token_id'); + console.log(result); +})(); + +``` + +### **discardSlackToken** + +- HTTP Method: DELETE +- Endpoint: /channels/slack/tokens/{token_id} + +**Required Parameters** + +| Name | Type | Description | +| :------ | :----- | :---------- | +| tokenId | string | | + +**Return Type** + +DiscardResult + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.discardSlackToken('token_id'); + console.log(result); +})(); + +``` + +### **saveTeamsToken** + +- HTTP Method: POST +- Endpoint: /channels/teams/tokens + +**Required Parameters** + +| input | object | Request body. | + +**Return Type** + +Returns a dict object. + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const input = {}; + const result = await sdk.channels.saveTeamsToken(input); + console.log(result); +})(); + +``` + +### **getTeamsTokens** + +- HTTP Method: GET +- Endpoint: /channels/teams/tokens + +**Return Type** + +ArrayWithMetadataOfTeamsToken + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.getTeamsTokens(); + console.log(result); +})(); + +``` + +### **getTeamsToken** + +- HTTP Method: GET +- Endpoint: /channels/teams/tokens/{token_id} + +**Required Parameters** + +| Name | Type | Description | +| :------ | :----- | :---------- | +| tokenId | string | | + +**Return Type** + +TeamsTokenWithMetadata + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.getTeamsToken('token_id'); + console.log(result); +})(); + +``` + +### **discardTeamsToken** + +- HTTP Method: DELETE +- Endpoint: /channels/teams/tokens/{token_id} + +**Required Parameters** + +| Name | Type | Description | +| :------ | :----- | :---------- | +| tokenId | string | | + +**Return Type** + +DiscardResult + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.discardTeamsToken('token_id'); + console.log(result); +})(); + +``` + +### **saveWebPushToken** + +- HTTP Method: POST +- Endpoint: /channels/web_push/tokens + +**Required Parameters** + +| input | object | Request body. | + +**Return Type** + +WebPushToken + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const input = { + endpoint: 'https://example.com/webhook', + keys: { auth: '', p256dh: '' }, + }; + const result = await sdk.channels.saveWebPushToken(input); + console.log(result); +})(); + +``` + +### **getWebPushTokens** + +- HTTP Method: GET +- Endpoint: /channels/web_push/tokens + +**Return Type** + +ArrayWithMetadataOfWebPushToken + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.getWebPushTokens(); + console.log(result); +})(); + +``` + +### **getWebPushToken** + +- HTTP Method: GET +- Endpoint: /channels/web_push/tokens/{token_id} + +**Required Parameters** + +| Name | Type | Description | +| :------ | :----- | :---------- | +| tokenId | string | | + +**Return Type** + +WebPushTokenWithMetadata + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.getWebPushToken('token_id'); + console.log(result); +})(); + +``` + +### **discardWebPushToken** + +- HTTP Method: DELETE +- Endpoint: /channels/web_push/tokens/{token_id} + +**Required Parameters** + +| Name | Type | Description | +| :------ | :----- | :---------- | +| tokenId | string | | + +**Return Type** + +DiscardResult + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const result = await sdk.channels.discardWebPushToken('token_id'); + console.log(result); +})(); + +``` + +### **saveSlackInstallation** + +- HTTP Method: POST +- Endpoint: /integrations/slack/installations + +**Required Parameters** + +| input | object | Request body. | + +**Return Type** + +SlackInstallation + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const input = { + access_token: 'xoxb-123456789012-1234567890123-12345678901234567890abcdef123456', + app_id: 'A12345678', + authed_user: { scope: 'identify,commands' }, + bot_user_id: 'U12345678', + enterprise_id: 'E12345678', + enterprise_name: 'Enterprise Grid, Inc.', + incoming_webhook: { + channel: 'C12345678', + channel_id: 'C12345678', + configuration_url: 'https://teamname.slack.com/services/B12345678', + url: 'https://hooks.slack.com/services/T12345678/B12345678/123456789012345678901234', + }, + scope: 'identify,commands,bot', + team: { name: 'Team Installing Your App' }, + team_id: 'T12345678', + team_name: 'Team Installing Your App', + }; + const result = await sdk.integrations.saveSlackInstallation(input); + console.log(result); +})(); + +``` + +### **finishSlackInstallation** + +- HTTP Method: POST +- Endpoint: /integrations/slack/installations/finish + +**Required Parameters** + +| input | object | Request body. | + +**Return Type** + +SlackInstallation + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const input = { app_id: '12345678901', code: 'string', redirect_url: 'string' }; + const result = await sdk.integrations.finishSlackInstallation(input); + console.log(result); +})(); + +``` + +### **startSlackInstallation** + +- HTTP Method: POST +- Endpoint: /integrations/slack/installations/start + +**Required Parameters** + +| input | object | Request body. | + +**Return Type** + +Returns a dict object. + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const input = { + app_id: '12345678901', + auth_url: 'https://example.com/auth', + extra_scopes: ['scope1', 'scope2'], + redirect_url: 'https://example.com/redirect', + }; + const result = await sdk.integrations.startSlackInstallation(input); + console.log(result); +})(); + +``` + +### **saveTemplatesInstallation** + +- HTTP Method: POST +- Endpoint: /integrations/templates/installations + +**Required Parameters** + +| input | object | Request body. | + +**Return Type** + +TemplatesInstallation + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const input = { + apns: { + app_id: 'com.example.myapp', + badge: 'unread', + certificate: 'MIICXQIBAAKBgQC3J2', + key_id: 'ABCD1234EF', + team_id: 'ABCD1234EF', + }, + }; + const result = await sdk.integrations.saveTemplatesInstallation(input); + console.log(result); +})(); + +``` + +### **startWebPushInstallation** + +- HTTP Method: POST +- Endpoint: /integrations/web_push/installations/start + +**Required Parameters** + +| input | object | Request body. | + +**Return Type** + +WebPushStartInstallationResponse + +**Example Usage Code Snippet** + +```Typescript +import { Client } from '@magicbell/user-client'; + +const sdk = new Client({ accessToken: process.env.CLIENT_ACCESS_TOKEN }); + +(async () => { + const input = { + imports: [], + originalName: 'start_web_push_installation_request', + services: ['integrations'], + filePath: 'src/services/integrations/models', + modelName: 'StartWebPushInstallationRequest', + title: 'StartWebPushInstallationRequest', + }; + const result = await sdk.integrations.startWebPushInstallation(input); + console.log(result); +})(); + +``` diff --git a/packages/user-client/src/services/channels/Channels.ts b/packages/user-client/src/services/channels/Channels.ts new file mode 100644 index 000000000..75cf62600 --- /dev/null +++ b/packages/user-client/src/services/channels/Channels.ts @@ -0,0 +1,374 @@ +import BaseService from '../../BaseService'; +import { serializePath } from '../../http/QuerySerializer'; +import { ApnsToken } from './models/ApnsToken'; +import { ApnsTokenWithMetadata } from './models/ApnsTokenWithMetadata'; +import { ArrayWithMetadataOfApnsToken } from './models/ArrayWithMetadataOfApnsToken'; +import { ArrayWithMetadataOfFcmToken } from './models/ArrayWithMetadataOfFcmToken'; +import { ArrayWithMetadataOfSlackToken } from './models/ArrayWithMetadataOfSlackToken'; +import { ArrayWithMetadataOfTeamsToken } from './models/ArrayWithMetadataOfTeamsToken'; +import { ArrayWithMetadataOfWebPushToken } from './models/ArrayWithMetadataOfWebPushToken'; +import { DiscardResult } from './models/DiscardResult'; +import { FcmToken } from './models/FcmToken'; +import { FcmTokenWithMetadata } from './models/FcmTokenWithMetadata'; +import { SaveMobilePushApnsTokenRequest } from './models/SaveMobilePushApnsTokenRequest'; +import { SaveMobilePushFcmTokenRequest } from './models/SaveMobilePushFcmTokenRequest'; +import { SaveSlackTokenRequest } from './models/SaveSlackTokenRequest'; +import { SaveTeamsTokenRequest } from './models/SaveTeamsTokenRequest'; +import { SaveWebPushTokenRequest } from './models/SaveWebPushTokenRequest'; +import { SlackToken } from './models/SlackToken'; +import { SlackTokenWithMetadata } from './models/SlackTokenWithMetadata'; +import { TeamsTokenWithMetadata } from './models/TeamsTokenWithMetadata'; +import { WebPushToken } from './models/WebPushToken'; +import { WebPushTokenWithMetadata } from './models/WebPushTokenWithMetadata'; + +export class ChannelsService extends BaseService { + async getMobilePushApnsTokens(): Promise { + const urlEndpoint = '/channels/mobile_push/apns/tokens'; + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.get( + finalUrl, + {}, + { + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as ArrayWithMetadataOfApnsToken; + return responseModel; + } + + async saveMobilePushApnsToken(input: SaveMobilePushApnsTokenRequest): Promise { + const headers: { [key: string]: string } = { 'Content-Type': 'application/json' }; + const urlEndpoint = '/channels/mobile_push/apns/tokens'; + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.post( + finalUrl, + input, + { + ...headers, + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as ApnsToken; + return responseModel; + } + + async getMobilePushApnsToken(tokenId: string): Promise { + if (tokenId === undefined) { + throw new Error('The following parameter is required: tokenId, cannot be empty or blank'); + } + let urlEndpoint = '/channels/mobile_push/apns/tokens/{token_id}'; + urlEndpoint = urlEndpoint.replace('{token_id}', serializePath('simple', false, tokenId, undefined)); + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.get( + finalUrl, + {}, + { + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as ApnsTokenWithMetadata; + return responseModel; + } + + async discardMobilePushApnsToken(tokenId: string): Promise { + if (tokenId === undefined) { + throw new Error('The following parameter is required: tokenId, cannot be empty or blank'); + } + let urlEndpoint = '/channels/mobile_push/apns/tokens/{token_id}'; + urlEndpoint = urlEndpoint.replace('{token_id}', serializePath('simple', false, tokenId, undefined)); + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.delete( + finalUrl, + {}, + { + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as DiscardResult; + return responseModel; + } + + async getMobilePushFcmTokens(): Promise { + const urlEndpoint = '/channels/mobile_push/fcm/tokens'; + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.get( + finalUrl, + {}, + { + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as ArrayWithMetadataOfFcmToken; + return responseModel; + } + + async saveMobilePushFcmToken(input: SaveMobilePushFcmTokenRequest): Promise { + const headers: { [key: string]: string } = { 'Content-Type': 'application/json' }; + const urlEndpoint = '/channels/mobile_push/fcm/tokens'; + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.post( + finalUrl, + input, + { + ...headers, + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as FcmToken; + return responseModel; + } + + async getMobilePushFcmToken(tokenId: string): Promise { + if (tokenId === undefined) { + throw new Error('The following parameter is required: tokenId, cannot be empty or blank'); + } + let urlEndpoint = '/channels/mobile_push/fcm/tokens/{token_id}'; + urlEndpoint = urlEndpoint.replace('{token_id}', serializePath('simple', false, tokenId, undefined)); + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.get( + finalUrl, + {}, + { + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as FcmTokenWithMetadata; + return responseModel; + } + + async discardMobilePushFcmToken(tokenId: string): Promise { + if (tokenId === undefined) { + throw new Error('The following parameter is required: tokenId, cannot be empty or blank'); + } + let urlEndpoint = '/channels/mobile_push/fcm/tokens/{token_id}'; + urlEndpoint = urlEndpoint.replace('{token_id}', serializePath('simple', false, tokenId, undefined)); + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.delete( + finalUrl, + {}, + { + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as DiscardResult; + return responseModel; + } + + async getSlackTokens(): Promise { + const urlEndpoint = '/channels/slack/tokens'; + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.get( + finalUrl, + {}, + { + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as ArrayWithMetadataOfSlackToken; + return responseModel; + } + + async saveSlackToken(input: SaveSlackTokenRequest): Promise { + const headers: { [key: string]: string } = { 'Content-Type': 'application/json' }; + const urlEndpoint = '/channels/slack/tokens'; + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.post( + finalUrl, + input, + { + ...headers, + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as SlackToken; + return responseModel; + } + + async getSlackToken(tokenId: string): Promise { + if (tokenId === undefined) { + throw new Error('The following parameter is required: tokenId, cannot be empty or blank'); + } + let urlEndpoint = '/channels/slack/tokens/{token_id}'; + urlEndpoint = urlEndpoint.replace('{token_id}', serializePath('simple', false, tokenId, undefined)); + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.get( + finalUrl, + {}, + { + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as SlackTokenWithMetadata; + return responseModel; + } + + async discardSlackToken(tokenId: string): Promise { + if (tokenId === undefined) { + throw new Error('The following parameter is required: tokenId, cannot be empty or blank'); + } + let urlEndpoint = '/channels/slack/tokens/{token_id}'; + urlEndpoint = urlEndpoint.replace('{token_id}', serializePath('simple', false, tokenId, undefined)); + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.delete( + finalUrl, + {}, + { + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as DiscardResult; + return responseModel; + } + + async getTeamsTokens(): Promise { + const urlEndpoint = '/channels/teams/tokens'; + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.get( + finalUrl, + {}, + { + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as ArrayWithMetadataOfTeamsToken; + return responseModel; + } + + async saveTeamsToken(input: SaveTeamsTokenRequest): Promise { + const headers: { [key: string]: string } = { 'Content-Type': 'application/json' }; + const urlEndpoint = '/channels/teams/tokens'; + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.post( + finalUrl, + input, + { + ...headers, + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data; + return responseModel; + } + + async getTeamsToken(tokenId: string): Promise { + if (tokenId === undefined) { + throw new Error('The following parameter is required: tokenId, cannot be empty or blank'); + } + let urlEndpoint = '/channels/teams/tokens/{token_id}'; + urlEndpoint = urlEndpoint.replace('{token_id}', serializePath('simple', false, tokenId, undefined)); + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.get( + finalUrl, + {}, + { + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as TeamsTokenWithMetadata; + return responseModel; + } + + async discardTeamsToken(tokenId: string): Promise { + if (tokenId === undefined) { + throw new Error('The following parameter is required: tokenId, cannot be empty or blank'); + } + let urlEndpoint = '/channels/teams/tokens/{token_id}'; + urlEndpoint = urlEndpoint.replace('{token_id}', serializePath('simple', false, tokenId, undefined)); + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.delete( + finalUrl, + {}, + { + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as DiscardResult; + return responseModel; + } + + async getWebPushTokens(): Promise { + const urlEndpoint = '/channels/web_push/tokens'; + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.get( + finalUrl, + {}, + { + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as ArrayWithMetadataOfWebPushToken; + return responseModel; + } + + async saveWebPushToken(input: SaveWebPushTokenRequest): Promise { + const headers: { [key: string]: string } = { 'Content-Type': 'application/json' }; + const urlEndpoint = '/channels/web_push/tokens'; + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.post( + finalUrl, + input, + { + ...headers, + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as WebPushToken; + return responseModel; + } + + async getWebPushToken(tokenId: string): Promise { + if (tokenId === undefined) { + throw new Error('The following parameter is required: tokenId, cannot be empty or blank'); + } + let urlEndpoint = '/channels/web_push/tokens/{token_id}'; + urlEndpoint = urlEndpoint.replace('{token_id}', serializePath('simple', false, tokenId, undefined)); + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.get( + finalUrl, + {}, + { + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as WebPushTokenWithMetadata; + return responseModel; + } + + async discardWebPushToken(tokenId: string): Promise { + if (tokenId === undefined) { + throw new Error('The following parameter is required: tokenId, cannot be empty or blank'); + } + let urlEndpoint = '/channels/web_push/tokens/{token_id}'; + urlEndpoint = urlEndpoint.replace('{token_id}', serializePath('simple', false, tokenId, undefined)); + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.delete( + finalUrl, + {}, + { + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as DiscardResult; + return responseModel; + } +} diff --git a/packages/user-client/src/services/channels/index.ts b/packages/user-client/src/services/channels/index.ts new file mode 100644 index 000000000..4afbdb880 --- /dev/null +++ b/packages/user-client/src/services/channels/index.ts @@ -0,0 +1,20 @@ +export type { ApnsToken } from './models/ApnsToken'; +export type { ApnsTokenWithMetadata } from './models/ApnsTokenWithMetadata'; +export type { ArrayWithMetadataOfApnsToken } from './models/ArrayWithMetadataOfApnsToken'; +export type { ArrayWithMetadataOfFcmToken } from './models/ArrayWithMetadataOfFcmToken'; +export type { ArrayWithMetadataOfSlackToken } from './models/ArrayWithMetadataOfSlackToken'; +export type { ArrayWithMetadataOfTeamsToken } from './models/ArrayWithMetadataOfTeamsToken'; +export type { ArrayWithMetadataOfWebPushToken } from './models/ArrayWithMetadataOfWebPushToken'; +export type { DiscardResult } from './models/DiscardResult'; +export type { FcmToken } from './models/FcmToken'; +export type { FcmTokenWithMetadata } from './models/FcmTokenWithMetadata'; +export type { SaveMobilePushApnsTokenRequest } from './models/SaveMobilePushApnsTokenRequest'; +export type { SaveMobilePushFcmTokenRequest } from './models/SaveMobilePushFcmTokenRequest'; +export type { SaveSlackTokenRequest } from './models/SaveSlackTokenRequest'; +export type { SaveTeamsTokenRequest } from './models/SaveTeamsTokenRequest'; +export type { SaveWebPushTokenRequest } from './models/SaveWebPushTokenRequest'; +export type { SlackToken } from './models/SlackToken'; +export type { SlackTokenWithMetadata } from './models/SlackTokenWithMetadata'; +export type { TeamsTokenWithMetadata } from './models/TeamsTokenWithMetadata'; +export type { WebPushToken } from './models/WebPushToken'; +export type { WebPushTokenWithMetadata } from './models/WebPushTokenWithMetadata'; diff --git a/packages/user-client/src/services/channels/models/ApnsToken.ts b/packages/user-client/src/services/channels/models/ApnsToken.ts new file mode 100644 index 000000000..b09a7ba29 --- /dev/null +++ b/packages/user-client/src/services/channels/models/ApnsToken.ts @@ -0,0 +1,6 @@ +type InstallationId = 'development' | 'production'; + +export interface ApnsToken { + device_token: string; + installation_id?: InstallationId; +} diff --git a/packages/user-client/src/services/channels/models/ApnsTokenWithMetadata.ts b/packages/user-client/src/services/channels/models/ApnsTokenWithMetadata.ts new file mode 100644 index 000000000..b59481de9 --- /dev/null +++ b/packages/user-client/src/services/channels/models/ApnsTokenWithMetadata.ts @@ -0,0 +1,14 @@ +import { ApnsToken } from './ApnsToken'; + +type Data = ApnsToken; + +export interface ApnsTokenWithMetadata { + data: Data; + metadata: Metadata; +} +interface Metadata { + created_at: string; + discarded_at?: string; + id: string; + updated_at?: string; +} diff --git a/packages/user-client/src/services/channels/models/ArrayWithMetadataOfApnsToken.ts b/packages/user-client/src/services/channels/models/ArrayWithMetadataOfApnsToken.ts new file mode 100644 index 000000000..63f550037 --- /dev/null +++ b/packages/user-client/src/services/channels/models/ArrayWithMetadataOfApnsToken.ts @@ -0,0 +1,16 @@ +import { ApnsToken } from './ApnsToken'; + +type Data = ApnsToken; + +export interface ArrayWithMetadataOfApnsToken { + data: { + data: Data; + metadata: Metadata; + }[]; +} +interface Metadata { + created_at: string; + discarded_at?: string; + id: string; + updated_at?: string; +} diff --git a/packages/user-client/src/services/channels/models/ArrayWithMetadataOfFcmToken.ts b/packages/user-client/src/services/channels/models/ArrayWithMetadataOfFcmToken.ts new file mode 100644 index 000000000..8645e33a2 --- /dev/null +++ b/packages/user-client/src/services/channels/models/ArrayWithMetadataOfFcmToken.ts @@ -0,0 +1,16 @@ +import { FcmToken } from './FcmToken'; + +type Data = FcmToken; + +export interface ArrayWithMetadataOfFcmToken { + data: { + data: Data; + metadata: Metadata; + }[]; +} +interface Metadata { + created_at: string; + discarded_at?: string; + id: string; + updated_at?: string; +} diff --git a/packages/user-client/src/services/channels/models/ArrayWithMetadataOfSlackToken.ts b/packages/user-client/src/services/channels/models/ArrayWithMetadataOfSlackToken.ts new file mode 100644 index 000000000..8392dddca --- /dev/null +++ b/packages/user-client/src/services/channels/models/ArrayWithMetadataOfSlackToken.ts @@ -0,0 +1,16 @@ +import { SlackToken } from './SlackToken'; + +type Data = SlackToken; + +export interface ArrayWithMetadataOfSlackToken { + data: { + data: Data; + metadata: Metadata; + }[]; +} +interface Metadata { + created_at: string; + discarded_at?: string; + id: string; + updated_at?: string; +} diff --git a/packages/user-client/src/services/channels/models/ArrayWithMetadataOfTeamsToken.ts b/packages/user-client/src/services/channels/models/ArrayWithMetadataOfTeamsToken.ts new file mode 100644 index 000000000..3b6d2b5df --- /dev/null +++ b/packages/user-client/src/services/channels/models/ArrayWithMetadataOfTeamsToken.ts @@ -0,0 +1,16 @@ +import { TeamsToken } from './TeamsToken'; + +type Data = TeamsToken; + +export interface ArrayWithMetadataOfTeamsToken { + data: { + data: Data; + metadata: Metadata; + }[]; +} +interface Metadata { + created_at: string; + discarded_at?: string; + id: string; + updated_at?: string; +} diff --git a/packages/user-client/src/services/channels/models/ArrayWithMetadataOfWebPushToken.ts b/packages/user-client/src/services/channels/models/ArrayWithMetadataOfWebPushToken.ts new file mode 100644 index 000000000..fa68aa35d --- /dev/null +++ b/packages/user-client/src/services/channels/models/ArrayWithMetadataOfWebPushToken.ts @@ -0,0 +1,16 @@ +import { WebPushToken } from './WebPushToken'; + +type Data = WebPushToken; + +export interface ArrayWithMetadataOfWebPushToken { + data: { + data: Data; + metadata: Metadata; + }[]; +} +interface Metadata { + created_at: string; + discarded_at?: string; + id: string; + updated_at?: string; +} diff --git a/packages/user-client/src/services/channels/models/DiscardResult.ts b/packages/user-client/src/services/channels/models/DiscardResult.ts new file mode 100644 index 000000000..77643bc61 --- /dev/null +++ b/packages/user-client/src/services/channels/models/DiscardResult.ts @@ -0,0 +1,4 @@ +export interface DiscardResult { + discarded_at?: string; + id?: string; +} diff --git a/packages/user-client/src/services/channels/models/FcmToken.ts b/packages/user-client/src/services/channels/models/FcmToken.ts new file mode 100644 index 000000000..7043fee31 --- /dev/null +++ b/packages/user-client/src/services/channels/models/FcmToken.ts @@ -0,0 +1,6 @@ +type InstallationId = 'development' | 'production'; + +export interface FcmToken { + device_token: string; + installation_id?: InstallationId; +} diff --git a/packages/user-client/src/services/channels/models/FcmTokenWithMetadata.ts b/packages/user-client/src/services/channels/models/FcmTokenWithMetadata.ts new file mode 100644 index 000000000..46925b058 --- /dev/null +++ b/packages/user-client/src/services/channels/models/FcmTokenWithMetadata.ts @@ -0,0 +1,14 @@ +import { FcmToken } from './FcmToken'; + +type Data = FcmToken; + +export interface FcmTokenWithMetadata { + data: Data; + metadata: Metadata; +} +interface Metadata { + created_at: string; + discarded_at?: string; + id: string; + updated_at?: string; +} diff --git a/packages/user-client/src/services/channels/models/SaveMobilePushApnsTokenRequest.ts b/packages/user-client/src/services/channels/models/SaveMobilePushApnsTokenRequest.ts new file mode 100644 index 000000000..1919a8d4e --- /dev/null +++ b/packages/user-client/src/services/channels/models/SaveMobilePushApnsTokenRequest.ts @@ -0,0 +1,6 @@ +type InstallationId = 'development' | 'production'; + +export interface SaveMobilePushApnsTokenRequest { + device_token: string; + installation_id?: InstallationId; +} diff --git a/packages/user-client/src/services/channels/models/SaveMobilePushFcmTokenRequest.ts b/packages/user-client/src/services/channels/models/SaveMobilePushFcmTokenRequest.ts new file mode 100644 index 000000000..0fe9e6451 --- /dev/null +++ b/packages/user-client/src/services/channels/models/SaveMobilePushFcmTokenRequest.ts @@ -0,0 +1,6 @@ +type InstallationId = 'development' | 'production'; + +export interface SaveMobilePushFcmTokenRequest { + device_token: string; + installation_id?: InstallationId; +} diff --git a/packages/user-client/src/services/channels/models/SaveSlackTokenRequest.ts b/packages/user-client/src/services/channels/models/SaveSlackTokenRequest.ts new file mode 100644 index 000000000..7445c2d01 --- /dev/null +++ b/packages/user-client/src/services/channels/models/SaveSlackTokenRequest.ts @@ -0,0 +1,12 @@ +export interface SaveSlackTokenRequest { + oauth?: Oauth; + webhook?: Webhook; +} +interface Oauth { + channel_id: string; + installation_id: string; + scope?: string; +} +interface Webhook { + url: string; +} diff --git a/packages/user-client/src/services/channels/models/SaveTeamsTokenRequest.ts b/packages/user-client/src/services/channels/models/SaveTeamsTokenRequest.ts new file mode 100644 index 000000000..82c591d41 --- /dev/null +++ b/packages/user-client/src/services/channels/models/SaveTeamsTokenRequest.ts @@ -0,0 +1 @@ +export interface SaveTeamsTokenRequest {} diff --git a/packages/user-client/src/services/channels/models/SaveWebPushTokenRequest.ts b/packages/user-client/src/services/channels/models/SaveWebPushTokenRequest.ts new file mode 100644 index 000000000..a4bb6070b --- /dev/null +++ b/packages/user-client/src/services/channels/models/SaveWebPushTokenRequest.ts @@ -0,0 +1,8 @@ +export interface SaveWebPushTokenRequest { + endpoint: string; + keys: Keys; +} +interface Keys { + auth: string; + p256dh: string; +} diff --git a/packages/user-client/src/services/channels/models/SlackToken.ts b/packages/user-client/src/services/channels/models/SlackToken.ts new file mode 100644 index 000000000..46db8652d --- /dev/null +++ b/packages/user-client/src/services/channels/models/SlackToken.ts @@ -0,0 +1,12 @@ +export interface SlackToken { + oauth?: Oauth; + webhook?: Webhook; +} +interface Oauth { + channel_id: string; + installation_id: string; + scope?: string; +} +interface Webhook { + url: string; +} diff --git a/packages/user-client/src/services/channels/models/SlackTokenWithMetadata.ts b/packages/user-client/src/services/channels/models/SlackTokenWithMetadata.ts new file mode 100644 index 000000000..47f9edd8f --- /dev/null +++ b/packages/user-client/src/services/channels/models/SlackTokenWithMetadata.ts @@ -0,0 +1,14 @@ +import { SlackToken } from './SlackToken'; + +type Data = SlackToken; + +export interface SlackTokenWithMetadata { + data: Data; + metadata: Metadata; +} +interface Metadata { + created_at: string; + discarded_at?: string; + id: string; + updated_at?: string; +} diff --git a/packages/user-client/src/services/channels/models/TeamsToken.ts b/packages/user-client/src/services/channels/models/TeamsToken.ts new file mode 100644 index 000000000..207768b58 --- /dev/null +++ b/packages/user-client/src/services/channels/models/TeamsToken.ts @@ -0,0 +1 @@ +export interface TeamsToken {} diff --git a/packages/user-client/src/services/channels/models/TeamsTokenWithMetadata.ts b/packages/user-client/src/services/channels/models/TeamsTokenWithMetadata.ts new file mode 100644 index 000000000..04e418ca5 --- /dev/null +++ b/packages/user-client/src/services/channels/models/TeamsTokenWithMetadata.ts @@ -0,0 +1,14 @@ +import { TeamsToken } from './TeamsToken'; + +type Data = TeamsToken; + +export interface TeamsTokenWithMetadata { + data: Data; + metadata: Metadata; +} +interface Metadata { + created_at: string; + discarded_at?: string; + id: string; + updated_at?: string; +} diff --git a/packages/user-client/src/services/channels/models/WebPushToken.ts b/packages/user-client/src/services/channels/models/WebPushToken.ts new file mode 100644 index 000000000..a69af1965 --- /dev/null +++ b/packages/user-client/src/services/channels/models/WebPushToken.ts @@ -0,0 +1,8 @@ +export interface WebPushToken { + endpoint: string; + keys: Keys; +} +interface Keys { + auth: string; + p256dh: string; +} diff --git a/packages/user-client/src/services/channels/models/WebPushTokenWithMetadata.ts b/packages/user-client/src/services/channels/models/WebPushTokenWithMetadata.ts new file mode 100644 index 000000000..1d26abff7 --- /dev/null +++ b/packages/user-client/src/services/channels/models/WebPushTokenWithMetadata.ts @@ -0,0 +1,14 @@ +import { WebPushToken } from './WebPushToken'; + +type Data = WebPushToken; + +export interface WebPushTokenWithMetadata { + data: Data; + metadata: Metadata; +} +interface Metadata { + created_at: string; + discarded_at?: string; + id: string; + updated_at?: string; +} diff --git a/packages/user-client/src/services/integrations/Integrations.ts b/packages/user-client/src/services/integrations/Integrations.ts new file mode 100644 index 000000000..1f1ea339e --- /dev/null +++ b/packages/user-client/src/services/integrations/Integrations.ts @@ -0,0 +1,96 @@ +import BaseService from '../../BaseService'; +import { FinishSlackInstallationRequest } from './models/FinishSlackInstallationRequest'; +import { SaveSlackInstallationRequest } from './models/SaveSlackInstallationRequest'; +import { SaveTemplatesInstallationRequest } from './models/SaveTemplatesInstallationRequest'; +import { SlackInstallation } from './models/SlackInstallation'; +import { StartSlackInstallationRequest } from './models/StartSlackInstallationRequest'; +import { StartWebPushInstallationRequest } from './models/StartWebPushInstallationRequest'; +import { TemplatesInstallation } from './models/TemplatesInstallation'; +import { WebPushStartInstallationResponse } from './models/WebPushStartInstallationResponse'; + +export class IntegrationsService extends BaseService { + async saveSlackInstallation(input: SaveSlackInstallationRequest): Promise { + const headers: { [key: string]: string } = { 'Content-Type': 'application/json' }; + const urlEndpoint = '/integrations/slack/installations'; + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.post( + finalUrl, + input, + { + ...headers, + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as SlackInstallation; + return responseModel; + } + + async finishSlackInstallation(input: FinishSlackInstallationRequest): Promise { + const headers: { [key: string]: string } = { 'Content-Type': 'application/json' }; + const urlEndpoint = '/integrations/slack/installations/finish'; + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.post( + finalUrl, + input, + { + ...headers, + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as SlackInstallation; + return responseModel; + } + + async startSlackInstallation(input: StartSlackInstallationRequest): Promise { + const headers: { [key: string]: string } = { 'Content-Type': 'application/json' }; + const urlEndpoint = '/integrations/slack/installations/start'; + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.post( + finalUrl, + input, + { + ...headers, + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data; + return responseModel; + } + + async saveTemplatesInstallation(input: SaveTemplatesInstallationRequest): Promise { + const headers: { [key: string]: string } = { 'Content-Type': 'application/json' }; + const urlEndpoint = '/integrations/templates/installations'; + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.post( + finalUrl, + input, + { + ...headers, + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as TemplatesInstallation; + return responseModel; + } + + async startWebPushInstallation(input: StartWebPushInstallationRequest): Promise { + const headers: { [key: string]: string } = { 'Content-Type': 'application/json' }; + const urlEndpoint = '/integrations/web_push/installations/start'; + const finalUrl = encodeURI(`${this.baseUrl + urlEndpoint}`); + const response: any = await this.httpClient.post( + finalUrl, + input, + { + ...headers, + ...this.getAuthorizationHeader(), + }, + true, + ); + const responseModel = response.data as WebPushStartInstallationResponse; + return responseModel; + } +} diff --git a/packages/user-client/src/services/integrations/index.ts b/packages/user-client/src/services/integrations/index.ts new file mode 100644 index 000000000..77176afb2 --- /dev/null +++ b/packages/user-client/src/services/integrations/index.ts @@ -0,0 +1,8 @@ +export type { FinishSlackInstallationRequest } from './models/FinishSlackInstallationRequest'; +export type { SaveSlackInstallationRequest } from './models/SaveSlackInstallationRequest'; +export type { SaveTemplatesInstallationRequest } from './models/SaveTemplatesInstallationRequest'; +export type { SlackInstallation } from './models/SlackInstallation'; +export type { StartSlackInstallationRequest } from './models/StartSlackInstallationRequest'; +export type { StartWebPushInstallationRequest } from './models/StartWebPushInstallationRequest'; +export type { TemplatesInstallation } from './models/TemplatesInstallation'; +export type { WebPushStartInstallationResponse } from './models/WebPushStartInstallationResponse'; diff --git a/packages/user-client/src/services/integrations/models/FinishSlackInstallationRequest.ts b/packages/user-client/src/services/integrations/models/FinishSlackInstallationRequest.ts new file mode 100644 index 000000000..045fe42fb --- /dev/null +++ b/packages/user-client/src/services/integrations/models/FinishSlackInstallationRequest.ts @@ -0,0 +1,5 @@ +export interface FinishSlackInstallationRequest { + app_id: string; + code: string; + redirect_url?: string; +} diff --git a/packages/user-client/src/services/integrations/models/SaveSlackInstallationRequest.ts b/packages/user-client/src/services/integrations/models/SaveSlackInstallationRequest.ts new file mode 100644 index 000000000..b1491797b --- /dev/null +++ b/packages/user-client/src/services/integrations/models/SaveSlackInstallationRequest.ts @@ -0,0 +1,36 @@ +export interface SaveSlackInstallationRequest { + access_token: string; + app_id: string; + authed_user: AuthedUser; + bot_user_id?: string; + enterprise?: Enterprise; + expires_in?: number; + id?: string; + incoming_webhook?: IncomingWebhook; + is_enterprise_install?: boolean; + refresh_token?: string; + scope?: string; + team: Team; + token_type?: string; +} +interface AuthedUser { + access_token?: string; + expires_in?: number; + id: string; + refresh_token?: string; + scope?: string; + token_type?: string; +} +interface Enterprise { + id: string; + name: string; +} +interface IncomingWebhook { + channel: string; + configuration_url: string; + url: string; +} +interface Team { + id: string; + name?: string; +} diff --git a/packages/user-client/src/services/integrations/models/SaveTemplatesInstallationRequest.ts b/packages/user-client/src/services/integrations/models/SaveTemplatesInstallationRequest.ts new file mode 100644 index 000000000..55714e2f8 --- /dev/null +++ b/packages/user-client/src/services/integrations/models/SaveTemplatesInstallationRequest.ts @@ -0,0 +1,5 @@ +export interface SaveTemplatesInstallationRequest { + category?: string; + channel: string; + text: string; +} diff --git a/packages/user-client/src/services/integrations/models/SlackInstallation.ts b/packages/user-client/src/services/integrations/models/SlackInstallation.ts new file mode 100644 index 000000000..932b34eaa --- /dev/null +++ b/packages/user-client/src/services/integrations/models/SlackInstallation.ts @@ -0,0 +1,36 @@ +export interface SlackInstallation { + access_token: string; + app_id: string; + authed_user: AuthedUser; + bot_user_id?: string; + enterprise?: Enterprise; + expires_in?: number; + id?: string; + incoming_webhook?: IncomingWebhook; + is_enterprise_install?: boolean; + refresh_token?: string; + scope?: string; + team: Team; + token_type?: string; +} +interface AuthedUser { + access_token?: string; + expires_in?: number; + id: string; + refresh_token?: string; + scope?: string; + token_type?: string; +} +interface Enterprise { + id: string; + name: string; +} +interface IncomingWebhook { + channel: string; + configuration_url: string; + url: string; +} +interface Team { + id: string; + name?: string; +} diff --git a/packages/user-client/src/services/integrations/models/StartSlackInstallationRequest.ts b/packages/user-client/src/services/integrations/models/StartSlackInstallationRequest.ts new file mode 100644 index 000000000..1ddc3afc4 --- /dev/null +++ b/packages/user-client/src/services/integrations/models/StartSlackInstallationRequest.ts @@ -0,0 +1,6 @@ +export interface StartSlackInstallationRequest { + app_id: string; + auth_url?: string; + extra_scopes?: string[]; + redirect_url?: string; +} diff --git a/packages/user-client/src/services/integrations/models/StartWebPushInstallationRequest.ts b/packages/user-client/src/services/integrations/models/StartWebPushInstallationRequest.ts new file mode 100644 index 000000000..d32c3e111 --- /dev/null +++ b/packages/user-client/src/services/integrations/models/StartWebPushInstallationRequest.ts @@ -0,0 +1 @@ +export interface StartWebPushInstallationRequest {} diff --git a/packages/user-client/src/services/integrations/models/TemplatesInstallation.ts b/packages/user-client/src/services/integrations/models/TemplatesInstallation.ts new file mode 100644 index 000000000..2f7fdc8f7 --- /dev/null +++ b/packages/user-client/src/services/integrations/models/TemplatesInstallation.ts @@ -0,0 +1,5 @@ +export interface TemplatesInstallation { + category?: string; + channel: string; + text: string; +} diff --git a/packages/user-client/src/services/integrations/models/WebPushStartInstallationResponse.ts b/packages/user-client/src/services/integrations/models/WebPushStartInstallationResponse.ts new file mode 100644 index 000000000..f5f4f439f --- /dev/null +++ b/packages/user-client/src/services/integrations/models/WebPushStartInstallationResponse.ts @@ -0,0 +1,4 @@ +export interface WebPushStartInstallationResponse { + auth_token: string; + public_key: string; +} diff --git a/packages/user-client/tsconfig.build.json b/packages/user-client/tsconfig.build.json new file mode 100644 index 000000000..d4cb15091 --- /dev/null +++ b/packages/user-client/tsconfig.build.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.build.json", + "include": ["src"], + "exclude": ["src/client/@custom_types"], + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist", + "target": "es2018", + "baseUrl": "./", + "declarationMap": true, + "lib": ["ES2021.String", "dom", "esnext"], + "noEmit": false + } +} diff --git a/tsconfig.json b/tsconfig.json index bac95c319..047ee3630 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,11 @@ ], "compilerOptions": { "baseUrl": ".", - "typeRoots": ["./node_modules/@types", "./types", "@testing-library/jest-dom/jest-globals"], + "typeRoots": [ + "./node_modules/@types", + "./types", + "@testing-library/jest-dom/jest-globals" + ], "resolveJsonModule": true, "paths": { "~/sb-decorators": [".storybook/decorators"], @@ -20,6 +24,7 @@ "@magicbell/embeddable": ["packages/embeddable/src"], "@magicbell/magicbell-react": ["packages/react/src"], "@magicbell/react-headless": ["packages/react-headless/src"], + "@magicbell/user-client": ["packages/user-client/src"], "@magicbell/utils": ["packages/utils/src"], "@magicbell/webpush": ["packages/webpush/src"], "magicbell": ["packages/magicbell/src"], diff --git a/tsconfig.test.json b/tsconfig.test.json new file mode 100644 index 000000000..ab2524c6c --- /dev/null +++ b/tsconfig.test.json @@ -0,0 +1,16 @@ +{ + "extends": "./tsconfig.json", + "include": [ + "packages", + "scripts", + "types", + "jest", + "@testing-library/jest-dom", + "@testing-library/jest-dom/jest-globals" + ], + "compilerOptions": { + "noUnusedLocals": false, + "noUnusedParameters": false, + "typeRoots": ["./node_modules/@types", "./types", "@testing-library/jest-dom/jest-globals"] + } +} diff --git a/yarn.lock b/yarn.lock index d8c47f82b..a0848c7aa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3736,6 +3736,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.29.tgz#a0c58d67a42f8953c13d32f0acda47ed26dfce40" integrity sha512-LhF+9fbIX4iPzhsRLpK5H7iPdvW8L4IwGciXQIOEcuF62+9nw/VQVsOViAOOGxY3OlOKGLFv0sWwJXdwQeTn6A== +"@types/node@^17.0.23": + version "17.0.45" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" + integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== + "@types/node@^18.0.0": version "18.19.37" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.37.tgz#506ee89d6b5edd5a4c4e01b22162dd8309718a35" @@ -5813,6 +5818,11 @@ detect-indent@^6.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== +detect-indent@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-7.0.1.tgz#cbb060a12842b9c4d333f1cac4aa4da1bb66bc25" + integrity sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g== + detect-libc@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" @@ -5823,6 +5833,11 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== +detect-newline@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-4.0.1.tgz#fcefdb5713e1fb8cb2839b8b6ee22e6716ab8f23" + integrity sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog== + detect-node-es@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493" @@ -6816,7 +6831,7 @@ fast-diff@^1.1.2: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== -fast-glob@^3.2.9: +fast-glob@^3.2.9, fast-glob@^3.3.0: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -7210,6 +7225,11 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-stdin@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-9.0.0.tgz#3983ff82e03d56f1b2ea0d3e60325f39d703a575" + integrity sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA== + get-stream@^5.0.0, get-stream@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" @@ -7257,6 +7277,11 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +git-hooks-list@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/git-hooks-list/-/git-hooks-list-3.1.0.tgz#386dc531dcc17474cf094743ff30987a3d3e70fc" + integrity sha512-LF8VeHeR7v+wAbXqfgRlTSX/1BJR9Q1vEMR8JAz1cEg6GX07+zyj3sAdDvYjj/xnlIfVuGgj4qBei1K3hKH+PA== + github-from-package@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" @@ -7379,6 +7404,17 @@ globby@^11.0.0, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +globby@^13.1.2: + version "13.2.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592" + integrity sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w== + dependencies: + dir-glob "^3.0.1" + fast-glob "^3.3.0" + ignore "^5.2.4" + merge2 "^1.4.1" + slash "^4.0.0" + globrex@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" @@ -7774,6 +7810,11 @@ ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78" integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg== +ignore@^5.2.4: + version "5.3.1" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" + integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== + immer@^9.0.21: version "9.0.21" resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176" @@ -8115,6 +8156,11 @@ is-plain-obj@^1.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== +is-plain-obj@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" + integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== + is-plain-object@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" @@ -8867,7 +8913,7 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== -json-stringify-safe@~5.0.1: +json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== @@ -9690,6 +9736,15 @@ nise@^5.1.1: just-extend "^4.0.2" path-to-regexp "^1.7.0" +nock@^13.2.4: + version "13.5.4" + resolved "https://registry.yarnpkg.com/nock/-/nock-13.5.4.tgz#8918f0addc70a63736170fef7106a9721e0dc479" + integrity sha512-yAyTfdeNJGGBFxWdzSKCBYxs5FxLbCg5X5Q4ets974hcQzG1+qCxvIyOo4j2Ry6MUlhWVMX4OoYDefAIIwupjw== + dependencies: + debug "^4.1.0" + json-stringify-safe "^5.0.1" + propagate "^2.0.0" + node-abi@^3.3.0: version "3.45.0" resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.45.0.tgz#f568f163a3bfca5aacfce1fbeee1fa2cc98441f5" @@ -10447,6 +10502,11 @@ prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, object-assign "^4.1.1" react-is "^16.13.1" +propagate@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" + integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== + property-information@^5.0.0: version "5.6.0" resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69" @@ -10629,7 +10689,7 @@ react-devtools-inline@4.4.0: dependencies: es6-symbol "^3" -"react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", react-dom@^18.2.0: +"react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", react-dom@^18.3.1: version "18.3.1" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== @@ -10808,7 +10868,7 @@ react-use@^17.5.0: ts-easing "^0.2.0" tslib "^2.1.0" -"react@^16.8.0 || ^17.0.0 || ^18.0.0", react@^18.2.0: +"react@^16.8.0 || ^17.0.0 || ^18.0.0", react@^18.3.1: version "18.3.1" resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== @@ -11349,6 +11409,11 @@ semver@^7.3.5, semver@^7.3.7, semver@^7.5.3, semver@^7.5.4: dependencies: lru-cache "^6.0.0" +semver@^7.6.0: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + send@0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" @@ -11550,6 +11615,11 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" + integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== + slice-ansi@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" @@ -11596,6 +11666,25 @@ smartwrap@^2.0.2: wcwidth "^1.0.1" yargs "^15.1.0" +sort-object-keys@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sort-object-keys/-/sort-object-keys-1.1.3.tgz#bff833fe85cab147b34742e45863453c1e190b45" + integrity sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg== + +sort-package-json@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/sort-package-json/-/sort-package-json-2.10.0.tgz#6be07424bf3b7db9fbb1bdd69e7945f301026d8a" + integrity sha512-MYecfvObMwJjjJskhxYfuOADkXp1ZMMnCFC8yhp+9HDsk7HhR336hd7eiBs96lTXfiqmUNI+WQCeCMRBhl251g== + dependencies: + detect-indent "^7.0.1" + detect-newline "^4.0.0" + get-stdin "^9.0.0" + git-hooks-list "^3.0.0" + globby "^13.1.2" + is-plain-obj "^4.1.0" + semver "^7.6.0" + sort-object-keys "^1.1.3" + source-map-js@^1.0.2, source-map-js@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af"