From 4f5bb1b081ab9941709a5dd707dad74d6869c361 Mon Sep 17 00:00:00 2001 From: aviadl Date: Sun, 21 Jun 2026 14:09:53 +0300 Subject: [PATCH] feat: create outbound app from template + tests fixes https://github.com/descope/node-sdk/issues/743 --- README.md | 15 ++++ lib/management/outboundapplication.test.ts | 82 ++++++++++++++++++++++ lib/management/outboundapplication.ts | 10 +++ lib/management/paths.ts | 1 + lib/management/types.ts | 35 +++++++++ 5 files changed, 143 insertions(+) diff --git a/README.md b/README.md index ec67f7a9b..9106fab48 100644 --- a/README.md +++ b/README.md @@ -1475,6 +1475,21 @@ const { id } = ... }); +// Create an outbound application from a preconfigured app library template. +// The template (e.g. "hubspot", "google", "slack") prepopulates the provider's +// OAuth config - authorization/token endpoints, default scopes, pkce, etc. +// Any field set in `overrides` takes precedence over the template default. +const { id: templatedId } = + await descopeClient.management.outboundApplication.createApplicationByTemplate({ + templateId: 'hubspot', + clientId: 'my-client-id', + clientSecret: 'my-client-secret', + overrides: { + name: 'HubSpot', + // authorizationUrl / tokenUrl / defaultScopes / pkce inherited from the template + }, + }); + // Update an outbound application. // Update will override all fields as is. Use carefully. await descopeClient.management.outboundApplication.updateApplication({ diff --git a/lib/management/outboundapplication.test.ts b/lib/management/outboundapplication.test.ts index f59e0e34a..844d90380 100644 --- a/lib/management/outboundapplication.test.ts +++ b/lib/management/outboundapplication.test.ts @@ -89,6 +89,88 @@ describe('Management OutboundApplication', () => { }); }); + describe('createOutboundApplicationByTemplate', () => { + it('should send the correct request and receive correct response', async () => { + const httpResponse = { + ok: true, + json: () => mockOutboundApplicationResponse, + clone: () => ({ + json: () => Promise.resolve(mockOutboundApplicationResponse), + }), + status: 200, + }; + mockHttpClient.post.mockResolvedValue(httpResponse); + + const resp: SdkResponse = + await management.outboundApplication.createApplicationByTemplate({ + templateId: 'hubspot', + id: 'hubspot', + clientId: 'client-id', + clientSecret: 'shhh..', + tenantId: 'tenant789', + overrides: { + name: 'HubSpot', + defaultScopes: ['crm.objects.contacts.read'], + pkce: true, + }, + }); + + expect(mockHttpClient.post).toHaveBeenCalledWith( + apiPaths.outboundApplication.createByTemplate, + { + templateId: 'hubspot', + id: 'hubspot', + clientId: 'client-id', + clientSecret: 'shhh..', + tenantId: 'tenant789', + overrides: { + name: 'HubSpot', + defaultScopes: ['crm.objects.contacts.read'], + pkce: true, + }, + }, + ); + + expect(resp).toEqual({ + code: 200, + data: mockOutboundApplicationResponse.app, + ok: true, + response: httpResponse, + }); + }); + + it('should work with only templateId', async () => { + const httpResponse = { + ok: true, + json: () => mockOutboundApplicationResponse, + clone: () => ({ + json: () => Promise.resolve(mockOutboundApplicationResponse), + }), + status: 200, + }; + mockHttpClient.post.mockResolvedValue(httpResponse); + + const resp: SdkResponse = + await management.outboundApplication.createApplicationByTemplate({ + templateId: 'google', + }); + + expect(mockHttpClient.post).toHaveBeenCalledWith( + apiPaths.outboundApplication.createByTemplate, + { + templateId: 'google', + }, + ); + + expect(resp).toEqual({ + code: 200, + data: mockOutboundApplicationResponse.app, + ok: true, + response: httpResponse, + }); + }); + }); + describe('updateOutboundApplication', () => { it('should send the correct request and receive correct response', async () => { const httpResponse = { diff --git a/lib/management/outboundapplication.ts b/lib/management/outboundapplication.ts index e751ee74a..8d7e770ff 100644 --- a/lib/management/outboundapplication.ts +++ b/lib/management/outboundapplication.ts @@ -5,6 +5,7 @@ import { OutboundAppToken, FetchOutboundAppTokenOptions, OutboundAppTokenResponse, + CreateOutboundAppByTemplateOptions, } from './types'; type OutboundApplicationResponse = { @@ -27,6 +28,15 @@ const withOutboundApplication = (httpClient: HttpClient) => ({ }), (data) => data.app, ), + createApplicationByTemplate: ( + options: CreateOutboundAppByTemplateOptions, + ): Promise> => + transformResponse( + httpClient.post(apiPaths.outboundApplication.createByTemplate, { + ...options, + }), + (data) => data.app, + ), updateApplication: ( app: OutboundApplication & { clientSecret?: string }, ): Promise> => diff --git a/lib/management/paths.ts b/lib/management/paths.ts index 45e4a83f6..01af98c9b 100644 --- a/lib/management/paths.ts +++ b/lib/management/paths.ts @@ -100,6 +100,7 @@ export default { }, outboundApplication: { create: '/v1/mgmt/outbound/app/create', + createByTemplate: '/v1/mgmt/outbound/app/create/bytemplate', update: '/v1/mgmt/outbound/app/update', delete: '/v1/mgmt/outbound/app/delete', load: '/v1/mgmt/outbound/app', diff --git a/lib/management/types.ts b/lib/management/types.ts index b623dbc4b..5e34d58da 100644 --- a/lib/management/types.ts +++ b/lib/management/types.ts @@ -1069,6 +1069,41 @@ export type OutboundApplication = { prompt?: Array; }; +// Fields of an outbound application that can override a template's +// prepopulated values when creating an application from the app library. +export type OutboundAppTemplateOverrides = { + name?: string; + description?: string; + logo?: string; + discoveryUrl?: string; + authorizationUrl?: string; + authorizationUrlParams?: URLParam[]; + tokenUrl?: string; + tokenUrlParams?: URLParam[]; + revocationUrl?: string; + defaultScopes?: string[]; + defaultRedirectUrl?: string; + callbackDomain?: string; + pkce?: boolean; + accessType?: AccessType; + prompt?: Array; + useDcr?: boolean; + dcrUrl?: string; +}; + +// Options for creating an outbound application from a preconfigured +// app library template. The template prepopulates the provider's OAuth +// config (endpoints, scopes, pkce, etc.); any field set in `overrides` +// takes precedence over the template default. +export type CreateOutboundAppByTemplateOptions = { + templateId: string; + id?: string; + clientId?: string; + clientSecret?: string; + tenantId?: string; + overrides?: OutboundAppTemplateOverrides; +}; + // Example for URLParam type (adjust as needed) export type URLParam = { key: string;