Skip to content

Commit 6d24e66

Browse files
committed
Adding new project? and oauth? params to CreateServerClientOpts
Adding invokeWorkflowForExternalUser
1 parent 2dc725e commit 6d24e66

File tree

2 files changed

+225
-15
lines changed

2 files changed

+225
-15
lines changed

packages/sdk/src/server/__tests__/server.test.ts

Lines changed: 127 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,18 @@ describe("ServerClient", () => {
2121
const client = createClient(params);
2222
expect(client).toBeInstanceOf(ServerClient);
2323
});
24+
25+
it("should mock the createClient method with a project object and return a ServerClient instance", () => {
26+
const params = {
27+
project: {
28+
publicKey: "test-public-key",
29+
secretKey: "test-secret",
30+
},
31+
};
32+
33+
const client = createClient(params);
34+
expect(client).toBeInstanceOf(ServerClient);
35+
});
2436
});
2537

2638
describe("makeRequest", () => {
@@ -534,12 +546,16 @@ describe("ServerClient", () => {
534546
} as unknown as ClientCredentials;
535547

536548
// Inject the mock oauthClient into the ServerClient instance
537-
client = new ServerClient(
549+
const client = new ServerClient(
538550
{
539-
publicKey: "test-public-key",
540-
secretKey: "test-secret-key",
541-
oauthClientId: "test-client-id",
542-
oauthClientSecret: "test-client-secret",
551+
project: {
552+
publicKey: "test-public-key",
553+
secretKey: "test",
554+
},
555+
oauth: {
556+
clientId: "test-client-id",
557+
clientSecret: "test-client-secret",
558+
},
543559
},
544560
oauthClientMock,
545561
);
@@ -607,10 +623,14 @@ describe("ServerClient", () => {
607623

608624
const client = new ServerClient(
609625
{
610-
publicKey: "test-public-key",
611-
secretKey: "test-secret-key",
612-
oauthClientId: "test-client-id",
613-
oauthClientSecret: "test-client-secret",
626+
project: {
627+
publicKey: "test-public-key",
628+
secretKey: "test",
629+
},
630+
oauth: {
631+
clientId: "test-client-id",
632+
clientSecret: "test-client-secret",
633+
},
614634
},
615635
oauthClientMock,
616636
);
@@ -643,4 +663,102 @@ describe("ServerClient", () => {
643663
);
644664
});
645665
});
666+
667+
describe("invokeWorkflowForExternalUser", () => {
668+
let client: ServerClient;
669+
670+
beforeEach(() => {
671+
fetchMock.resetMocks();
672+
// Create mock AccessToken objects
673+
const expiredTokenMock = {
674+
token: {
675+
access_token: "expired-oauth-token",
676+
},
677+
expired: jest.fn().mockReturnValue(true),
678+
};
679+
680+
const newTokenMock = {
681+
token: {
682+
access_token: "new-oauth-token",
683+
},
684+
expired: jest.fn().mockReturnValue(false),
685+
};
686+
687+
const getTokenMock = jest
688+
.fn()
689+
.mockResolvedValueOnce(expiredTokenMock)
690+
.mockResolvedValueOnce(newTokenMock);
691+
692+
const oauthClientMock = {
693+
getToken: getTokenMock,
694+
} as unknown as ClientCredentials;
695+
client = new ServerClient(
696+
{
697+
project: {
698+
publicKey: "test-public-key",
699+
secretKey: "test",
700+
},
701+
oauth: {
702+
clientId: "test-client-id",
703+
clientSecret: "test-client-secret",
704+
},
705+
},
706+
oauthClientMock,
707+
);
708+
});
709+
710+
it("should include externalUserId and publicKey headers", async () => {
711+
fetchMock.mockResponseOnce(
712+
JSON.stringify({
713+
result: "workflow-response",
714+
}),
715+
{
716+
headers: {
717+
"Content-Type": "application/json",
718+
},
719+
},
720+
);
721+
722+
const result = await client.invokeWorkflowForExternalUser("https://example.com/workflow", "external-user-id", {
723+
body: {
724+
foo: "bar",
725+
},
726+
});
727+
728+
expect(result).toEqual({
729+
result: "workflow-response",
730+
});
731+
732+
expect(fetchMock).toHaveBeenCalledWith(
733+
"https://example.com/workflow",
734+
expect.objectContaining({
735+
headers: expect.objectContaining({
736+
"X-PD-External-User-ID": "external-user-id",
737+
"X-PD-Project-Public-Key": "test-public-key",
738+
}),
739+
}),
740+
);
741+
});
742+
743+
it("should throw error when externalUserId is missing", async () => {
744+
await expect(client.invokeWorkflowForExternalUser("https://example.com/workflow", "", {
745+
body: {
746+
foo: "bar",
747+
},
748+
})).rejects.toThrow("External user ID is required");
749+
});
750+
751+
it("should throw error when publicKey is missing", async () => {
752+
const clientWithoutPublicKey = new ServerClient({
753+
secretKey: "test-secret-key",
754+
});
755+
756+
await expect(clientWithoutPublicKey.invokeWorkflowForExternalUser("https://example.com/workflow", "external-user-id", {
757+
body: {
758+
foo: "bar",
759+
},
760+
})).rejects.toThrow("Project public key is required to map the external user ID to the correct project");
761+
});
762+
});
763+
646764
});

packages/sdk/src/server/index.ts

Lines changed: 98 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,55 @@ import {
77
ClientCredentials,
88
} from "simple-oauth2";
99

10+
export type ProjectKeys = {
11+
publicKey: string;
12+
secretKey: string;
13+
};
14+
15+
export type PipedreamOAuthClient = {
16+
clientId: string;
17+
clientSecret: string;
18+
};
19+
1020
/**
1121
* Options for creating a server-side client.
1222
* This is used to configure the ServerClient instance.
1323
*/
1424
export type CreateServerClientOpts = {
1525
/**
26+
* @deprecated Use the `project` object instead.
1627
* The public API key for accessing the service.
1728
*/
1829
publicKey?: string;
1930

2031
/**
32+
* @deprecated Use the `project` object instead.
2133
* The secret API key for accessing the service.
2234
*/
2335
secretKey?: string;
2436

2537
/**
38+
* @deprecated Use the `oauth` object instead.
2639
* The client ID of your workspace's OAuth application.
2740
*/
2841
oauthClientId?: string;
2942

3043
/**
44+
* @deprecated Use the `oauth` object instead.
3145
* The client secret of your workspace's OAuth application.
3246
*/
3347
oauthClientSecret?: string;
3448

49+
/**
50+
* The project object, containing publicKey and secretKey.
51+
*/
52+
project?: ProjectKeys;
53+
54+
/**
55+
* The OAuth object, containing client ID and client secret.
56+
*/
57+
oauth?: PipedreamOAuthClient;
58+
3559
/**
3660
* The API host URL. Used by Pipedream employees. Defaults to "api.pipedream.com" if not provided.
3761
*/
@@ -310,6 +334,10 @@ export class ServerClient {
310334
) {
311335
this.secretKey = opts.secretKey;
312336
this.publicKey = opts.publicKey;
337+
if (opts.project) {
338+
this.publicKey = opts.project.publicKey;
339+
this.secretKey = opts.project.secretKey;
340+
}
313341

314342
const { apiHost = "api.pipedream.com" } = opts;
315343
this.baseURL = `https://${apiHost}/v1`;
@@ -327,18 +355,21 @@ export class ServerClient {
327355
{
328356
oauthClientId: id,
329357
oauthClientSecret: secret,
358+
oauth,
330359
}: CreateServerClientOpts,
331360
tokenHost: string,
332361
) {
333-
if (!id || !secret) {
362+
if (!oauth || !id || !secret) {
334363
return;
335364
}
336365

366+
const client = {
367+
id: id ?? oauth.clientId,
368+
secret: secret ?? oauth.clientSecret,
369+
};
370+
337371
this.oauthClient = new ClientCredentials({
338-
client: {
339-
id,
340-
secret,
341-
},
372+
client,
342373
auth: {
343374
tokenHost,
344375
tokenPath: "/v1/oauth/token",
@@ -716,7 +747,6 @@ export class ServerClient {
716747

717748
/**
718749
* Invokes a workflow using the URL of its HTTP interface(s), by sending an
719-
* HTTP POST request with the provided body.
720750
*
721751
* @param url - The URL of the workflow's HTTP interface.
722752
* @param opts - The options for the request.
@@ -764,4 +794,66 @@ export class ServerClient {
764794
body,
765795
});
766796
}
797+
798+
/**
799+
* Invokes a workflow for a Pipedream Connect user in a project
800+
*
801+
* @param url - The URL of the workflow's HTTP interface.
802+
* @param externalUserId — Your end user ID, for whom you're invoking the workflow.
803+
* @param opts - The options for the request.
804+
* @param opts.body - The body of the request. It must be a JSON-serializable
805+
* value (e.g. an object, null, a string, etc.).
806+
* @param opts.headers - The headers to include in the request. Note that the
807+
* Authorization header will always be set with an OAuth access token
808+
* retrieved by the client.
809+
*
810+
* @returns A promise resolving to the response from the workflow.
811+
*
812+
* @example
813+
*
814+
* ```typescript
815+
* const response = await client.invokeWorkflow(
816+
* "https://your-workflow-url.m.pipedream.net",
817+
* "your-external-user-id",
818+
* {
819+
* body: {
820+
* foo: 123,
821+
* bar: "abc",
822+
* baz: null,
823+
* },
824+
* headers: {
825+
* "Accept": "application/json",
826+
* },
827+
* },
828+
* );
829+
* console.log(response);
830+
* ```
831+
*/
832+
public async invokeWorkflowForExternalUser(url: string, externalUserId: string, opts: RequestOptions = {}): Promise<unknown> {
833+
const {
834+
body,
835+
headers = {},
836+
} = opts;
837+
838+
if (!externalUserId) {
839+
throw new Error("External user ID is required");
840+
}
841+
842+
if (!this.publicKey) {
843+
throw new Error("Project public key is required to map the external user ID to the correct project");
844+
}
845+
846+
return this.makeRequest("", {
847+
...opts,
848+
baseURL: url,
849+
method: opts.method || "POST", // Default to POST if not specified
850+
headers: {
851+
...headers,
852+
"Authorization": await this.oauthAuthorizationHeader(),
853+
"X-PD-External-User-ID": externalUserId,
854+
"X-PD-Project-Public-Key": this.publicKey,
855+
},
856+
body,
857+
});
858+
}
767859
}

0 commit comments

Comments
 (0)