@superblocksteam/sdk-api 2.0.119 → 2.0.120-next.1
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/dist/integrations/base/graphql-integration-client.d.ts +6 -2
- package/dist/integrations/base/graphql-integration-client.d.ts.map +1 -1
- package/dist/integrations/base/graphql-integration-client.js +18 -5
- package/dist/integrations/base/graphql-integration-client.js.map +1 -1
- package/dist/integrations/documentation-resolver.test.js +143 -1
- package/dist/integrations/documentation-resolver.test.js.map +1 -1
- package/dist/integrations/documentation.d.ts +1 -0
- package/dist/integrations/documentation.d.ts.map +1 -1
- package/dist/integrations/documentation.js +31 -8
- package/dist/integrations/documentation.js.map +1 -1
- package/dist/integrations/graphql/client.test.d.ts +15 -0
- package/dist/integrations/graphql/client.test.d.ts.map +1 -0
- package/dist/integrations/graphql/client.test.js +148 -0
- package/dist/integrations/graphql/client.test.js.map +1 -0
- package/dist/integrations/graphql/types.d.ts +21 -2
- package/dist/integrations/graphql/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/integrations/base/graphql-integration-client.ts +24 -3
- package/src/integrations/documentation-resolver.test.ts +159 -1
- package/src/integrations/documentation.ts +63 -11
- package/src/integrations/graphql/client.test.ts +220 -0
- package/src/integrations/graphql/docs.manifest.json +6 -1
- package/src/integrations/graphql/overlays/dynamic-headers.md +34 -0
- package/src/integrations/graphql/types.ts +21 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for GraphQLClientImpl.
|
|
3
|
+
*
|
|
4
|
+
* Validates:
|
|
5
|
+
* - The proto request built by query()/mutation() matches the
|
|
6
|
+
* graphql.v1.Plugin shape expected by the orchestrator.
|
|
7
|
+
* - Optional per-request headers are forwarded as the proto's repeated
|
|
8
|
+
* `headers` field (key/value Property entries) so dynamic values like
|
|
9
|
+
* Authorization tokens can be sent from API code.
|
|
10
|
+
* - Variables are serialized into the proto `custom.variables` Property.
|
|
11
|
+
* - Trace metadata is passed through to the executeQuery callback.
|
|
12
|
+
* - Response Zod validation throws RestApiValidationError on mismatch.
|
|
13
|
+
*/
|
|
14
|
+
import { describe, it, expect, vi } from "vitest";
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
import { RestApiValidationError } from "../../errors.js";
|
|
17
|
+
import { GraphQLClientImpl } from "./client.js";
|
|
18
|
+
const TEST_CONFIG = {
|
|
19
|
+
id: "graphql-test-id",
|
|
20
|
+
name: "Test GraphQL",
|
|
21
|
+
pluginId: "graphqlintegration",
|
|
22
|
+
configuration: {},
|
|
23
|
+
};
|
|
24
|
+
function createClient(mockResult) {
|
|
25
|
+
const executeQuery = vi.fn().mockResolvedValue(mockResult);
|
|
26
|
+
const client = new GraphQLClientImpl(TEST_CONFIG, executeQuery);
|
|
27
|
+
return { client, executeQuery };
|
|
28
|
+
}
|
|
29
|
+
const UserResponseSchema = z.object({
|
|
30
|
+
data: z.object({
|
|
31
|
+
user: z.object({
|
|
32
|
+
id: z.string(),
|
|
33
|
+
name: z.string(),
|
|
34
|
+
}),
|
|
35
|
+
}),
|
|
36
|
+
});
|
|
37
|
+
const SAMPLE_QUERY = `query GetUser($id: ID!) {
|
|
38
|
+
user(id: $id) { id name }
|
|
39
|
+
}`;
|
|
40
|
+
const SAMPLE_MUTATION = `mutation CreateUser($name: String!) {
|
|
41
|
+
createUser(name: $name) { id name }
|
|
42
|
+
}`;
|
|
43
|
+
describe("GraphQLClientImpl", () => {
|
|
44
|
+
describe("query()", () => {
|
|
45
|
+
it("returns validated data on a successful response", async () => {
|
|
46
|
+
const { client } = createClient({
|
|
47
|
+
data: { user: { id: "u1", name: "Alice" } },
|
|
48
|
+
});
|
|
49
|
+
const result = await client.query(SAMPLE_QUERY, { response: UserResponseSchema }, { id: "u1" });
|
|
50
|
+
expect(result.data.user).toEqual({ id: "u1", name: "Alice" });
|
|
51
|
+
});
|
|
52
|
+
it("builds the proto request with body, variables, and defaults", async () => {
|
|
53
|
+
const { client, executeQuery } = createClient({
|
|
54
|
+
data: { user: { id: "u1", name: "Alice" } },
|
|
55
|
+
});
|
|
56
|
+
await client.query(SAMPLE_QUERY, { response: UserResponseSchema }, { id: "u1" });
|
|
57
|
+
expect(executeQuery).toHaveBeenCalledOnce();
|
|
58
|
+
const request = executeQuery.mock.calls[0][0];
|
|
59
|
+
expect(request.body).toBe(SAMPLE_QUERY);
|
|
60
|
+
expect(request.verboseHttpOutput).toBe(false);
|
|
61
|
+
expect(request.failOnGraphqlErrors).toBe(true);
|
|
62
|
+
expect(request.custom).toEqual({
|
|
63
|
+
variables: {
|
|
64
|
+
key: "variables",
|
|
65
|
+
value: JSON.stringify({ id: "u1" }),
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
expect(request.headers).toBeUndefined();
|
|
69
|
+
});
|
|
70
|
+
it("omits custom when no variables are provided", async () => {
|
|
71
|
+
const { client, executeQuery } = createClient({
|
|
72
|
+
data: { user: { id: "u1", name: "Alice" } },
|
|
73
|
+
});
|
|
74
|
+
await client.query(SAMPLE_QUERY, { response: UserResponseSchema });
|
|
75
|
+
const request = executeQuery.mock.calls[0][0];
|
|
76
|
+
expect(request.custom).toBeUndefined();
|
|
77
|
+
});
|
|
78
|
+
it("forwards per-request headers into the proto headers field", async () => {
|
|
79
|
+
const { client, executeQuery } = createClient({
|
|
80
|
+
data: { user: { id: "u1", name: "Alice" } },
|
|
81
|
+
});
|
|
82
|
+
await client.query(SAMPLE_QUERY, { response: UserResponseSchema }, { id: "u1" }, undefined, {
|
|
83
|
+
Authorization: "Bearer abc123",
|
|
84
|
+
"X-Trace-Id": "trace-1",
|
|
85
|
+
});
|
|
86
|
+
const request = executeQuery.mock.calls[0][0];
|
|
87
|
+
expect(request.headers).toEqual([
|
|
88
|
+
{ key: "Authorization", value: "Bearer abc123" },
|
|
89
|
+
{ key: "X-Trace-Id", value: "trace-1" },
|
|
90
|
+
]);
|
|
91
|
+
});
|
|
92
|
+
it("does not set headers when an empty headers object is passed", async () => {
|
|
93
|
+
const { client, executeQuery } = createClient({
|
|
94
|
+
data: { user: { id: "u1", name: "Alice" } },
|
|
95
|
+
});
|
|
96
|
+
await client.query(SAMPLE_QUERY, { response: UserResponseSchema }, undefined, undefined, {});
|
|
97
|
+
const request = executeQuery.mock.calls[0][0];
|
|
98
|
+
expect(request.headers).toBeUndefined();
|
|
99
|
+
});
|
|
100
|
+
it("passes trace metadata through to executeQuery", async () => {
|
|
101
|
+
const { client, executeQuery } = createClient({
|
|
102
|
+
data: { user: { id: "u1", name: "Alice" } },
|
|
103
|
+
});
|
|
104
|
+
await client.query(SAMPLE_QUERY, { response: UserResponseSchema }, undefined, { label: "graphql.getUser", description: "Fetch a user by id" });
|
|
105
|
+
expect(executeQuery).toHaveBeenCalledWith(expect.any(Object), undefined, {
|
|
106
|
+
label: "graphql.getUser",
|
|
107
|
+
description: "Fetch a user by id",
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
it("throws RestApiValidationError when the response fails schema validation", async () => {
|
|
111
|
+
const { client } = createClient({
|
|
112
|
+
data: { user: { id: "u1" /* missing name */ } },
|
|
113
|
+
});
|
|
114
|
+
await expect(client.query(SAMPLE_QUERY, { response: UserResponseSchema })).rejects.toThrow(RestApiValidationError);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
describe("mutation()", () => {
|
|
118
|
+
it("returns validated data on a successful response", async () => {
|
|
119
|
+
const { client } = createClient({
|
|
120
|
+
data: { createUser: { id: "u2", name: "Bob" } },
|
|
121
|
+
});
|
|
122
|
+
const Schema = z.object({
|
|
123
|
+
data: z.object({
|
|
124
|
+
createUser: z.object({ id: z.string(), name: z.string() }),
|
|
125
|
+
}),
|
|
126
|
+
});
|
|
127
|
+
const result = await client.mutation(SAMPLE_MUTATION, { response: Schema }, { name: "Bob" });
|
|
128
|
+
expect(result.data.createUser).toEqual({ id: "u2", name: "Bob" });
|
|
129
|
+
});
|
|
130
|
+
it("forwards per-request headers into the proto headers field", async () => {
|
|
131
|
+
const { client, executeQuery } = createClient({
|
|
132
|
+
data: { createUser: { id: "u2", name: "Bob" } },
|
|
133
|
+
});
|
|
134
|
+
const Schema = z.object({
|
|
135
|
+
data: z.object({
|
|
136
|
+
createUser: z.object({ id: z.string(), name: z.string() }),
|
|
137
|
+
}),
|
|
138
|
+
});
|
|
139
|
+
await client.mutation(SAMPLE_MUTATION, { response: Schema }, { name: "Bob" }, undefined, { Authorization: "Bearer xyz" });
|
|
140
|
+
const request = executeQuery.mock.calls[0][0];
|
|
141
|
+
expect(request.body).toBe(SAMPLE_MUTATION);
|
|
142
|
+
expect(request.headers).toEqual([
|
|
143
|
+
{ key: "Authorization", value: "Bearer xyz" },
|
|
144
|
+
]);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
//# sourceMappingURL=client.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.test.js","sourceRoot":"","sources":["../../../src/integrations/graphql/client.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -59,6 +59,17 @@ import type { TraceMetadata } from "../registry.js";
|
|
|
59
59
|
* { response: MutationResponseSchema },
|
|
60
60
|
* { name: 'John Doe' }
|
|
61
61
|
* );
|
|
62
|
+
*
|
|
63
|
+
* // Send per-request headers (e.g. a dynamic Authorization token).
|
|
64
|
+
* // Static headers and auth configured on the integration apply automatically;
|
|
65
|
+
* // use this parameter only for values that vary per request.
|
|
66
|
+
* await graphql.query(
|
|
67
|
+
* `query { me { id } }`,
|
|
68
|
+
* { response: z.object({ data: z.object({ me: z.object({ id: z.string() }) }) }) },
|
|
69
|
+
* undefined,
|
|
70
|
+
* undefined,
|
|
71
|
+
* { Authorization: `Bearer ${token}` },
|
|
72
|
+
* );
|
|
62
73
|
* ```
|
|
63
74
|
*/
|
|
64
75
|
export interface GraphQLClient extends BaseIntegrationClient {
|
|
@@ -71,6 +82,10 @@ export interface GraphQLClient extends BaseIntegrationClient {
|
|
|
71
82
|
* `metadata` accept plain objects, pass `undefined` for variables when you only need metadata:
|
|
72
83
|
* `query(q, schema, undefined, { label: "..." })`
|
|
73
84
|
* @param metadata - Optional trace metadata for observability
|
|
85
|
+
* @param headers - Optional HTTP headers to include on this request. Static
|
|
86
|
+
* headers and auth configured on the integration apply automatically; use
|
|
87
|
+
* this parameter only for values that vary per request (e.g. a bearer token
|
|
88
|
+
* derived from the API's input).
|
|
74
89
|
* @returns Validated query result
|
|
75
90
|
*
|
|
76
91
|
* @example
|
|
@@ -89,7 +104,7 @@ export interface GraphQLClient extends BaseIntegrationClient {
|
|
|
89
104
|
*/
|
|
90
105
|
query<TResponse>(query: string, schema: {
|
|
91
106
|
response: z.ZodSchema<TResponse>;
|
|
92
|
-
}, variables?: Record<string, unknown>, metadata?: TraceMetadata): Promise<TResponse>;
|
|
107
|
+
}, variables?: Record<string, unknown>, metadata?: TraceMetadata, headers?: Record<string, string>): Promise<TResponse>;
|
|
93
108
|
/**
|
|
94
109
|
* Execute a GraphQL mutation.
|
|
95
110
|
*
|
|
@@ -99,6 +114,10 @@ export interface GraphQLClient extends BaseIntegrationClient {
|
|
|
99
114
|
* `metadata` accept plain objects, pass `undefined` for variables when you only need metadata:
|
|
100
115
|
* `mutation(m, schema, undefined, { label: "..." })`
|
|
101
116
|
* @param metadata - Optional trace metadata for observability
|
|
117
|
+
* @param headers - Optional HTTP headers to include on this request. Static
|
|
118
|
+
* headers and auth configured on the integration apply automatically; use
|
|
119
|
+
* this parameter only for values that vary per request (e.g. a bearer token
|
|
120
|
+
* derived from the API's input).
|
|
102
121
|
* @returns Validated mutation result
|
|
103
122
|
*
|
|
104
123
|
* @example
|
|
@@ -118,6 +137,6 @@ export interface GraphQLClient extends BaseIntegrationClient {
|
|
|
118
137
|
*/
|
|
119
138
|
mutation<TResponse>(mutation: string, schema: {
|
|
120
139
|
response: z.ZodSchema<TResponse>;
|
|
121
|
-
}, variables?: Record<string, unknown>, metadata?: TraceMetadata): Promise<TResponse>;
|
|
140
|
+
}, variables?: Record<string, unknown>, metadata?: TraceMetadata, headers?: Record<string, string>): Promise<TResponse>;
|
|
122
141
|
}
|
|
123
142
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/integrations/graphql/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/integrations/graphql/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AACH,MAAM,WAAW,aAAc,SAAQ,qBAAqB;IAC1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,KAAK,CAAC,SAAS,EACb,KAAK,EAAE,MAAM,EACb,MAAM,EAAE;QAAE,QAAQ,EAAE,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAA;KAAE,EAC5C,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,QAAQ,CAAC,EAAE,aAAa,EACxB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC,CAAC;IAEtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACH,QAAQ,CAAC,SAAS,EAChB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE;QAAE,QAAQ,EAAE,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAA;KAAE,EAC5C,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,QAAQ,CAAC,EAAE,aAAa,EACxB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,SAAS,CAAC,CAAC;CACvB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@superblocksteam/sdk-api",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.120-next.1",
|
|
4
4
|
"description": "Superblocks SDK for TypeScript-based API definitions",
|
|
5
5
|
"license": "Superblocks Community Software License",
|
|
6
6
|
"files": [
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"eslint": "^9.39.1",
|
|
29
29
|
"eslint-config-prettier": "^10.1.8",
|
|
30
30
|
"tsx": "^4.19.0",
|
|
31
|
-
"typescript": "^
|
|
32
|
-
"typescript-eslint": "^8.
|
|
31
|
+
"typescript": "^6.0.3",
|
|
32
|
+
"typescript-eslint": "^8.59.2",
|
|
33
33
|
"uuid": "^9.0.0",
|
|
34
34
|
"vitest": "^2.0.0"
|
|
35
35
|
},
|
|
@@ -5,8 +5,11 @@
|
|
|
5
5
|
* Handles proto message construction and response validation.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import type { PartialMessage } from "@bufbuild/protobuf";
|
|
8
9
|
import { z } from "zod";
|
|
9
10
|
|
|
11
|
+
import type { Property } from "@superblocksteam/types/dist/src/common/v1/plugin_pb";
|
|
12
|
+
|
|
10
13
|
import { RestApiValidationError } from "../../errors.js";
|
|
11
14
|
import type { QueryExecutor, TraceMetadata } from "../registry.js";
|
|
12
15
|
import type { IntegrationConfig, IntegrationClientImpl } from "../types.js";
|
|
@@ -19,6 +22,8 @@ import type { IntegrationConfig, IntegrationClientImpl } from "../types.js";
|
|
|
19
22
|
* - Executing queries and mutations through the orchestrator
|
|
20
23
|
* - Required Zod schema validation on full GraphQL response
|
|
21
24
|
* - Variables serialization for parameterized queries
|
|
25
|
+
* - Optional per-request HTTP headers (in addition to any headers configured
|
|
26
|
+
* on the integration)
|
|
22
27
|
*/
|
|
23
28
|
export abstract class GraphQLIntegrationClient implements IntegrationClientImpl {
|
|
24
29
|
readonly name: string;
|
|
@@ -40,6 +45,7 @@ export abstract class GraphQLIntegrationClient implements IntegrationClientImpl
|
|
|
40
45
|
* @param schema - Zod schema for full response validation (REQUIRED)
|
|
41
46
|
* @param variables - Optional variables for the query
|
|
42
47
|
* @param metadata - Optional trace metadata for observability
|
|
48
|
+
* @param headers - Optional HTTP headers to send with the request
|
|
43
49
|
* @returns Validated query result
|
|
44
50
|
*/
|
|
45
51
|
async query<TResponse>(
|
|
@@ -47,8 +53,9 @@ export abstract class GraphQLIntegrationClient implements IntegrationClientImpl
|
|
|
47
53
|
schema: { response: z.ZodSchema<TResponse> },
|
|
48
54
|
variables?: Record<string, unknown>,
|
|
49
55
|
metadata?: TraceMetadata,
|
|
56
|
+
headers?: Record<string, string>,
|
|
50
57
|
): Promise<TResponse> {
|
|
51
|
-
return this.executeGraphQL(query, schema, variables, metadata);
|
|
58
|
+
return this.executeGraphQL(query, schema, variables, metadata, headers);
|
|
52
59
|
}
|
|
53
60
|
|
|
54
61
|
/**
|
|
@@ -58,6 +65,7 @@ export abstract class GraphQLIntegrationClient implements IntegrationClientImpl
|
|
|
58
65
|
* @param schema - Zod schema for full response validation (REQUIRED)
|
|
59
66
|
* @param variables - Optional variables for the mutation
|
|
60
67
|
* @param metadata - Optional trace metadata for observability
|
|
68
|
+
* @param headers - Optional HTTP headers to send with the request
|
|
61
69
|
* @returns Validated mutation result
|
|
62
70
|
*/
|
|
63
71
|
async mutation<TResponse>(
|
|
@@ -65,8 +73,9 @@ export abstract class GraphQLIntegrationClient implements IntegrationClientImpl
|
|
|
65
73
|
schema: { response: z.ZodSchema<TResponse> },
|
|
66
74
|
variables?: Record<string, unknown>,
|
|
67
75
|
metadata?: TraceMetadata,
|
|
76
|
+
headers?: Record<string, string>,
|
|
68
77
|
): Promise<TResponse> {
|
|
69
|
-
return this.executeGraphQL(mutation, schema, variables, metadata);
|
|
78
|
+
return this.executeGraphQL(mutation, schema, variables, metadata, headers);
|
|
70
79
|
}
|
|
71
80
|
|
|
72
81
|
/**
|
|
@@ -80,9 +89,17 @@ export abstract class GraphQLIntegrationClient implements IntegrationClientImpl
|
|
|
80
89
|
schema: { response: z.ZodSchema<TResponse> },
|
|
81
90
|
variables?: Record<string, unknown>,
|
|
82
91
|
metadata?: TraceMetadata,
|
|
92
|
+
headers?: Record<string, string>,
|
|
83
93
|
): Promise<TResponse> {
|
|
94
|
+
const headerProps: PartialMessage<Property>[] = [];
|
|
95
|
+
if (headers) {
|
|
96
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
97
|
+
headerProps.push({ key, value });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
84
101
|
// Build graphql.v1.Plugin proto message
|
|
85
|
-
const request = {
|
|
102
|
+
const request: Record<string, unknown> = {
|
|
86
103
|
body: queryString,
|
|
87
104
|
custom: variables
|
|
88
105
|
? {
|
|
@@ -96,6 +113,10 @@ export abstract class GraphQLIntegrationClient implements IntegrationClientImpl
|
|
|
96
113
|
failOnGraphqlErrors: true,
|
|
97
114
|
};
|
|
98
115
|
|
|
116
|
+
if (headerProps.length > 0) {
|
|
117
|
+
request.headers = headerProps;
|
|
118
|
+
}
|
|
119
|
+
|
|
99
120
|
// Execute query through orchestrator
|
|
100
121
|
const response = await this.executeQuery(request, undefined, metadata);
|
|
101
122
|
|
|
@@ -659,7 +659,27 @@ describe("resolveIntegrationDocumentation", () => {
|
|
|
659
659
|
}
|
|
660
660
|
});
|
|
661
661
|
|
|
662
|
-
it("throws when overlay entry has
|
|
662
|
+
it("throws when overlay entry has neither versionRange nor sdkVersionRange", async () => {
|
|
663
|
+
const integrationsDirectory = createPluginDocsFixture("dropbox", {
|
|
664
|
+
"README.md": "base",
|
|
665
|
+
"docs.manifest.json": JSON.stringify({
|
|
666
|
+
overlays: [{ file: "overlays/01.md" }],
|
|
667
|
+
}),
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
try {
|
|
671
|
+
await expect(
|
|
672
|
+
resolveIntegrationDocumentation("dropbox", {
|
|
673
|
+
pluginVersion: "0.4.0",
|
|
674
|
+
integrationsDirectory,
|
|
675
|
+
}),
|
|
676
|
+
).rejects.toThrowError(/Invalid overlay entry/);
|
|
677
|
+
} finally {
|
|
678
|
+
rmSync(integrationsDirectory, { recursive: true, force: true });
|
|
679
|
+
}
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
it("throws when overlay entry has empty versionRange and no sdkVersionRange", async () => {
|
|
663
683
|
const integrationsDirectory = createPluginDocsFixture("dropbox", {
|
|
664
684
|
"README.md": "base",
|
|
665
685
|
"docs.manifest.json": JSON.stringify({
|
|
@@ -680,6 +700,144 @@ describe("resolveIntegrationDocumentation", () => {
|
|
|
680
700
|
});
|
|
681
701
|
});
|
|
682
702
|
|
|
703
|
+
describe("sdkVersionRange", () => {
|
|
704
|
+
it("applies overlay when sdkVersion matches sdkVersionRange", async () => {
|
|
705
|
+
const integrationsDirectory = createPluginDocsFixture("graphql", {
|
|
706
|
+
"README.md": "base-graphql-docs",
|
|
707
|
+
"docs.manifest.json": JSON.stringify({
|
|
708
|
+
overlays: [
|
|
709
|
+
{ file: "overlays/headers.md", sdkVersionRange: ">=0.0.2" },
|
|
710
|
+
],
|
|
711
|
+
}),
|
|
712
|
+
"overlays/headers.md": "dynamic-headers-docs",
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
try {
|
|
716
|
+
const docs = await resolveIntegrationDocumentation("graphql", {
|
|
717
|
+
sdkVersion: "0.0.2",
|
|
718
|
+
integrationsDirectory,
|
|
719
|
+
});
|
|
720
|
+
expect(docs).toBe("base-graphql-docs\n\ndynamic-headers-docs");
|
|
721
|
+
} finally {
|
|
722
|
+
rmSync(integrationsDirectory, { recursive: true, force: true });
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
it("skips overlay when sdkVersion does not match sdkVersionRange", async () => {
|
|
727
|
+
const integrationsDirectory = createPluginDocsFixture("graphql", {
|
|
728
|
+
"README.md": "base-graphql-docs",
|
|
729
|
+
"docs.manifest.json": JSON.stringify({
|
|
730
|
+
overlays: [
|
|
731
|
+
{ file: "overlays/headers.md", sdkVersionRange: ">=0.0.2" },
|
|
732
|
+
],
|
|
733
|
+
}),
|
|
734
|
+
"overlays/headers.md": "dynamic-headers-docs",
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
try {
|
|
738
|
+
const docs = await resolveIntegrationDocumentation("graphql", {
|
|
739
|
+
sdkVersion: "0.0.1",
|
|
740
|
+
integrationsDirectory,
|
|
741
|
+
});
|
|
742
|
+
expect(docs).toBe("base-graphql-docs");
|
|
743
|
+
} finally {
|
|
744
|
+
rmSync(integrationsDirectory, { recursive: true, force: true });
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
it("skips overlay when sdkVersion is not provided", async () => {
|
|
749
|
+
const integrationsDirectory = createPluginDocsFixture("graphql", {
|
|
750
|
+
"README.md": "base-graphql-docs",
|
|
751
|
+
"docs.manifest.json": JSON.stringify({
|
|
752
|
+
overlays: [
|
|
753
|
+
{ file: "overlays/headers.md", sdkVersionRange: ">=0.0.2" },
|
|
754
|
+
],
|
|
755
|
+
}),
|
|
756
|
+
"overlays/headers.md": "dynamic-headers-docs",
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
try {
|
|
760
|
+
const docs = await resolveIntegrationDocumentation("graphql", {
|
|
761
|
+
pluginVersion: "0.0.10",
|
|
762
|
+
integrationsDirectory,
|
|
763
|
+
});
|
|
764
|
+
expect(docs).toBe("base-graphql-docs");
|
|
765
|
+
} finally {
|
|
766
|
+
rmSync(integrationsDirectory, { recursive: true, force: true });
|
|
767
|
+
}
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
it("requires both versionRange and sdkVersionRange to match when both are specified", async () => {
|
|
771
|
+
const integrationsDirectory = createPluginDocsFixture("graphql", {
|
|
772
|
+
"README.md": "base",
|
|
773
|
+
"docs.manifest.json": JSON.stringify({
|
|
774
|
+
overlays: [
|
|
775
|
+
{
|
|
776
|
+
file: "overlays/both.md",
|
|
777
|
+
versionRange: ">=0.0.10",
|
|
778
|
+
sdkVersionRange: ">=0.0.2",
|
|
779
|
+
},
|
|
780
|
+
],
|
|
781
|
+
}),
|
|
782
|
+
"overlays/both.md": "both-match-overlay",
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
try {
|
|
786
|
+
// Both match
|
|
787
|
+
expect(
|
|
788
|
+
await resolveIntegrationDocumentation("graphql", {
|
|
789
|
+
pluginVersion: "0.0.10",
|
|
790
|
+
sdkVersion: "0.0.2",
|
|
791
|
+
integrationsDirectory,
|
|
792
|
+
}),
|
|
793
|
+
).toBe("base\n\nboth-match-overlay");
|
|
794
|
+
|
|
795
|
+
// Only plugin matches
|
|
796
|
+
expect(
|
|
797
|
+
await resolveIntegrationDocumentation("graphql", {
|
|
798
|
+
pluginVersion: "0.0.10",
|
|
799
|
+
sdkVersion: "0.0.1",
|
|
800
|
+
integrationsDirectory,
|
|
801
|
+
}),
|
|
802
|
+
).toBe("base");
|
|
803
|
+
|
|
804
|
+
// Only sdk matches
|
|
805
|
+
expect(
|
|
806
|
+
await resolveIntegrationDocumentation("graphql", {
|
|
807
|
+
pluginVersion: "0.0.9",
|
|
808
|
+
sdkVersion: "0.0.2",
|
|
809
|
+
integrationsDirectory,
|
|
810
|
+
}),
|
|
811
|
+
).toBe("base");
|
|
812
|
+
} finally {
|
|
813
|
+
rmSync(integrationsDirectory, { recursive: true, force: true });
|
|
814
|
+
}
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
it("allows overlay with only sdkVersionRange (no versionRange)", async () => {
|
|
818
|
+
const integrationsDirectory = createPluginDocsFixture("graphql", {
|
|
819
|
+
"README.md": "base",
|
|
820
|
+
"docs.manifest.json": JSON.stringify({
|
|
821
|
+
overlays: [
|
|
822
|
+
{ file: "overlays/sdk-only.md", sdkVersionRange: ">=0.0.2" },
|
|
823
|
+
],
|
|
824
|
+
}),
|
|
825
|
+
"overlays/sdk-only.md": "sdk-gated-overlay",
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
try {
|
|
829
|
+
// No pluginVersion needed — only sdkVersion matters
|
|
830
|
+
const docs = await resolveIntegrationDocumentation("graphql", {
|
|
831
|
+
sdkVersion: "0.0.3",
|
|
832
|
+
integrationsDirectory,
|
|
833
|
+
});
|
|
834
|
+
expect(docs).toBe("base\n\nsdk-gated-overlay");
|
|
835
|
+
} finally {
|
|
836
|
+
rmSync(integrationsDirectory, { recursive: true, force: true });
|
|
837
|
+
}
|
|
838
|
+
});
|
|
839
|
+
});
|
|
840
|
+
|
|
683
841
|
it("blocks overlay paths outside the plugin directory", async () => {
|
|
684
842
|
const integrationsDirectory = createPluginDocsFixture("dropbox", {
|
|
685
843
|
"README.md": "base",
|
|
@@ -11,7 +11,13 @@ const DOC_DIRECTORY_ALIASES = {
|
|
|
11
11
|
|
|
12
12
|
interface DocumentationOverlay {
|
|
13
13
|
file: string;
|
|
14
|
-
versionRange
|
|
14
|
+
versionRange?: string;
|
|
15
|
+
/** When set, checks against the sdk-api version ("javascriptsdkapi") running
|
|
16
|
+
* in the orchestrator instead of the plugin-specific version. Use this to
|
|
17
|
+
* gate overlays on sdk-api features that ship independently of plugin
|
|
18
|
+
* version bumps. Both versionRange and sdkVersionRange can be specified
|
|
19
|
+
* together — the overlay is applied only when all specified ranges match. */
|
|
20
|
+
sdkVersionRange?: string;
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
interface DocumentationManifest {
|
|
@@ -32,6 +38,7 @@ type OverlayOperation =
|
|
|
32
38
|
|
|
33
39
|
export interface ResolveIntegrationDocumentationOptions {
|
|
34
40
|
pluginVersion?: SemVer;
|
|
41
|
+
sdkVersion?: SemVer;
|
|
35
42
|
integrationsDirectory?: string;
|
|
36
43
|
}
|
|
37
44
|
|
|
@@ -82,16 +89,17 @@ async function parseDocumentationManifest(
|
|
|
82
89
|
typeof overlay !== "object" ||
|
|
83
90
|
overlay === null ||
|
|
84
91
|
!("file" in overlay) ||
|
|
85
|
-
!("versionRange" in overlay)
|
|
92
|
+
(!("versionRange" in overlay) && !("sdkVersionRange" in overlay))
|
|
86
93
|
) {
|
|
87
94
|
throw new Error(
|
|
88
95
|
`Invalid overlay entry at index ${index} in ${manifestPath}.`,
|
|
89
96
|
);
|
|
90
97
|
}
|
|
91
98
|
|
|
92
|
-
const { file, versionRange } = overlay as {
|
|
99
|
+
const { file, versionRange, sdkVersionRange } = overlay as {
|
|
93
100
|
file: unknown;
|
|
94
101
|
versionRange: unknown;
|
|
102
|
+
sdkVersionRange: unknown;
|
|
95
103
|
};
|
|
96
104
|
|
|
97
105
|
if (typeof file !== "string" || file.trim().length === 0) {
|
|
@@ -99,18 +107,35 @@ async function parseDocumentationManifest(
|
|
|
99
107
|
`Invalid "file" for overlay index ${index} in ${manifestPath}.`,
|
|
100
108
|
);
|
|
101
109
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
versionRange.trim().length
|
|
105
|
-
|
|
110
|
+
|
|
111
|
+
const hasVersionRange =
|
|
112
|
+
typeof versionRange === "string" && versionRange.trim().length > 0;
|
|
113
|
+
const hasSdkVersionRange =
|
|
114
|
+
typeof sdkVersionRange === "string" &&
|
|
115
|
+
sdkVersionRange.trim().length > 0;
|
|
116
|
+
|
|
117
|
+
if ("versionRange" in overlay && !hasVersionRange) {
|
|
106
118
|
throw new Error(
|
|
107
119
|
`Invalid "versionRange" for overlay index ${index} in ${manifestPath}.`,
|
|
108
120
|
);
|
|
109
121
|
}
|
|
122
|
+
if ("sdkVersionRange" in overlay && !hasSdkVersionRange) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
`Invalid "sdkVersionRange" for overlay index ${index} in ${manifestPath}.`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
if (!hasVersionRange && !hasSdkVersionRange) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
`Overlay at index ${index} in ${manifestPath} must specify at least one of "versionRange" or "sdkVersionRange".`,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
110
132
|
|
|
111
133
|
return {
|
|
112
134
|
file,
|
|
113
|
-
versionRange,
|
|
135
|
+
...(hasVersionRange ? { versionRange: versionRange as string } : {}),
|
|
136
|
+
...(hasSdkVersionRange
|
|
137
|
+
? { sdkVersionRange: sdkVersionRange as string }
|
|
138
|
+
: {}),
|
|
114
139
|
};
|
|
115
140
|
},
|
|
116
141
|
);
|
|
@@ -380,11 +405,38 @@ function ensureOverlayPathIsWithinPluginDir(
|
|
|
380
405
|
}
|
|
381
406
|
}
|
|
382
407
|
|
|
408
|
+
function overlayMatchesVersions(
|
|
409
|
+
overlay: DocumentationOverlay,
|
|
410
|
+
pluginVersion: SemVer | undefined,
|
|
411
|
+
sdkVersion: SemVer | undefined,
|
|
412
|
+
): boolean {
|
|
413
|
+
if (
|
|
414
|
+
overlay.versionRange &&
|
|
415
|
+
(!pluginVersion ||
|
|
416
|
+
!versionMatchesRange(pluginVersion, overlay.versionRange))
|
|
417
|
+
) {
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (
|
|
422
|
+
overlay.sdkVersionRange &&
|
|
423
|
+
(!sdkVersion || !versionMatchesRange(sdkVersion, overlay.sdkVersionRange))
|
|
424
|
+
) {
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return true;
|
|
429
|
+
}
|
|
430
|
+
|
|
383
431
|
export async function resolveIntegrationDocumentation(
|
|
384
432
|
pluginId: string,
|
|
385
433
|
options: ResolveIntegrationDocumentationOptions = {},
|
|
386
434
|
): Promise<string> {
|
|
387
|
-
const {
|
|
435
|
+
const {
|
|
436
|
+
pluginVersion,
|
|
437
|
+
sdkVersion,
|
|
438
|
+
integrationsDirectory = INTEGRATIONS_DIR,
|
|
439
|
+
} = options;
|
|
388
440
|
|
|
389
441
|
const pluginDirectory = resolve(
|
|
390
442
|
integrationsDirectory,
|
|
@@ -397,13 +449,13 @@ export async function resolveIntegrationDocumentation(
|
|
|
397
449
|
const baseDocumentationPath = resolve(pluginDirectory, baseFileName);
|
|
398
450
|
const baseDocumentation = await readFile(baseDocumentationPath, "utf8");
|
|
399
451
|
|
|
400
|
-
if (!manifest
|
|
452
|
+
if (!manifest) {
|
|
401
453
|
return baseDocumentation;
|
|
402
454
|
}
|
|
403
455
|
|
|
404
456
|
let resolvedDocumentation = baseDocumentation;
|
|
405
457
|
for (const overlay of manifest.overlays) {
|
|
406
|
-
if (!
|
|
458
|
+
if (!overlayMatchesVersions(overlay, pluginVersion, sdkVersion)) {
|
|
407
459
|
continue;
|
|
408
460
|
}
|
|
409
461
|
|