@zapier/zapier-sdk 0.18.3 → 0.18.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +1 -1
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +0 -18
- package/dist/api/schemas.d.ts +0 -109
- package/dist/api/schemas.d.ts.map +1 -1
- package/dist/api/schemas.js +0 -67
- package/dist/api/types.d.ts +2 -1
- package/dist/api/types.d.ts.map +1 -1
- package/dist/index.cjs +337 -673
- package/dist/index.d.mts +52 -105
- package/dist/index.mjs +337 -673
- package/dist/plugins/listApps/index.d.ts +2 -8
- package/dist/plugins/listApps/index.d.ts.map +1 -1
- package/dist/plugins/listApps/index.js +4 -6
- package/dist/plugins/listApps/index.test.js +62 -82
- package/dist/plugins/listApps/schemas.d.ts +35 -14
- package/dist/plugins/listApps/schemas.d.ts.map +1 -1
- package/dist/plugins/listApps/schemas.js +44 -14
- package/dist/plugins/listAuthentications/index.test.js +16 -0
- package/dist/schemas/App.d.ts +28 -28
- package/dist/schemas/App.d.ts.map +1 -1
- package/dist/schemas/App.js +3 -8
- package/dist/sdk.d.ts +1 -1
- package/dist/sdk.test.js +12 -9
- package/package.json +1 -1
- package/dist/api/client.integration.test.d.ts +0 -5
- package/dist/api/client.integration.test.d.ts.map +0 -1
- package/dist/api/client.integration.test.js +0 -318
- package/dist/api/client.methods.test.d.ts +0 -2
- package/dist/api/client.methods.test.d.ts.map +0 -1
- package/dist/api/client.methods.test.js +0 -158
- package/dist/api/router.d.ts +0 -16
- package/dist/api/router.d.ts.map +0 -1
- package/dist/api/router.js +0 -31
- package/dist/api/router.test.d.ts +0 -2
- package/dist/api/router.test.d.ts.map +0 -1
- package/dist/api/router.test.js +0 -103
- package/dist/temporary-internal-core/handlers/listApps.d.ts +0 -67
- package/dist/temporary-internal-core/handlers/listApps.d.ts.map +0 -1
- package/dist/temporary-internal-core/handlers/listApps.js +0 -134
- package/dist/temporary-internal-core/handlers/listApps.test.d.ts +0 -2
- package/dist/temporary-internal-core/handlers/listApps.test.d.ts.map +0 -1
- package/dist/temporary-internal-core/handlers/listApps.test.js +0 -367
- package/dist/temporary-internal-core/index.d.ts +0 -18
- package/dist/temporary-internal-core/index.d.ts.map +0 -1
- package/dist/temporary-internal-core/index.js +0 -18
- package/dist/temporary-internal-core/schemas/apps/index.d.ts +0 -175
- package/dist/temporary-internal-core/schemas/apps/index.d.ts.map +0 -1
- package/dist/temporary-internal-core/schemas/apps/index.js +0 -97
- package/dist/temporary-internal-core/schemas/errors/index.d.ts +0 -139
- package/dist/temporary-internal-core/schemas/errors/index.d.ts.map +0 -1
- package/dist/temporary-internal-core/schemas/errors/index.js +0 -129
- package/dist/temporary-internal-core/schemas/implementations/index.d.ts +0 -127
- package/dist/temporary-internal-core/schemas/implementations/index.d.ts.map +0 -1
- package/dist/temporary-internal-core/schemas/implementations/index.js +0 -79
- package/dist/temporary-internal-core/types/handler.d.ts +0 -51
- package/dist/temporary-internal-core/types/handler.d.ts.map +0 -1
- package/dist/temporary-internal-core/types/handler.js +0 -8
- package/dist/temporary-internal-core/types/index.d.ts +0 -5
- package/dist/temporary-internal-core/types/index.d.ts.map +0 -1
- package/dist/temporary-internal-core/types/index.js +0 -4
- package/dist/temporary-internal-core/utils/app-locators.d.ts +0 -34
- package/dist/temporary-internal-core/utils/app-locators.d.ts.map +0 -1
- package/dist/temporary-internal-core/utils/app-locators.js +0 -39
- package/dist/temporary-internal-core/utils/string-utils.d.ts +0 -28
- package/dist/temporary-internal-core/utils/string-utils.d.ts.map +0 -1
- package/dist/temporary-internal-core/utils/string-utils.js +0 -52
- package/dist/temporary-internal-core/utils/transformations.d.ts +0 -18
- package/dist/temporary-internal-core/utils/transformations.d.ts.map +0 -1
- package/dist/temporary-internal-core/utils/transformations.js +0 -36
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Handler for listApps operation
|
|
3
|
-
*
|
|
4
|
-
* This handler will become an SDK API endpoint handler.
|
|
5
|
-
* It encapsulates the business logic for listing apps, including:
|
|
6
|
-
* - Search augmentation (merging search results with specified apps)
|
|
7
|
-
* - API calls to internal services
|
|
8
|
-
* - Response transformation
|
|
9
|
-
* - Pagination with latest_only support
|
|
10
|
-
*
|
|
11
|
-
* Note: App key resolution (slugs → implementation IDs) happens in the SDK plugin.
|
|
12
|
-
* The handler receives pre-resolved implementation IDs and performs search augmentation.
|
|
13
|
-
*/
|
|
14
|
-
import type { Handler, HandlerDeps } from "../types";
|
|
15
|
-
import { type ListAppsHandlerRequest, type ListAppsResponse } from "../schemas/apps";
|
|
16
|
-
/**
|
|
17
|
-
* Simple HTTP client interface for calling internal APIs
|
|
18
|
-
*
|
|
19
|
-
* Why pass this as a dependency instead of importing directly?
|
|
20
|
-
*
|
|
21
|
-
* 1. **Different implementations in different contexts:**
|
|
22
|
-
* - In the SDK: Uses the ApiClient wrapper (adds auth, error handling, etc.)
|
|
23
|
-
* - In the SDK API: Would use internal HTTP client or direct service calls
|
|
24
|
-
*
|
|
25
|
-
* 2. **Testability:**
|
|
26
|
-
* - Can mock httpClient in tests without complex setup
|
|
27
|
-
* - Can verify API calls and responses in isolation
|
|
28
|
-
*
|
|
29
|
-
* 3. **No hard coupling:**
|
|
30
|
-
* - Handler doesn't know/care how HTTP calls are made
|
|
31
|
-
* - Makes migration to SDK API easier (just swap implementation)
|
|
32
|
-
*/
|
|
33
|
-
export interface HttpClient {
|
|
34
|
-
get<T = unknown>(path: string, options?: {
|
|
35
|
-
searchParams?: Record<string, string>;
|
|
36
|
-
}): Promise<T>;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Dependencies required by the listApps handler
|
|
40
|
-
*
|
|
41
|
-
* Passed as a parameter (dependency injection) rather than imported directly
|
|
42
|
-
* to allow different implementations in different environments:
|
|
43
|
-
* - SDK plugin injects: { httpClient: api } (SDK's ApiClient)
|
|
44
|
-
* - SDK API would inject: { httpClient: internalHttpClient } (direct internal calls)
|
|
45
|
-
* - Tests inject: { httpClient: mockHttpClient } (mocked responses)
|
|
46
|
-
*
|
|
47
|
-
* Extends HandlerDeps to ensure compatibility with the Handler interface.
|
|
48
|
-
*/
|
|
49
|
-
export interface ListAppsHandlerDeps extends HandlerDeps {
|
|
50
|
-
httpClient: HttpClient;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Handles listApps operation
|
|
54
|
-
*
|
|
55
|
-
* Conforms to the Handler<TRequest, TResponse, TDeps> interface contract.
|
|
56
|
-
* Validates and normalizes the request at the handler boundary before processing.
|
|
57
|
-
*
|
|
58
|
-
* Accepts flexible input types (via schema):
|
|
59
|
-
* - implementationIds: string (comma-separated) or string[]
|
|
60
|
-
* - pageSize: string or number
|
|
61
|
-
*
|
|
62
|
-
* @param request - The request with pre-resolved implementation IDs (handler performs search augmentation)
|
|
63
|
-
* @param deps - Dependencies injected by the caller (see ListAppsHandlerDeps for why we use DI)
|
|
64
|
-
* @returns Paginated list of apps with normalized data
|
|
65
|
-
*/
|
|
66
|
-
export declare const handleListApps: Handler<ListAppsHandlerRequest, ListAppsResponse, ListAppsHandlerDeps>;
|
|
67
|
-
//# sourceMappingURL=listApps.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"listApps.d.ts","sourceRoot":"","sources":["../../../src/temporary-internal-core/handlers/listApps.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACrD,OAAO,EAEL,KAAK,sBAAsB,EAC3B,KAAK,gBAAgB,EACtB,MAAM,iBAAiB,CAAC;AAkBzB;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,CAAC,GAAG,OAAO,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,GAClD,OAAO,CAAC,CAAC,CAAC,CAAC;CACf;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,mBAAoB,SAAQ,WAAW;IACtD,UAAU,EAAE,UAAU,CAAC;CACxB;AA2ED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc,EAAE,OAAO,CAClC,sBAAsB,EACtB,gBAAgB,EAChB,mBAAmB,CA+EpB,CAAC"}
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Handler for listApps operation
|
|
3
|
-
*
|
|
4
|
-
* This handler will become an SDK API endpoint handler.
|
|
5
|
-
* It encapsulates the business logic for listing apps, including:
|
|
6
|
-
* - Search augmentation (merging search results with specified apps)
|
|
7
|
-
* - API calls to internal services
|
|
8
|
-
* - Response transformation
|
|
9
|
-
* - Pagination with latest_only support
|
|
10
|
-
*
|
|
11
|
-
* Note: App key resolution (slugs → implementation IDs) happens in the SDK plugin.
|
|
12
|
-
* The handler receives pre-resolved implementation IDs and performs search augmentation.
|
|
13
|
-
*/
|
|
14
|
-
import { ListAppsHandlerRequestSchema, } from "../schemas/apps";
|
|
15
|
-
import { splitVersionedKey } from "../utils/string-utils";
|
|
16
|
-
import { transformImplementationMetaToAppItem, extractPaginationCursor, } from "../utils/transformations";
|
|
17
|
-
// ============================================================================
|
|
18
|
-
// Constants
|
|
19
|
-
// ============================================================================
|
|
20
|
-
const DEFAULT_PAGE_SIZE = 20;
|
|
21
|
-
// ============================================================================
|
|
22
|
-
// Business Logic Helpers
|
|
23
|
-
// ============================================================================
|
|
24
|
-
/**
|
|
25
|
-
* Augments implementation IDs with search results
|
|
26
|
-
*
|
|
27
|
-
* Calls the search endpoint and merges results with existing implementation IDs,
|
|
28
|
-
* deduplicating by implementation name (without version) and preferring public versions.
|
|
29
|
-
*/
|
|
30
|
-
async function augmentWithSearchResults({ searchTerm, implementationIds, httpClient, }) {
|
|
31
|
-
const searchResponse = await httpClient.get("/zapier/api/v4/implementations-meta/search/", {
|
|
32
|
-
searchParams: { term: searchTerm },
|
|
33
|
-
});
|
|
34
|
-
// Deduplicate by implementation name, preferring public versions
|
|
35
|
-
// This ensures that when a user has access to both a private version (e.g., 1.0.17)
|
|
36
|
-
// and a public version (e.g., 1.1.0), we use the public one for search results
|
|
37
|
-
const byImplementationName = new Map();
|
|
38
|
-
for (const result of searchResponse.results) {
|
|
39
|
-
const [implementationName] = splitVersionedKey(result.id);
|
|
40
|
-
const isPublic = result.visibility === "public";
|
|
41
|
-
const existing = byImplementationName.get(implementationName);
|
|
42
|
-
// Take this result if we don't have one yet, or if this one is public and existing is not
|
|
43
|
-
if (!existing || (isPublic && !existing.isPublic)) {
|
|
44
|
-
byImplementationName.set(implementationName, { result, isPublic });
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
const searchResults = Array.from(byImplementationName.values()).map((entry) => transformImplementationMetaToAppItem(entry.result));
|
|
48
|
-
const implementationNameSet = new Set(implementationIds.map((id) => {
|
|
49
|
-
const [name] = splitVersionedKey(id);
|
|
50
|
-
return name;
|
|
51
|
-
}));
|
|
52
|
-
const additionalIds = [];
|
|
53
|
-
for (const result of searchResults) {
|
|
54
|
-
const [implementationName] = splitVersionedKey(result.key);
|
|
55
|
-
if (!implementationNameSet.has(implementationName)) {
|
|
56
|
-
implementationNameSet.add(implementationName);
|
|
57
|
-
// Use the full implementation_id from the result
|
|
58
|
-
additionalIds.push(result.implementation_id);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return [...implementationIds, ...additionalIds];
|
|
62
|
-
}
|
|
63
|
-
// ============================================================================
|
|
64
|
-
// Main Handler
|
|
65
|
-
// ============================================================================
|
|
66
|
-
/**
|
|
67
|
-
* Handles listApps operation
|
|
68
|
-
*
|
|
69
|
-
* Conforms to the Handler<TRequest, TResponse, TDeps> interface contract.
|
|
70
|
-
* Validates and normalizes the request at the handler boundary before processing.
|
|
71
|
-
*
|
|
72
|
-
* Accepts flexible input types (via schema):
|
|
73
|
-
* - implementationIds: string (comma-separated) or string[]
|
|
74
|
-
* - pageSize: string or number
|
|
75
|
-
*
|
|
76
|
-
* @param request - The request with pre-resolved implementation IDs (handler performs search augmentation)
|
|
77
|
-
* @param deps - Dependencies injected by the caller (see ListAppsHandlerDeps for why we use DI)
|
|
78
|
-
* @returns Paginated list of apps with normalized data
|
|
79
|
-
*/
|
|
80
|
-
export const handleListApps = async ({ request, deps }) => {
|
|
81
|
-
// Validate and normalize request at handler boundary
|
|
82
|
-
// Schema automatically normalizes:
|
|
83
|
-
// - implementationIds: string → string[]
|
|
84
|
-
// - pageSize: string → number
|
|
85
|
-
const validatedRequest = ListAppsHandlerRequestSchema.parse(request);
|
|
86
|
-
const { httpClient } = deps;
|
|
87
|
-
let { implementationIds } = validatedRequest;
|
|
88
|
-
const pageSize = validatedRequest.pageSize ?? DEFAULT_PAGE_SIZE;
|
|
89
|
-
if (validatedRequest.search) {
|
|
90
|
-
implementationIds = await augmentWithSearchResults({
|
|
91
|
-
searchTerm: validatedRequest.search,
|
|
92
|
-
implementationIds,
|
|
93
|
-
httpClient,
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
if (implementationIds.length === 0) {
|
|
97
|
-
// If search was provided but found nothing, return empty (bug fix)
|
|
98
|
-
if (validatedRequest.search) {
|
|
99
|
-
return {
|
|
100
|
-
data: [],
|
|
101
|
-
nextCursor: undefined,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
const searchParams = {
|
|
105
|
-
latest_only: "true",
|
|
106
|
-
selected_apis: "",
|
|
107
|
-
limit: pageSize.toString(),
|
|
108
|
-
};
|
|
109
|
-
if (validatedRequest.cursor) {
|
|
110
|
-
searchParams.offset = validatedRequest.cursor;
|
|
111
|
-
}
|
|
112
|
-
const implementationsResponse = await httpClient.get("/zapier/api/v4/implementations-meta/lookup/", {
|
|
113
|
-
searchParams,
|
|
114
|
-
});
|
|
115
|
-
return {
|
|
116
|
-
data: implementationsResponse.results.map(transformImplementationMetaToAppItem),
|
|
117
|
-
nextCursor: extractPaginationCursor(implementationsResponse),
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
const searchParams = {
|
|
121
|
-
selected_apis: implementationIds.join(","),
|
|
122
|
-
limit: pageSize.toString(),
|
|
123
|
-
};
|
|
124
|
-
if (validatedRequest.cursor) {
|
|
125
|
-
searchParams.offset = validatedRequest.cursor;
|
|
126
|
-
}
|
|
127
|
-
const implementationsResponse = await httpClient.get("/zapier/api/v4/implementations-meta/lookup/", {
|
|
128
|
-
searchParams,
|
|
129
|
-
});
|
|
130
|
-
return {
|
|
131
|
-
data: implementationsResponse.results.map(transformImplementationMetaToAppItem),
|
|
132
|
-
nextCursor: extractPaginationCursor(implementationsResponse),
|
|
133
|
-
};
|
|
134
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"listApps.test.d.ts","sourceRoot":"","sources":["../../../src/temporary-internal-core/handlers/listApps.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,367 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import { handleListApps, } from "./listApps";
|
|
3
|
-
import { ListAppsResponseSchema, } from "../schemas/apps";
|
|
4
|
-
describe("handleListApps", () => {
|
|
5
|
-
const mockImplementation = (id, name, visibility = "public") => ({
|
|
6
|
-
id,
|
|
7
|
-
name,
|
|
8
|
-
slug: name.toLowerCase().replace(/\s+/g, "-"),
|
|
9
|
-
description: `${name} description`,
|
|
10
|
-
primary_color: "#000000",
|
|
11
|
-
categories: [{ id: 1, name: "test", slug: "test" }],
|
|
12
|
-
visibility,
|
|
13
|
-
});
|
|
14
|
-
describe("pageSize handling", () => {
|
|
15
|
-
it("should use default pageSize of 20 when not provided", async () => {
|
|
16
|
-
const mockHttpClient = {
|
|
17
|
-
get: vi.fn().mockResolvedValue({
|
|
18
|
-
count: 0,
|
|
19
|
-
results: [],
|
|
20
|
-
}),
|
|
21
|
-
};
|
|
22
|
-
await handleListApps({
|
|
23
|
-
request: {
|
|
24
|
-
implementationIds: [],
|
|
25
|
-
search: undefined,
|
|
26
|
-
pageSize: undefined,
|
|
27
|
-
cursor: undefined,
|
|
28
|
-
},
|
|
29
|
-
deps: { httpClient: mockHttpClient },
|
|
30
|
-
});
|
|
31
|
-
expect(mockHttpClient.get).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
|
|
32
|
-
searchParams: expect.objectContaining({
|
|
33
|
-
limit: "20",
|
|
34
|
-
}),
|
|
35
|
-
}));
|
|
36
|
-
});
|
|
37
|
-
it("should use custom pageSize when provided", async () => {
|
|
38
|
-
const mockHttpClient = {
|
|
39
|
-
get: vi.fn().mockResolvedValue({
|
|
40
|
-
count: 0,
|
|
41
|
-
results: [],
|
|
42
|
-
}),
|
|
43
|
-
};
|
|
44
|
-
await handleListApps({
|
|
45
|
-
request: {
|
|
46
|
-
implementationIds: [],
|
|
47
|
-
search: undefined,
|
|
48
|
-
pageSize: 50,
|
|
49
|
-
cursor: undefined,
|
|
50
|
-
},
|
|
51
|
-
deps: { httpClient: mockHttpClient },
|
|
52
|
-
});
|
|
53
|
-
expect(mockHttpClient.get).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
|
|
54
|
-
searchParams: expect.objectContaining({
|
|
55
|
-
limit: "50",
|
|
56
|
-
}),
|
|
57
|
-
}));
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
describe("empty implementation IDs handling", () => {
|
|
61
|
-
it("should fetch all apps when no implementationIds and no search", async () => {
|
|
62
|
-
const mockResponse = {
|
|
63
|
-
count: 2,
|
|
64
|
-
results: [
|
|
65
|
-
mockImplementation("SlackCLIAPI@1.0.0", "Slack"),
|
|
66
|
-
mockImplementation("GitHubCLIAPI@1.0.0", "GitHub"),
|
|
67
|
-
],
|
|
68
|
-
};
|
|
69
|
-
const mockHttpClient = {
|
|
70
|
-
get: vi.fn().mockResolvedValue(mockResponse),
|
|
71
|
-
};
|
|
72
|
-
const result = await handleListApps({
|
|
73
|
-
request: {
|
|
74
|
-
implementationIds: [],
|
|
75
|
-
search: undefined,
|
|
76
|
-
pageSize: undefined,
|
|
77
|
-
cursor: undefined,
|
|
78
|
-
},
|
|
79
|
-
deps: { httpClient: mockHttpClient },
|
|
80
|
-
});
|
|
81
|
-
expect(result.data).toHaveLength(2);
|
|
82
|
-
expect(mockHttpClient.get).toHaveBeenCalledWith("/zapier/api/v4/implementations-meta/lookup/", expect.objectContaining({
|
|
83
|
-
searchParams: expect.objectContaining({
|
|
84
|
-
latest_only: "true",
|
|
85
|
-
selected_apis: "",
|
|
86
|
-
}),
|
|
87
|
-
}));
|
|
88
|
-
});
|
|
89
|
-
it("should return empty when search finds nothing (bug fix)", async () => {
|
|
90
|
-
const mockSearchResponse = {
|
|
91
|
-
count: 0,
|
|
92
|
-
results: [],
|
|
93
|
-
};
|
|
94
|
-
const mockHttpClient = {
|
|
95
|
-
get: vi.fn().mockResolvedValue(mockSearchResponse),
|
|
96
|
-
};
|
|
97
|
-
const result = await handleListApps({
|
|
98
|
-
request: {
|
|
99
|
-
implementationIds: [],
|
|
100
|
-
search: "NonexistentApp",
|
|
101
|
-
pageSize: undefined,
|
|
102
|
-
cursor: undefined,
|
|
103
|
-
},
|
|
104
|
-
deps: { httpClient: mockHttpClient },
|
|
105
|
-
});
|
|
106
|
-
// Should only call search endpoint
|
|
107
|
-
expect(mockHttpClient.get).toHaveBeenCalledTimes(1);
|
|
108
|
-
expect(mockHttpClient.get).toHaveBeenCalledWith("/zapier/api/v4/implementations-meta/search/", expect.objectContaining({
|
|
109
|
-
searchParams: { term: "NonexistentApp" },
|
|
110
|
-
}));
|
|
111
|
-
// Should return empty (bug fix - not all apps)
|
|
112
|
-
expect(result.data).toHaveLength(0);
|
|
113
|
-
expect(result.nextCursor).toBeUndefined();
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
describe("search augmentation", () => {
|
|
117
|
-
it("should augment results with search when search term provided", async () => {
|
|
118
|
-
const mockSearchResponse = {
|
|
119
|
-
count: 1,
|
|
120
|
-
results: [mockImplementation("SlackCLIAPI@1.0.0", "Slack")],
|
|
121
|
-
};
|
|
122
|
-
const mockLookupResponse = {
|
|
123
|
-
count: 2,
|
|
124
|
-
results: [
|
|
125
|
-
mockImplementation("GitHubCLIAPI@1.0.0", "GitHub"),
|
|
126
|
-
mockImplementation("SlackCLIAPI@1.0.0", "Slack"),
|
|
127
|
-
],
|
|
128
|
-
next: null,
|
|
129
|
-
};
|
|
130
|
-
const mockHttpClient = {
|
|
131
|
-
get: vi
|
|
132
|
-
.fn()
|
|
133
|
-
.mockResolvedValueOnce(mockSearchResponse) // First call: search
|
|
134
|
-
.mockResolvedValueOnce(mockLookupResponse), // Second call: lookup
|
|
135
|
-
};
|
|
136
|
-
const result = await handleListApps({
|
|
137
|
-
request: {
|
|
138
|
-
implementationIds: ["GitHubCLIAPI@1.0.0"],
|
|
139
|
-
search: "Slack",
|
|
140
|
-
pageSize: undefined,
|
|
141
|
-
cursor: undefined,
|
|
142
|
-
},
|
|
143
|
-
deps: { httpClient: mockHttpClient },
|
|
144
|
-
});
|
|
145
|
-
// Should call both search and lookup
|
|
146
|
-
expect(mockHttpClient.get).toHaveBeenCalledTimes(2);
|
|
147
|
-
expect(mockHttpClient.get).toHaveBeenNthCalledWith(1, "/zapier/api/v4/implementations-meta/search/", expect.objectContaining({
|
|
148
|
-
searchParams: { term: "Slack" },
|
|
149
|
-
}));
|
|
150
|
-
expect(mockHttpClient.get).toHaveBeenNthCalledWith(2, "/zapier/api/v4/implementations-meta/lookup/", expect.objectContaining({
|
|
151
|
-
searchParams: expect.objectContaining({
|
|
152
|
-
selected_apis: "GitHubCLIAPI@1.0.0,SlackCLIAPI@1.0.0",
|
|
153
|
-
}),
|
|
154
|
-
}));
|
|
155
|
-
expect(result.data).toHaveLength(2);
|
|
156
|
-
});
|
|
157
|
-
it("should not duplicate apps when search returns apps already in implementationIds", async () => {
|
|
158
|
-
const mockSearchResponse = {
|
|
159
|
-
count: 1,
|
|
160
|
-
results: [mockImplementation("SlackCLIAPI@1.0.0", "Slack")],
|
|
161
|
-
};
|
|
162
|
-
const mockLookupResponse = {
|
|
163
|
-
count: 1,
|
|
164
|
-
results: [mockImplementation("SlackCLIAPI@1.0.0", "Slack")],
|
|
165
|
-
next: null,
|
|
166
|
-
};
|
|
167
|
-
const mockHttpClient = {
|
|
168
|
-
get: vi
|
|
169
|
-
.fn()
|
|
170
|
-
.mockResolvedValueOnce(mockSearchResponse)
|
|
171
|
-
.mockResolvedValueOnce(mockLookupResponse),
|
|
172
|
-
};
|
|
173
|
-
await handleListApps({
|
|
174
|
-
request: {
|
|
175
|
-
implementationIds: ["SlackCLIAPI@1.0.0"],
|
|
176
|
-
search: "Slack",
|
|
177
|
-
pageSize: undefined,
|
|
178
|
-
cursor: undefined,
|
|
179
|
-
},
|
|
180
|
-
deps: { httpClient: mockHttpClient },
|
|
181
|
-
});
|
|
182
|
-
// Should call search and lookup but deduplicate IDs
|
|
183
|
-
expect(mockHttpClient.get).toHaveBeenCalledTimes(2);
|
|
184
|
-
expect(mockHttpClient.get).toHaveBeenNthCalledWith(2, "/zapier/api/v4/implementations-meta/lookup/", expect.objectContaining({
|
|
185
|
-
searchParams: expect.objectContaining({
|
|
186
|
-
selected_apis: "SlackCLIAPI@1.0.0",
|
|
187
|
-
}),
|
|
188
|
-
}));
|
|
189
|
-
});
|
|
190
|
-
it("should prefer public version when search returns multiple versions of same app", async () => {
|
|
191
|
-
// Simulates a user having access to both private (1.0.17) and public (1.1.0) versions
|
|
192
|
-
const mockSearchResponse = {
|
|
193
|
-
count: 2,
|
|
194
|
-
results: [
|
|
195
|
-
mockImplementation("LumAppsCLIAPI@1.0.17", "LumApps (1.0.17)", "private"),
|
|
196
|
-
mockImplementation("LumAppsCLIAPI@1.1.0", "LumApps", "public"),
|
|
197
|
-
],
|
|
198
|
-
};
|
|
199
|
-
const mockLookupResponse = {
|
|
200
|
-
count: 1,
|
|
201
|
-
results: [
|
|
202
|
-
mockImplementation("LumAppsCLIAPI@1.1.0", "LumApps", "public"),
|
|
203
|
-
],
|
|
204
|
-
next: null,
|
|
205
|
-
};
|
|
206
|
-
const mockHttpClient = {
|
|
207
|
-
get: vi
|
|
208
|
-
.fn()
|
|
209
|
-
.mockResolvedValueOnce(mockSearchResponse)
|
|
210
|
-
.mockResolvedValueOnce(mockLookupResponse),
|
|
211
|
-
};
|
|
212
|
-
await handleListApps({
|
|
213
|
-
request: {
|
|
214
|
-
implementationIds: [],
|
|
215
|
-
search: "lumapps",
|
|
216
|
-
pageSize: undefined,
|
|
217
|
-
cursor: undefined,
|
|
218
|
-
},
|
|
219
|
-
deps: { httpClient: mockHttpClient },
|
|
220
|
-
});
|
|
221
|
-
// Should request the public version (1.1.0), not the private one (1.0.17)
|
|
222
|
-
expect(mockHttpClient.get).toHaveBeenNthCalledWith(2, "/zapier/api/v4/implementations-meta/lookup/", expect.objectContaining({
|
|
223
|
-
searchParams: expect.objectContaining({
|
|
224
|
-
selected_apis: "LumAppsCLIAPI@1.1.0",
|
|
225
|
-
}),
|
|
226
|
-
}));
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
describe("specific implementation IDs", () => {
|
|
230
|
-
it("should fetch specific apps when implementationIds provided", async () => {
|
|
231
|
-
const mockResponse = {
|
|
232
|
-
count: 2,
|
|
233
|
-
results: [
|
|
234
|
-
mockImplementation("SlackCLIAPI@1.0.0", "Slack"),
|
|
235
|
-
mockImplementation("GitHubCLIAPI@1.0.0", "GitHub"),
|
|
236
|
-
],
|
|
237
|
-
};
|
|
238
|
-
const mockHttpClient = {
|
|
239
|
-
get: vi.fn().mockResolvedValue(mockResponse),
|
|
240
|
-
};
|
|
241
|
-
const result = await handleListApps({
|
|
242
|
-
request: {
|
|
243
|
-
implementationIds: ["SlackCLIAPI@1.0.0", "GitHubCLIAPI@1.0.0"],
|
|
244
|
-
search: undefined,
|
|
245
|
-
pageSize: undefined,
|
|
246
|
-
cursor: undefined,
|
|
247
|
-
},
|
|
248
|
-
deps: { httpClient: mockHttpClient },
|
|
249
|
-
});
|
|
250
|
-
expect(result.data).toHaveLength(2);
|
|
251
|
-
expect(mockHttpClient.get).toHaveBeenCalledWith("/zapier/api/v4/implementations-meta/lookup/", expect.objectContaining({
|
|
252
|
-
searchParams: expect.objectContaining({
|
|
253
|
-
selected_apis: "SlackCLIAPI@1.0.0,GitHubCLIAPI@1.0.0",
|
|
254
|
-
}),
|
|
255
|
-
}));
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
describe("pagination", () => {
|
|
259
|
-
it("should pass cursor to API when provided", async () => {
|
|
260
|
-
const mockResponse = {
|
|
261
|
-
count: 0,
|
|
262
|
-
results: [],
|
|
263
|
-
};
|
|
264
|
-
const mockHttpClient = {
|
|
265
|
-
get: vi.fn().mockResolvedValue(mockResponse),
|
|
266
|
-
};
|
|
267
|
-
await handleListApps({
|
|
268
|
-
request: {
|
|
269
|
-
implementationIds: ["SlackCLIAPI@1.0.0"],
|
|
270
|
-
search: undefined,
|
|
271
|
-
pageSize: undefined,
|
|
272
|
-
cursor: "page-2-cursor",
|
|
273
|
-
},
|
|
274
|
-
deps: { httpClient: mockHttpClient },
|
|
275
|
-
});
|
|
276
|
-
expect(mockHttpClient.get).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
|
|
277
|
-
searchParams: expect.objectContaining({
|
|
278
|
-
offset: "page-2-cursor",
|
|
279
|
-
}),
|
|
280
|
-
}));
|
|
281
|
-
});
|
|
282
|
-
it("should extract nextCursor from response", async () => {
|
|
283
|
-
const mockResponse = {
|
|
284
|
-
count: 1,
|
|
285
|
-
results: [mockImplementation("SlackCLIAPI@1.0.0", "Slack")],
|
|
286
|
-
next: "https://api.zapier.com/v4/implementations-meta/lookup/?offset=next-page",
|
|
287
|
-
};
|
|
288
|
-
const mockHttpClient = {
|
|
289
|
-
get: vi.fn().mockResolvedValue(mockResponse),
|
|
290
|
-
};
|
|
291
|
-
const result = await handleListApps({
|
|
292
|
-
request: {
|
|
293
|
-
implementationIds: [],
|
|
294
|
-
search: undefined,
|
|
295
|
-
pageSize: undefined,
|
|
296
|
-
cursor: undefined,
|
|
297
|
-
},
|
|
298
|
-
deps: { httpClient: mockHttpClient },
|
|
299
|
-
});
|
|
300
|
-
expect(result.nextCursor).toBe("next-page");
|
|
301
|
-
});
|
|
302
|
-
});
|
|
303
|
-
describe("request validation", () => {
|
|
304
|
-
it("should reject request with invalid implementationIds type", async () => {
|
|
305
|
-
const mockHttpClient = {
|
|
306
|
-
get: vi.fn(),
|
|
307
|
-
};
|
|
308
|
-
const invalidRequest = {
|
|
309
|
-
implementationIds: "not-an-array",
|
|
310
|
-
search: undefined,
|
|
311
|
-
pageSize: undefined,
|
|
312
|
-
cursor: undefined,
|
|
313
|
-
};
|
|
314
|
-
const deps = {
|
|
315
|
-
httpClient: mockHttpClient,
|
|
316
|
-
};
|
|
317
|
-
await expect(handleListApps({ request: invalidRequest, deps })).rejects.toThrow();
|
|
318
|
-
});
|
|
319
|
-
it("should reject request with negative pageSize", async () => {
|
|
320
|
-
const mockHttpClient = {
|
|
321
|
-
get: vi.fn(),
|
|
322
|
-
};
|
|
323
|
-
const invalidRequest = {
|
|
324
|
-
implementationIds: [],
|
|
325
|
-
search: undefined,
|
|
326
|
-
pageSize: -1,
|
|
327
|
-
cursor: undefined,
|
|
328
|
-
};
|
|
329
|
-
const deps = {
|
|
330
|
-
httpClient: mockHttpClient,
|
|
331
|
-
};
|
|
332
|
-
await expect(handleListApps({ request: invalidRequest, deps })).rejects.toThrow();
|
|
333
|
-
});
|
|
334
|
-
});
|
|
335
|
-
describe("response validation", () => {
|
|
336
|
-
it("should return response matching ListAppsResponseSchema", async () => {
|
|
337
|
-
const mockHttpClient = {
|
|
338
|
-
get: vi.fn().mockResolvedValue({
|
|
339
|
-
count: 1,
|
|
340
|
-
results: [
|
|
341
|
-
{
|
|
342
|
-
id: "SlackCLIAPI@1.0.0",
|
|
343
|
-
name: "SlackCLIAPI",
|
|
344
|
-
slug: "slack",
|
|
345
|
-
description: "Slack app",
|
|
346
|
-
primary_color: "#000000",
|
|
347
|
-
},
|
|
348
|
-
],
|
|
349
|
-
}),
|
|
350
|
-
};
|
|
351
|
-
const request = {
|
|
352
|
-
implementationIds: [],
|
|
353
|
-
search: undefined,
|
|
354
|
-
pageSize: 20,
|
|
355
|
-
cursor: undefined,
|
|
356
|
-
};
|
|
357
|
-
const deps = {
|
|
358
|
-
httpClient: mockHttpClient,
|
|
359
|
-
};
|
|
360
|
-
const result = await handleListApps({ request, deps });
|
|
361
|
-
expect(() => ListAppsResponseSchema.parse(result)).not.toThrow();
|
|
362
|
-
expect(result).toHaveProperty("data");
|
|
363
|
-
expect(Array.isArray(result.data)).toBe(true);
|
|
364
|
-
expect(result).toHaveProperty("nextCursor");
|
|
365
|
-
});
|
|
366
|
-
});
|
|
367
|
-
});
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* temporary-internal-core
|
|
3
|
-
*
|
|
4
|
-
* Temporary internal directory for SDK core API extraction.
|
|
5
|
-
* This will be extracted and moved to @sdkapi/ as @zapier/zapier-sdk-core.
|
|
6
|
-
*
|
|
7
|
-
* Purpose:
|
|
8
|
-
* - Single source of truth for SDK API schemas
|
|
9
|
-
* - Zod schemas for request/response shapes
|
|
10
|
-
* - API endpoint contracts
|
|
11
|
-
* - Lives inside zapier-sdk during extraction/migration (bundles when published)
|
|
12
|
-
* - Will be extracted to separate package
|
|
13
|
-
*/
|
|
14
|
-
export type { HandlerDeps, Handler, ValidatedHandler } from "./types";
|
|
15
|
-
export * from "./schemas/apps";
|
|
16
|
-
export * from "./schemas/implementations";
|
|
17
|
-
export { handleListApps, type ListAppsHandlerDeps } from "./handlers/listApps";
|
|
18
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/temporary-internal-core/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAGtE,cAAc,gBAAgB,CAAC;AAC/B,cAAc,2BAA2B,CAAC;AAG1C,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* temporary-internal-core
|
|
3
|
-
*
|
|
4
|
-
* Temporary internal directory for SDK core API extraction.
|
|
5
|
-
* This will be extracted and moved to @sdkapi/ as @zapier/zapier-sdk-core.
|
|
6
|
-
*
|
|
7
|
-
* Purpose:
|
|
8
|
-
* - Single source of truth for SDK API schemas
|
|
9
|
-
* - Zod schemas for request/response shapes
|
|
10
|
-
* - API endpoint contracts
|
|
11
|
-
* - Lives inside zapier-sdk during extraction/migration (bundles when published)
|
|
12
|
-
* - Will be extracted to separate package
|
|
13
|
-
*/
|
|
14
|
-
// Schemas
|
|
15
|
-
export * from "./schemas/apps";
|
|
16
|
-
export * from "./schemas/implementations";
|
|
17
|
-
// Handlers
|
|
18
|
-
export { handleListApps } from "./handlers/listApps";
|