Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
82 changes: 82 additions & 0 deletions lib/management/outboundapplication.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<OutboundApplication> =
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<OutboundApplication> =
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 = {
Expand Down
10 changes: 10 additions & 0 deletions lib/management/outboundapplication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
OutboundAppToken,
FetchOutboundAppTokenOptions,
OutboundAppTokenResponse,
CreateOutboundAppByTemplateOptions,
} from './types';

type OutboundApplicationResponse = {
Expand All @@ -27,6 +28,15 @@ const withOutboundApplication = (httpClient: HttpClient) => ({
}),
(data) => data.app,
),
createApplicationByTemplate: (
options: CreateOutboundAppByTemplateOptions,
): Promise<SdkResponse<OutboundApplication>> =>
transformResponse<OutboundApplicationResponse, OutboundApplication>(
httpClient.post(apiPaths.outboundApplication.createByTemplate, {
...options,
}),
(data) => data.app,
),
updateApplication: (
app: OutboundApplication & { clientSecret?: string },
): Promise<SdkResponse<OutboundApplication>> =>
Expand Down
1 change: 1 addition & 0 deletions lib/management/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
35 changes: 35 additions & 0 deletions lib/management/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,41 @@ export type OutboundApplication = {
prompt?: Array<PromptType>;
};

// 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<PromptType>;
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;
Expand Down
Loading