diff --git a/examples/pnpm-lock.yaml b/examples/pnpm-lock.yaml index e3d14e2..cbc37a8 100644 --- a/examples/pnpm-lock.yaml +++ b/examples/pnpm-lock.yaml @@ -2593,12 +2593,12 @@ packages: '@typescript-eslint/visitor-keys': 7.18.0 dev: true - /@typescript-eslint/scope-manager@8.21.0: - resolution: {integrity: sha512-G3IBKz0/0IPfdeGRMbp+4rbjfSSdnGkXsM/pFZA8zM9t9klXDnB/YnKOBQ0GoPmoROa4bCq2NeHgJa5ydsQ4mA==} + /@typescript-eslint/scope-manager@8.22.0: + resolution: {integrity: sha512-/lwVV0UYgkj7wPSw0o8URy6YI64QmcOdwHuGuxWIYznO6d45ER0wXUbksr9pYdViAofpUCNJx/tAzNukgvaaiQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dependencies: - '@typescript-eslint/types': 8.21.0 - '@typescript-eslint/visitor-keys': 8.21.0 + '@typescript-eslint/types': 8.22.0 + '@typescript-eslint/visitor-keys': 8.22.0 dev: true /@typescript-eslint/type-utils@7.18.0(eslint@8.57.0)(typescript@5.5.4): @@ -2626,8 +2626,8 @@ packages: engines: {node: ^18.18.0 || >=20.0.0} dev: true - /@typescript-eslint/types@8.21.0: - resolution: {integrity: sha512-PAL6LUuQwotLW2a8VsySDBwYMm129vFm4tMVlylzdoTybTHaAi0oBp7Ac6LhSrHHOdLM3efH+nAR6hAWoMF89A==} + /@typescript-eslint/types@8.22.0: + resolution: {integrity: sha512-0S4M4baNzp612zwpD4YOieP3VowOARgK2EkN/GBn95hpyF8E2fbMT55sRHWBq+Huaqk3b3XK+rxxlM8sPgGM6A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dev: true @@ -2646,26 +2646,26 @@ packages: globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.5 - semver: 7.6.3 + semver: 7.7.0 ts-api-utils: 1.4.3(typescript@5.5.4) typescript: 5.5.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/typescript-estree@8.21.0(typescript@5.5.4): - resolution: {integrity: sha512-x+aeKh/AjAArSauz0GiQZsjT8ciadNMHdkUSwBB9Z6PrKc/4knM4g3UfHml6oDJmKC88a6//cdxnO/+P2LkMcg==} + /@typescript-eslint/typescript-estree@8.22.0(typescript@5.5.4): + resolution: {integrity: sha512-SJX99NAS2ugGOzpyhMza/tX+zDwjvwAtQFLsBo3GQxiGcvaKlqGBkmZ+Y1IdiSi9h4Q0Lr5ey+Cp9CGWNY/F/w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.8.0' dependencies: - '@typescript-eslint/types': 8.21.0 - '@typescript-eslint/visitor-keys': 8.21.0 + '@typescript-eslint/types': 8.22.0 + '@typescript-eslint/visitor-keys': 8.22.0 debug: 4.4.0 fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 - semver: 7.6.3 + semver: 7.7.0 ts-api-utils: 2.0.0(typescript@5.5.4) typescript: 5.5.4 transitivePeerDependencies: @@ -2688,17 +2688,17 @@ packages: - typescript dev: true - /@typescript-eslint/utils@8.21.0(eslint@8.57.0)(typescript@5.5.4): - resolution: {integrity: sha512-xcXBfcq0Kaxgj7dwejMbFyq7IOHgpNMtVuDveK7w3ZGwG9owKzhALVwKpTF2yrZmEwl9SWdetf3fxNzJQaVuxw==} + /@typescript-eslint/utils@8.22.0(eslint@8.57.0)(typescript@5.5.4): + resolution: {integrity: sha512-T8oc1MbF8L+Bk2msAvCUzjxVB2Z2f+vXYfcucE2wOmYs7ZUwco5Ep0fYZw8quNwOiw9K8GYVL+Kgc2pETNTLOg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.8.0' dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@8.57.0) - '@typescript-eslint/scope-manager': 8.21.0 - '@typescript-eslint/types': 8.21.0 - '@typescript-eslint/typescript-estree': 8.21.0(typescript@5.5.4) + '@typescript-eslint/scope-manager': 8.22.0 + '@typescript-eslint/types': 8.22.0 + '@typescript-eslint/typescript-estree': 8.22.0(typescript@5.5.4) eslint: 8.57.0 typescript: 5.5.4 transitivePeerDependencies: @@ -2713,11 +2713,11 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@typescript-eslint/visitor-keys@8.21.0: - resolution: {integrity: sha512-BkLMNpdV6prozk8LlyK/SOoWLmUFi+ZD+pcqti9ILCbVvHGk1ui1g4jJOc2WDLaeExz2qWwojxlPce5PljcT3w==} + /@typescript-eslint/visitor-keys@8.22.0: + resolution: {integrity: sha512-AWpYAXnUgvLNabGTy3uBylkgZoosva/miNd1I8Bz3SjotmQPbVqhO4Cczo8AsZ44XVErEBPr/CRSgaj8sG7g0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dependencies: - '@typescript-eslint/types': 8.21.0 + '@typescript-eslint/types': 8.22.0 eslint-visitor-keys: 4.2.0 dev: true @@ -4271,7 +4271,7 @@ packages: optional: true dependencies: '@typescript-eslint/eslint-plugin': 7.18.0(@typescript-eslint/parser@7.18.0)(eslint@8.57.0)(typescript@5.5.4) - '@typescript-eslint/utils': 8.21.0(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/utils': 8.22.0(eslint@8.57.0)(typescript@5.5.4) eslint: 8.57.0 jest: 29.7.0(@types/node@20.16.2)(ts-node@10.9.2) transitivePeerDependencies: @@ -5196,7 +5196,7 @@ packages: /is-bun-module@1.3.0: resolution: {integrity: sha512-DgXeu5UWI0IsMQundYb5UAOzm6G2eVnarJ0byP6Tm55iZNKceD59LNPA2L4VvsScTtHcw0yEkVwSf7PC+QoLSA==} dependencies: - semver: 7.6.3 + semver: 7.7.0 dev: true /is-callable@1.2.7: @@ -7139,6 +7139,13 @@ packages: resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} engines: {node: '>=10'} hasBin: true + dev: false + + /semver@7.7.0: + resolution: {integrity: sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==} + engines: {node: '>=10'} + hasBin: true + dev: true /set-function-length@1.2.0: resolution: {integrity: sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==} @@ -7889,6 +7896,11 @@ packages: engines: {node: '>= 4'} dev: false + /uuid@11.0.5: + resolution: {integrity: sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==} + hasBin: true + dev: false + /uuid@8.0.0: resolution: {integrity: sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==} hasBin: true @@ -8159,7 +8171,7 @@ packages: dev: false file:../lib/dist/cdk-practical-constructs-0.0.1.tgz(@asteasolutions/zod-to-openapi@7.0.0)(aws-cdk-lib@2.155.0)(zod@3.23.8): - resolution: {integrity: sha512-L3ljocWwduhV+40gwRLEpCwW/BQHCJvWrQ9IGKQGM9UHBMov4Zt+baje6PCx/ljIhqz3xwq70sv4puxpKRxUrg==, tarball: file:../lib/dist/cdk-practical-constructs-0.0.1.tgz} + resolution: {integrity: sha512-aOzmQSXU1N8fIJJtkSibe+gYVU64ppi393Mgy0wcEC/U9ZWMYuJU9FlO+HfCEcMbU6a4KFixpOm5ZTF82nuNlQ==, tarball: file:../lib/dist/cdk-practical-constructs-0.0.1.tgz} id: file:../lib/dist/cdk-practical-constructs-0.0.1.tgz name: cdk-practical-constructs version: 0.0.1 @@ -8187,6 +8199,7 @@ packages: qs: 6.11.2 scoperjs: 1.0.1 tmp: 0.2.1 + uuid: 11.0.5 zod: 3.23.8 transitivePeerDependencies: - aws-crt diff --git a/lib/src/utils/axios.ts b/lib/src/utils/axios.ts new file mode 100644 index 0000000..109ca5b --- /dev/null +++ b/lib/src/utils/axios.ts @@ -0,0 +1,54 @@ +/* eslint-disable no-console */ +import axios, { AxiosError, AxiosInstance } from 'axios'; + +export const prepareAxiosLogs = (client: AxiosInstance): void => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + client.interceptors.request.use((config: any) => { + // eslint-disable-next-line no-param-reassign + config.metadata = { startTime: new Date().getTime() }; + console.log(`> REQUEST: ${config.method?.toUpperCase()} ${config.baseURL}${config.url}`); + console.log( + JSON.stringify({ + baseURL: config.baseURL, + url: config.url, + params: config.params, + method: config.method, + headers: config.headers, + status: config.status, + data: config.data, + }), + ); + return config; + }); + + client.interceptors.response.use( + (response) => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const elapsedTime = new Date().getTime() - response.config.metadata.startTime; + console.log(`> RESPONSE: ${response.status} (${elapsedTime}ms)`); + console.log( + JSON.stringify({ + status: response.status, + headers: response.headers, + data: response.data, + }), + ); + return response; + }, + // eslint-disable-next-line promise/prefer-await-to-callbacks + (error: Error | AxiosError) => { + if (axios.isAxiosError(error)) { + console.log(`RESPONSE ERROR: status=${error.response?.status}`); + console.log( + JSON.stringify({ + status: error.response?.status, + headers: error.response?.headers, + data: error.response?.data, + }), + ); + } + throw error; + }, + ); +}; diff --git a/lib/src/wso2/wso2-api/handler/index.test.ts b/lib/src/wso2/wso2-api/handler/index.test.ts index f54cb87..e3270b1 100644 --- a/lib/src/wso2/wso2-api/handler/index.test.ts +++ b/lib/src/wso2/wso2-api/handler/index.test.ts @@ -550,12 +550,12 @@ describe('wso2 custom resource lambda', () => { } // api get endpoint urls mock - // nocks.push( - // nock(baseWso2Url) - // .get(/.*\/store\/v1\/apis\/123-456$/) - // .times(1) - // .reply(200, testDefs), - // ); + nocks.push( + nock(baseWso2Url) + .get(/.*\/store\/v1\/apis\/123-456$/) + .times(1) + .reply(200, testDefs), + ); // api get apis mock nocks.push( diff --git a/lib/src/wso2/wso2-api/handler/wso2-v1.ts b/lib/src/wso2/wso2-api/handler/wso2-v1.ts index 1d73047..d6cd962 100644 --- a/lib/src/wso2/wso2-api/handler/wso2-v1.ts +++ b/lib/src/wso2/wso2-api/handler/wso2-v1.ts @@ -7,6 +7,7 @@ import isEqual from 'lodash.isequal'; import { ApiFromListV1, + DevPortalAPIv1, PublisherPortalAPIv1, Wso2ApiDefinitionV1, Wso2ApiListV1, @@ -158,27 +159,26 @@ export const createUpdateAndChangeLifecycleStatusInWso2 = async ( } // get endpoint url - // console.log(`Getting API endpoint url`); - // const apir = await args.wso2Axios.get(`/api/am/store/v1/apis/${wso2ApiId}`); + console.log(`Getting API endpoint url`); + const apir = await args.wso2Axios.get(`/api/am/store/v1/apis/${wso2ApiId}`); // DISABLING ENDPOINT URL WHILE WE FIX AN ISSUE - const endpointUrl = ''; - // // find the endpoint URL of the environment that was defined in this API - // const apid = apir.data as DevPortalAPIv1; - // const endpointUrl = apid.endpointURLs?.reduce((acc, elem) => { - // if ( - // elem.environmentName && - // args.apiDefinition.gatewayEnvironments?.includes(elem.environmentName) - // ) { - // if (elem.URLs?.https) { - // return elem.URLs?.https; - // } - // if (elem.defaultVersionURLs?.https) { - // return elem.defaultVersionURLs?.https; - // } - // } - // return acc; - // }, ''); + // find the endpoint URL of the environment that was defined in this API + const apid = apir.data as DevPortalAPIv1; + const endpointUrl = apid.endpointURLs?.reduce((acc, elem) => { + if ( + elem.environmentName && + args.apiDefinition.gatewayEnvironments?.includes(elem.environmentName) + ) { + if (elem.URLs?.https) { + return elem.URLs?.https; + } + if (elem.defaultVersionURLs?.https) { + return elem.defaultVersionURLs?.https; + } + } + return acc; + }, ''); console.log('API created/updated successfully on WSO2 server'); diff --git a/lib/src/wso2/wso2-utils.ts b/lib/src/wso2/wso2-utils.ts index f2b185d..f860bd6 100644 --- a/lib/src/wso2/wso2-utils.ts +++ b/lib/src/wso2/wso2-utils.ts @@ -1,9 +1,11 @@ /* eslint-disable no-console */ import https from 'https'; -import axios, { AxiosError, AxiosInstance } from 'axios'; +import axios, { AxiosInstance } from 'axios'; import qs from 'qs'; +import { prepareAxiosLogs } from '../utils/axios'; + import { Wso2Config } from './types'; import { getSecretValue } from './utils'; @@ -22,7 +24,7 @@ export type Wso2ApimConfig = { password: string; /** * WSO2 client name registered before API calls - * @default 'wso2apim-sdk-client' + * @default cdk-practical-constructs-2015-01 */ clientName?: string; /** @@ -69,14 +71,20 @@ export const prepareAxiosForWso2Calls = async (wso2Config: Wso2Config): Promise< baseUrl: wso2Config.baseApiUrl, username, password: wso2Creds.pwd, - clientName: 'cdk-practical-constructs-wso2', + clientName: `cdk-practical-constructs-01-2025`, // change this suffix every time different scopes are requested to avoid problems with WSO2 caching }; - const clientCredentials = await registerClient(wso2ApimConfig); - const accessToken = await getBearerToken(wso2ApimConfig, clientCredentials); + // get Bearer Token + const axiosClient = axios.create({ + baseURL: wso2Config.baseApiUrl, + }); + prepareAxiosLogs(axiosClient); - await checkWso2ServerVersion(wso2ApimConfig, 'v1'); + const clientCredentials = await registerClient(wso2ApimConfig, axiosClient); + const accessToken = await getBearerToken(wso2ApimConfig, axiosClient, clientCredentials); + await checkWso2ServerVersion(wso2ApimConfig, axiosClient, 'v1'); + // set bearer token for all invocations const client = axios.create({ baseURL: wso2Config.baseApiUrl, headers: { @@ -84,64 +92,19 @@ export const prepareAxiosForWso2Calls = async (wso2Config: Wso2Config): Promise< }, }); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - client.interceptors.request.use((config: any) => { - // eslint-disable-next-line no-param-reassign - config.metadata = { startTime: new Date().getTime() }; - console.log(`> REQUEST: ${config.method?.toUpperCase()} ${wso2Config.baseApiUrl}${config.url}`); - console.log( - JSON.stringify({ - baseURL: config.baseURL, - url: config.url, - params: config.params, - method: config.method, - headers: config.headers, - status: config.status, - data: config.data, - }), - ); - return config; - }); - - client.interceptors.response.use( - (response) => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const elapsedTime = new Date().getTime() - response.config.metadata.startTime; - console.log(`> RESPONSE: ${response.status} (${elapsedTime}ms)`); - console.log( - JSON.stringify({ - status: response.status, - headers: response.headers, - data: response.data, - }), - ); - return response; - }, - // eslint-disable-next-line promise/prefer-await-to-callbacks - (error: Error | AxiosError) => { - if (axios.isAxiosError(error)) { - console.log(`RESPONSE ERROR: status=${error.response?.status}`); - console.log( - JSON.stringify({ - status: error.response?.status, - headers: error.response?.headers, - data: error.response?.data, - }), - ); - } - throw error; - }, - ); + prepareAxiosLogs(client); return client; }; -export const registerClient = async (config: Wso2ApimConfig): Promise => { +export const registerClient = async ( + config: Wso2ApimConfig, + axiosClient: AxiosInstance, +): Promise => { const data = { - clientName: config.clientName ?? 'wso2apim-sdk-client', + clientName: config.clientName ?? `cdk-practical-constructs-2025-01}`, owner: config.owner ?? config.username, - grantType: 'password refresh_token', + grantType: 'password refresh_token client_credentials', saasApp: true, }; @@ -157,8 +120,10 @@ export const registerClient = async (config: Wso2ApimConfig): Promise( - `${config.baseUrl}/client-registration/v0.17/register`, + console.log('Registering client...'); + + const res = axiosClient.post( + `/client-registration/v0.17/register`, data, axiosConfig, ); @@ -168,6 +133,7 @@ export const registerClient = async (config: Wso2ApimConfig): Promise => { // tested minimum for publisher: apim:api_create apim:api_view apim:api_publish apim:api_delete @@ -178,9 +144,7 @@ export const getBearerToken = async ( const data = qs.stringify({ // eslint-disable-next-line camelcase - grant_type: 'password', - username: config.username, - password: config.password, + grant_type: 'client_credentials', scope: `${scopesPublisher} ${scopesStore}`, }); @@ -198,22 +162,21 @@ export const getBearerToken = async ( }), }; - const res = axios.post<{ access_token: string }>( - `${config.baseUrl}/oauth2/token`, - data, - axiosConfig, - ); + console.log('Getting bearer token...'); + + const res = axiosClient.post<{ access_token: string }>(`/oauth2/token`, data, axiosConfig); return (await res).data.access_token; }; export const checkWso2ServerVersion = async ( config: Wso2ApimConfig, + axiosClient: AxiosInstance, wso2ApiVersion: string, ): Promise => { let info = ''; try { - const res = axios.get(`${config.baseUrl}/services/Version`, { + const res = axiosClient.get(`/services/Version`, { httpsAgent: new https.Agent({ rejectUnauthorized: config.tlsRejectUnauthorized }), }); const responseBody = (await res).data;