@superblocksteam/sdk-api 2.0.105-next.0 → 2.0.105-next.2
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/README.md +202 -86
- package/dist/api/definition.d.ts +11 -6
- package/dist/api/definition.d.ts.map +1 -1
- package/dist/api/definition.js +19 -12
- package/dist/api/definition.js.map +1 -1
- package/dist/api/definition.test.js +39 -15
- package/dist/api/definition.test.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +1 -1
- package/dist/index.d.ts +10 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -5
- package/dist/index.js.map +1 -1
- package/dist/integrations/base/index.d.ts +2 -1
- package/dist/integrations/base/index.d.ts.map +1 -1
- package/dist/integrations/base/index.js +1 -0
- package/dist/integrations/base/index.js.map +1 -1
- package/dist/integrations/base/rest-api-client-base.d.ts +48 -0
- package/dist/integrations/base/rest-api-client-base.d.ts.map +1 -0
- package/dist/integrations/base/rest-api-client-base.js +98 -0
- package/dist/integrations/base/rest-api-client-base.js.map +1 -0
- package/dist/integrations/base/rest-api-integration-client.d.ts +10 -20
- package/dist/integrations/base/rest-api-integration-client.d.ts.map +1 -1
- package/dist/integrations/base/rest-api-integration-client.js +10 -65
- package/dist/integrations/base/rest-api-integration-client.js.map +1 -1
- package/dist/integrations/box/types.d.ts +1 -1
- package/dist/integrations/declarations.d.ts +5 -63
- package/dist/integrations/declarations.d.ts.map +1 -1
- package/dist/integrations/declarations.js +5 -59
- package/dist/integrations/declarations.js.map +1 -1
- package/dist/integrations/documentation.test.js +0 -2
- package/dist/integrations/documentation.test.js.map +1 -1
- package/dist/integrations/googledrive/types.d.ts +1 -1
- package/dist/integrations/index.d.ts +1 -9
- package/dist/integrations/index.d.ts.map +1 -1
- package/dist/integrations/index.js +1 -6
- package/dist/integrations/index.js.map +1 -1
- package/dist/integrations/registry.d.ts +1 -10
- package/dist/integrations/registry.d.ts.map +1 -1
- package/dist/integrations/registry.js +0 -25
- package/dist/integrations/registry.js.map +1 -1
- package/dist/integrations/slack/client.d.ts +13 -9
- package/dist/integrations/slack/client.d.ts.map +1 -1
- package/dist/integrations/slack/client.js +60 -8
- package/dist/integrations/slack/client.js.map +1 -1
- package/dist/integrations/slack/client.test.d.ts +11 -0
- package/dist/integrations/slack/client.test.d.ts.map +1 -0
- package/dist/integrations/slack/client.test.js +368 -0
- package/dist/integrations/slack/client.test.js.map +1 -0
- package/dist/integrations/slack/index.d.ts +2 -1
- package/dist/integrations/slack/index.d.ts.map +1 -1
- package/dist/integrations/slack/index.js +1 -0
- package/dist/integrations/slack/index.js.map +1 -1
- package/dist/integrations/slack/types.d.ts +127 -28
- package/dist/integrations/slack/types.d.ts.map +1 -1
- package/dist/integrations/slack/types.js +27 -1
- package/dist/integrations/slack/types.js.map +1 -1
- package/dist/integrations/snowflake/client.d.ts +2 -2
- package/dist/integrations/snowflake/client.js +2 -2
- package/dist/runtime/context.d.ts +1 -1
- package/dist/runtime/executor.d.ts +2 -2
- package/dist/types.d.ts +14 -5
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/api/definition.test.ts +40 -15
- package/src/api/definition.ts +19 -12
- package/src/errors.ts +1 -1
- package/src/index.ts +13 -30
- package/src/integrations/asana/README.md +12 -12
- package/src/integrations/base/index.ts +2 -1
- package/src/integrations/base/rest-api-client-base.ts +134 -0
- package/src/integrations/base/rest-api-integration-client.ts +12 -89
- package/src/integrations/bitbucket/README.md +19 -19
- package/src/integrations/box/README.md +24 -24
- package/src/integrations/box/types.ts +1 -1
- package/src/integrations/circleci/README.md +18 -18
- package/src/integrations/declarations.ts +5 -91
- package/src/integrations/documentation.test.ts +0 -2
- package/src/integrations/googledrive/README.md +25 -22
- package/src/integrations/googledrive/types.ts +1 -1
- package/src/integrations/graphql/README.md +2 -2
- package/src/integrations/index.ts +0 -45
- package/src/integrations/registry.ts +1 -34
- package/src/integrations/salesforce/README.md +11 -9
- package/src/integrations/slack/README.md +62 -19
- package/src/integrations/slack/client.test.ts +553 -0
- package/src/integrations/slack/client.ts +92 -12
- package/src/integrations/slack/index.ts +6 -1
- package/src/integrations/slack/types.ts +142 -29
- package/src/integrations/snowflake/client.ts +2 -2
- package/src/integrations/zoom/README.md +15 -15
- package/src/runtime/context.ts +1 -1
- package/src/runtime/executor.ts +2 -2
- package/src/types.ts +14 -5
- package/dist/integrations/couchbase/client.d.ts +0 -36
- package/dist/integrations/couchbase/client.d.ts.map +0 -1
- package/dist/integrations/couchbase/client.js +0 -148
- package/dist/integrations/couchbase/client.js.map +0 -1
- package/dist/integrations/couchbase/index.d.ts +0 -8
- package/dist/integrations/couchbase/index.d.ts.map +0 -1
- package/dist/integrations/couchbase/index.js +0 -7
- package/dist/integrations/couchbase/index.js.map +0 -1
- package/dist/integrations/couchbase/types.d.ts +0 -100
- package/dist/integrations/couchbase/types.d.ts.map +0 -1
- package/dist/integrations/couchbase/types.js +0 -5
- package/dist/integrations/couchbase/types.js.map +0 -1
- package/dist/integrations/kafka/client.d.ts +0 -25
- package/dist/integrations/kafka/client.d.ts.map +0 -1
- package/dist/integrations/kafka/client.js +0 -124
- package/dist/integrations/kafka/client.js.map +0 -1
- package/dist/integrations/kafka/index.d.ts +0 -8
- package/dist/integrations/kafka/index.d.ts.map +0 -1
- package/dist/integrations/kafka/index.js +0 -7
- package/dist/integrations/kafka/index.js.map +0 -1
- package/dist/integrations/kafka/types.d.ts +0 -113
- package/dist/integrations/kafka/types.d.ts.map +0 -1
- package/dist/integrations/kafka/types.js +0 -5
- package/dist/integrations/kafka/types.js.map +0 -1
- package/dist/integrations/kinesis/client.d.ts +0 -31
- package/dist/integrations/kinesis/client.d.ts.map +0 -1
- package/dist/integrations/kinesis/client.js +0 -101
- package/dist/integrations/kinesis/client.js.map +0 -1
- package/dist/integrations/kinesis/index.d.ts +0 -8
- package/dist/integrations/kinesis/index.d.ts.map +0 -1
- package/dist/integrations/kinesis/index.js +0 -7
- package/dist/integrations/kinesis/index.js.map +0 -1
- package/dist/integrations/kinesis/types.d.ts +0 -97
- package/dist/integrations/kinesis/types.d.ts.map +0 -1
- package/dist/integrations/kinesis/types.js +0 -7
- package/dist/integrations/kinesis/types.js.map +0 -1
- package/dist/integrations/python/client.d.ts +0 -42
- package/dist/integrations/python/client.d.ts.map +0 -1
- package/dist/integrations/python/client.js +0 -89
- package/dist/integrations/python/client.js.map +0 -1
- package/dist/integrations/python/client.test.d.ts +0 -5
- package/dist/integrations/python/client.test.d.ts.map +0 -1
- package/dist/integrations/python/client.test.js +0 -214
- package/dist/integrations/python/client.test.js.map +0 -1
- package/dist/integrations/python/index.d.ts +0 -6
- package/dist/integrations/python/index.d.ts.map +0 -1
- package/dist/integrations/python/index.js +0 -5
- package/dist/integrations/python/index.js.map +0 -1
- package/dist/integrations/python/types.d.ts +0 -85
- package/dist/integrations/python/types.d.ts.map +0 -1
- package/dist/integrations/python/types.js +0 -5
- package/dist/integrations/python/types.js.map +0 -1
- package/src/integrations/couchbase/README.md +0 -138
- package/src/integrations/couchbase/client.ts +0 -225
- package/src/integrations/couchbase/index.ts +0 -8
- package/src/integrations/couchbase/types.ts +0 -126
- package/src/integrations/kafka/README.md +0 -144
- package/src/integrations/kafka/client.ts +0 -216
- package/src/integrations/kafka/index.ts +0 -14
- package/src/integrations/kafka/types.ts +0 -128
- package/src/integrations/kinesis/README.md +0 -153
- package/src/integrations/kinesis/client.ts +0 -146
- package/src/integrations/kinesis/index.ts +0 -14
- package/src/integrations/kinesis/types.ts +0 -114
- package/src/integrations/python/README.md +0 -566
- package/src/integrations/python/client.test.ts +0 -341
- package/src/integrations/python/client.ts +0 -136
- package/src/integrations/python/index.ts +0 -6
- package/src/integrations/python/types.ts +0 -92
package/src/index.ts
CHANGED
|
@@ -38,10 +38,14 @@
|
|
|
38
38
|
* throw new Error('User not found');
|
|
39
39
|
* }
|
|
40
40
|
*
|
|
41
|
-
* await ctx.integrations.notifier.
|
|
42
|
-
* channel: '#user-lookups',
|
|
43
|
-
*
|
|
44
|
-
*
|
|
41
|
+
* const notifyResult = await ctx.integrations.notifier.apiRequest(
|
|
42
|
+
* { method: 'POST', path: '/chat.postMessage', body: { channel: '#user-lookups', text: `Fetched user ${user.name}` } },
|
|
43
|
+
* { response: z.object({ channel: z.string(), ts: z.string() }) }
|
|
44
|
+
* );
|
|
45
|
+
*
|
|
46
|
+
* if (!notifyResult.ok) {
|
|
47
|
+
* throw new Error(`Slack API error: ${notifyResult.error}`);
|
|
48
|
+
* }
|
|
45
49
|
*
|
|
46
50
|
* return { user };
|
|
47
51
|
* },
|
|
@@ -129,20 +133,14 @@ export {
|
|
|
129
133
|
mongodb,
|
|
130
134
|
dynamodb,
|
|
131
135
|
cosmosdb,
|
|
132
|
-
couchbase,
|
|
133
136
|
s3,
|
|
134
137
|
gcs,
|
|
135
138
|
googleSheets,
|
|
136
|
-
kafka,
|
|
137
|
-
kinesis,
|
|
138
139
|
salesforce,
|
|
139
140
|
redis,
|
|
140
141
|
superblocksOcr,
|
|
141
|
-
python,
|
|
142
142
|
restApiIntegration,
|
|
143
143
|
getIntegrationDeclarations,
|
|
144
|
-
confluent,
|
|
145
|
-
redpanda,
|
|
146
144
|
lakebase,
|
|
147
145
|
snowflakePostgres,
|
|
148
146
|
smtp,
|
|
@@ -199,19 +197,13 @@ export {
|
|
|
199
197
|
type MongoDBRef,
|
|
200
198
|
type DynamoDBRef,
|
|
201
199
|
type CosmosDBRef,
|
|
202
|
-
type CouchbaseRef,
|
|
203
200
|
type S3Ref,
|
|
204
201
|
type GCSRef,
|
|
205
202
|
type GoogleSheetsRef,
|
|
206
|
-
type KafkaRef,
|
|
207
|
-
type KinesisRef,
|
|
208
203
|
type SalesforceRef,
|
|
209
204
|
type RedisRef,
|
|
210
205
|
type SuperblocksOCRRef,
|
|
211
|
-
type PythonRef,
|
|
212
206
|
type RestApiIntegrationRef,
|
|
213
|
-
type ConfluentRef,
|
|
214
|
-
type RedpandaRef,
|
|
215
207
|
type LakebaseRef,
|
|
216
208
|
type SnowflakePostgresRef,
|
|
217
209
|
type SmtpRef,
|
|
@@ -219,7 +211,11 @@ export {
|
|
|
219
211
|
|
|
220
212
|
// Integration client types (for advanced use cases)
|
|
221
213
|
export type { PostgresClient } from "./integrations/postgres/index.js";
|
|
222
|
-
export type {
|
|
214
|
+
export type {
|
|
215
|
+
SlackClient,
|
|
216
|
+
SlackResponse,
|
|
217
|
+
SlackErrorResponse,
|
|
218
|
+
} from "./integrations/slack/index.js";
|
|
223
219
|
export type { SnowflakeClient } from "./integrations/snowflake/index.js";
|
|
224
220
|
export type { SnowflakeCortexClient } from "./integrations/snowflakecortex/index.js";
|
|
225
221
|
export type { AirtableClient } from "./integrations/airtable/index.js";
|
|
@@ -259,29 +255,16 @@ export type {
|
|
|
259
255
|
DynamoDBAttributeValue,
|
|
260
256
|
} from "./integrations/dynamodb/index.js";
|
|
261
257
|
export type { CosmosDBClient } from "./integrations/cosmosdb/index.js";
|
|
262
|
-
export type {
|
|
263
|
-
CouchbaseClient,
|
|
264
|
-
CouchbaseIdentifier,
|
|
265
|
-
} from "./integrations/couchbase/index.js";
|
|
266
|
-
export type {
|
|
267
|
-
KinesisClient,
|
|
268
|
-
KinesisPutParams,
|
|
269
|
-
KinesisGetParams,
|
|
270
|
-
KinesisStreamIdentifier,
|
|
271
|
-
KinesisShardIteratorType,
|
|
272
|
-
} from "./integrations/kinesis/index.js";
|
|
273
258
|
export type {
|
|
274
259
|
SalesforceClient,
|
|
275
260
|
SalesforceCrudAction,
|
|
276
261
|
SalesforceBulkAction,
|
|
277
262
|
} from "./integrations/salesforce/index.js";
|
|
278
263
|
export type { RedisClient } from "./integrations/redis/index.js";
|
|
279
|
-
export type { KafkaClient } from "./integrations/kafka/index.js";
|
|
280
264
|
export type { S3Client } from "./integrations/s3/index.js";
|
|
281
265
|
export type { GCSClient } from "./integrations/gcs/index.js";
|
|
282
266
|
export type { GoogleSheetsClient } from "./integrations/gsheets/index.js";
|
|
283
267
|
export type { SuperblocksOCRClient } from "./integrations/superblocks-ocr/index.js";
|
|
284
|
-
export type { PythonClient } from "./integrations/python/index.js";
|
|
285
268
|
export type { RestApiIntegrationPluginClient } from "./integrations/restapiintegration/index.js";
|
|
286
269
|
export type { LakebaseClient } from "./integrations/lakebase/index.js";
|
|
287
270
|
export type { SnowflakePostgresClient } from "./integrations/snowflakepostgres/index.js";
|
|
@@ -49,7 +49,7 @@ export default api({
|
|
|
49
49
|
const result = await ctx.integrations.asana.apiRequest(
|
|
50
50
|
{
|
|
51
51
|
method: "POST",
|
|
52
|
-
path: "/
|
|
52
|
+
path: "/tasks",
|
|
53
53
|
body: {
|
|
54
54
|
data: {
|
|
55
55
|
name: name,
|
|
@@ -88,7 +88,7 @@ const ListTasksResponseSchema = z.object({
|
|
|
88
88
|
const result = await ctx.integrations.asana.apiRequest(
|
|
89
89
|
{
|
|
90
90
|
method: "GET",
|
|
91
|
-
path: `/
|
|
91
|
+
path: `/projects/${projectId}/tasks`,
|
|
92
92
|
params: {
|
|
93
93
|
opt_fields: "name,completed,due_on,assignee.name",
|
|
94
94
|
completed_since: "now", // Only incomplete tasks
|
|
@@ -108,7 +108,7 @@ result.data.forEach((task) => {
|
|
|
108
108
|
const result = await ctx.integrations.asana.apiRequest(
|
|
109
109
|
{
|
|
110
110
|
method: "PUT",
|
|
111
|
-
path: `/
|
|
111
|
+
path: `/tasks/${taskId}`,
|
|
112
112
|
body: {
|
|
113
113
|
data: {
|
|
114
114
|
completed: true,
|
|
@@ -126,7 +126,7 @@ const result = await ctx.integrations.asana.apiRequest(
|
|
|
126
126
|
const result = await ctx.integrations.asana.apiRequest(
|
|
127
127
|
{
|
|
128
128
|
method: "PUT",
|
|
129
|
-
path: `/
|
|
129
|
+
path: `/tasks/${taskId}`,
|
|
130
130
|
body: {
|
|
131
131
|
data: {
|
|
132
132
|
assignee: userId, // User GID
|
|
@@ -154,7 +154,7 @@ const ListProjectsResponseSchema = z.object({
|
|
|
154
154
|
const result = await ctx.integrations.asana.apiRequest(
|
|
155
155
|
{
|
|
156
156
|
method: "GET",
|
|
157
|
-
path: "/
|
|
157
|
+
path: "/projects",
|
|
158
158
|
params: {
|
|
159
159
|
workspace: workspaceId,
|
|
160
160
|
opt_fields: "name,archived,owner.name",
|
|
@@ -178,7 +178,7 @@ const CreateProjectResponseSchema = z.object({
|
|
|
178
178
|
const result = await ctx.integrations.asana.apiRequest(
|
|
179
179
|
{
|
|
180
180
|
method: "POST",
|
|
181
|
-
path: "/
|
|
181
|
+
path: "/projects",
|
|
182
182
|
body: {
|
|
183
183
|
data: {
|
|
184
184
|
name: "Q1 Marketing Campaign",
|
|
@@ -207,7 +207,7 @@ const StoryResponseSchema = z.object({
|
|
|
207
207
|
const result = await ctx.integrations.asana.apiRequest(
|
|
208
208
|
{
|
|
209
209
|
method: "POST",
|
|
210
|
-
path: `/
|
|
210
|
+
path: `/tasks/${taskId}/stories`,
|
|
211
211
|
body: {
|
|
212
212
|
data: {
|
|
213
213
|
text: "Great progress on this task!",
|
|
@@ -233,7 +233,7 @@ const SearchResponseSchema = z.object({
|
|
|
233
233
|
const result = await ctx.integrations.asana.apiRequest(
|
|
234
234
|
{
|
|
235
235
|
method: "GET",
|
|
236
|
-
path: "/
|
|
236
|
+
path: "/workspaces/{workspace_gid}/tasks/search",
|
|
237
237
|
params: {
|
|
238
238
|
"text.value": "urgent",
|
|
239
239
|
completed: false,
|
|
@@ -252,7 +252,7 @@ const result = await ctx.integrations.asana.apiRequest(
|
|
|
252
252
|
const result = await ctx.integrations.asana.apiRequest(
|
|
253
253
|
{
|
|
254
254
|
method: "GET",
|
|
255
|
-
path: `/
|
|
255
|
+
path: `/tasks/${taskId}/subtasks`,
|
|
256
256
|
params: {
|
|
257
257
|
opt_fields: "name,completed,assignee.name",
|
|
258
258
|
},
|
|
@@ -276,7 +276,7 @@ await asana.getTasks({ ... });
|
|
|
276
276
|
|
|
277
277
|
// CORRECT - Use apiRequest
|
|
278
278
|
await ctx.integrations.asana.apiRequest(
|
|
279
|
-
{ method: "POST", path: "/
|
|
279
|
+
{ method: "POST", path: "/tasks", body: { data: { ... } } },
|
|
280
280
|
{ response: CreateTaskResponseSchema }
|
|
281
281
|
);
|
|
282
282
|
```
|
|
@@ -381,7 +381,7 @@ async function getAllTasks(asana: AsanaClient, projectId: string) {
|
|
|
381
381
|
const result = await ctx.integrations.asana.apiRequest(
|
|
382
382
|
{
|
|
383
383
|
method: "GET",
|
|
384
|
-
path: `/
|
|
384
|
+
path: `/projects/${projectId}/tasks`,
|
|
385
385
|
params: {
|
|
386
386
|
limit: 100,
|
|
387
387
|
...(offset && { offset }),
|
|
@@ -405,7 +405,7 @@ import { RestApiValidationError } from "@superblocksteam/sdk-api";
|
|
|
405
405
|
|
|
406
406
|
try {
|
|
407
407
|
const result = await ctx.integrations.asana.apiRequest(
|
|
408
|
-
{ method: "POST", path: "/
|
|
408
|
+
{ method: "POST", path: "/tasks", body: { data: { ... } } },
|
|
409
409
|
{ response: CreateTaskResponseSchema }
|
|
410
410
|
);
|
|
411
411
|
} catch (error) {
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* Base clients for integrations.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
export { RestApiClientBase } from "./rest-api-client-base.js";
|
|
5
6
|
export { RestApiIntegrationClient } from "./rest-api-integration-client.js";
|
|
6
|
-
export type { RestApiRequest } from "./rest-api-
|
|
7
|
+
export type { RestApiRequest } from "./rest-api-client-base.js";
|
|
7
8
|
export { GraphQLIntegrationClient } from "./graphql-integration-client.js";
|
|
8
9
|
export type {
|
|
9
10
|
ApiRequestOptions,
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract base for REST API Integration clients.
|
|
3
|
+
*
|
|
4
|
+
* Owns the shared infrastructure that every REST-API-backed integration
|
|
5
|
+
* needs: config storage, parameter helpers, body validation, request
|
|
6
|
+
* construction, and query execution. Subclasses add their own
|
|
7
|
+
* `apiRequest` with whatever response-handling strategy they need
|
|
8
|
+
* (e.g. direct Zod validation, discriminated-union wrapping).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { PartialMessage } from "@bufbuild/protobuf";
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
|
|
14
|
+
import type { Property } from "@superblocksteam/types/dist/src/common/v1/plugin_pb";
|
|
15
|
+
import type { Plugin as RestApiIntegrationPlugin } from "@superblocksteam/types/dist/src/plugins/restapiintegration/v1/plugin_pb";
|
|
16
|
+
|
|
17
|
+
import { RestApiValidationError } from "../../errors.js";
|
|
18
|
+
import type { QueryExecutor, TraceMetadata } from "../registry.js";
|
|
19
|
+
import type { IntegrationConfig, IntegrationClientImpl } from "../types.js";
|
|
20
|
+
import type { ApiRequestOptions } from "./types.js";
|
|
21
|
+
|
|
22
|
+
export type RestApiRequest = PartialMessage<RestApiIntegrationPlugin>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Shared base for all REST API Integration clients.
|
|
26
|
+
*
|
|
27
|
+
* Provides config fields, parameter helpers, body validation, request
|
|
28
|
+
* building, and query execution. Does NOT define `apiRequest` — each
|
|
29
|
+
* concrete subclass supplies its own return-type contract.
|
|
30
|
+
*/
|
|
31
|
+
export abstract class RestApiClientBase implements IntegrationClientImpl {
|
|
32
|
+
readonly name: string;
|
|
33
|
+
readonly pluginId: string;
|
|
34
|
+
readonly config: IntegrationConfig;
|
|
35
|
+
|
|
36
|
+
protected readonly executeQuery: QueryExecutor;
|
|
37
|
+
|
|
38
|
+
constructor(config: IntegrationConfig, executeQuery: QueryExecutor) {
|
|
39
|
+
this.name = config.name;
|
|
40
|
+
this.pluginId = config.pluginId;
|
|
41
|
+
this.config = config;
|
|
42
|
+
this.executeQuery = executeQuery;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Create a Property object for query params / headers.
|
|
47
|
+
*/
|
|
48
|
+
protected createParam(key: string, value: unknown): PartialMessage<Property> {
|
|
49
|
+
return {
|
|
50
|
+
key,
|
|
51
|
+
value: typeof value === "string" ? value : JSON.stringify(value),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Validate the request body, build the proto request, and execute it.
|
|
57
|
+
*
|
|
58
|
+
* Returns the raw (unvalidated) response from the orchestrator.
|
|
59
|
+
* Subclasses call this, then apply their own response handling.
|
|
60
|
+
*
|
|
61
|
+
* @param options - HTTP method, path, body, params, headers
|
|
62
|
+
* @param bodySchema - Optional Zod schema for body validation
|
|
63
|
+
* @param metadata - Optional trace metadata for observability
|
|
64
|
+
* @returns Raw response from the orchestrator
|
|
65
|
+
*/
|
|
66
|
+
protected async executeApiRequest<TBody>(
|
|
67
|
+
options: ApiRequestOptions<TBody>,
|
|
68
|
+
bodySchema?: z.ZodSchema<TBody>,
|
|
69
|
+
metadata?: TraceMetadata,
|
|
70
|
+
): Promise<unknown> {
|
|
71
|
+
// Validate request body if both body and schema are present.
|
|
72
|
+
if (options.body !== undefined && bodySchema) {
|
|
73
|
+
const bodyParseResult = bodySchema.safeParse(options.body);
|
|
74
|
+
if (!bodyParseResult.success) {
|
|
75
|
+
throw new RestApiValidationError(
|
|
76
|
+
`Request body validation failed: ${bodyParseResult.error.message}`,
|
|
77
|
+
{
|
|
78
|
+
zodError: bodyParseResult.error,
|
|
79
|
+
data: options.body,
|
|
80
|
+
},
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const headers: PartialMessage<Property>[] = [];
|
|
86
|
+
if (options.headers) {
|
|
87
|
+
for (const [key, value] of Object.entries(options.headers)) {
|
|
88
|
+
headers.push(this.createParam(key, value));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const params: PartialMessage<Property>[] = [];
|
|
93
|
+
if (options.params) {
|
|
94
|
+
for (const [key, value] of Object.entries(options.params)) {
|
|
95
|
+
params.push(this.createParam(key, value));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const request: RestApiRequest = {
|
|
100
|
+
openApiAction: "genericHttpRequest",
|
|
101
|
+
httpMethod: options.method.toUpperCase(),
|
|
102
|
+
urlPath: options.path,
|
|
103
|
+
headers,
|
|
104
|
+
params,
|
|
105
|
+
responseType: "json",
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
if (options.body !== undefined) {
|
|
109
|
+
request.body = JSON.stringify(options.body);
|
|
110
|
+
request.bodyType = "jsonBody";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const result = await this.executeQuery(
|
|
114
|
+
request as Record<string, unknown>,
|
|
115
|
+
undefined,
|
|
116
|
+
metadata,
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
if (result === null || result === undefined) {
|
|
120
|
+
const nonNullResult = z.object({}).safeParse(result);
|
|
121
|
+
if (!nonNullResult.success) {
|
|
122
|
+
throw new RestApiValidationError(
|
|
123
|
+
`Integration query returned ${String(result)} — expected a JSON response object`,
|
|
124
|
+
{
|
|
125
|
+
zodError: nonNullResult.error,
|
|
126
|
+
data: result,
|
|
127
|
+
},
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -1,58 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Generic REST API Integration client with Zod response validation.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Extends RestApiClientBase with an apiRequest() that validates the
|
|
5
|
+
* full response against a caller-supplied Zod schema and throws
|
|
6
|
+
* RestApiValidationError on mismatch.
|
|
5
7
|
*/
|
|
6
8
|
|
|
7
|
-
import type { PartialMessage } from "@bufbuild/protobuf";
|
|
8
9
|
import type { z } from "zod";
|
|
9
10
|
|
|
10
|
-
import type { Property } from "@superblocksteam/types/dist/src/common/v1/plugin_pb";
|
|
11
|
-
import type { Plugin as RestApiIntegrationPlugin } from "@superblocksteam/types/dist/src/plugins/restapiintegration/v1/plugin_pb";
|
|
12
|
-
|
|
13
11
|
import { RestApiValidationError } from "../../errors.js";
|
|
14
|
-
import type {
|
|
15
|
-
import
|
|
12
|
+
import type { TraceMetadata } from "../registry.js";
|
|
13
|
+
import { RestApiClientBase } from "./rest-api-client-base.js";
|
|
16
14
|
import type {
|
|
17
15
|
ApiRequestOptions,
|
|
18
16
|
ApiRequestSchema,
|
|
19
17
|
SupportsApiRequest,
|
|
20
18
|
} from "./types.js";
|
|
21
19
|
|
|
22
|
-
export type RestApiRequest = PartialMessage<RestApiIntegrationPlugin>;
|
|
23
|
-
|
|
24
20
|
/**
|
|
25
21
|
* Base implementation for REST API Integration clients.
|
|
26
22
|
*
|
|
27
|
-
* All OpenAPI-based integration clients
|
|
28
|
-
*
|
|
23
|
+
* All OpenAPI-based integration clients (except those with
|
|
24
|
+
* integration-specific response handling) extend this class to
|
|
25
|
+
* inherit the generic apiRequest() method with runtime Zod validation.
|
|
29
26
|
*/
|
|
30
27
|
export abstract class RestApiIntegrationClient
|
|
31
|
-
|
|
28
|
+
extends RestApiClientBase
|
|
29
|
+
implements SupportsApiRequest
|
|
32
30
|
{
|
|
33
|
-
readonly name: string;
|
|
34
|
-
readonly pluginId: string;
|
|
35
|
-
readonly config: IntegrationConfig;
|
|
36
|
-
|
|
37
|
-
protected readonly executeQuery: QueryExecutor;
|
|
38
|
-
|
|
39
|
-
constructor(config: IntegrationConfig, executeQuery: QueryExecutor) {
|
|
40
|
-
this.name = config.name;
|
|
41
|
-
this.pluginId = config.pluginId;
|
|
42
|
-
this.config = config;
|
|
43
|
-
this.executeQuery = executeQuery;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Helper to create a Property object for params.
|
|
48
|
-
*/
|
|
49
|
-
protected createParam(key: string, value: unknown): PartialMessage<Property> {
|
|
50
|
-
return {
|
|
51
|
-
key,
|
|
52
|
-
value: typeof value === "string" ? value : JSON.stringify(value),
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
31
|
async apiRequest<TBody, TResponse>(
|
|
57
32
|
options: ApiRequestOptions<TBody>,
|
|
58
33
|
schema: ApiRequestSchema<TBody, TResponse> & {
|
|
@@ -60,59 +35,7 @@ export abstract class RestApiIntegrationClient
|
|
|
60
35
|
},
|
|
61
36
|
metadata?: TraceMetadata,
|
|
62
37
|
): Promise<TResponse> {
|
|
63
|
-
|
|
64
|
-
// Body schema is optional — if not provided, the body is sent unvalidated.
|
|
65
|
-
if (options.body !== undefined && schema?.body) {
|
|
66
|
-
const bodyParseResult = schema.body.safeParse(options.body);
|
|
67
|
-
if (!bodyParseResult.success) {
|
|
68
|
-
throw new RestApiValidationError(
|
|
69
|
-
`Request body validation failed: ${bodyParseResult.error.message}`,
|
|
70
|
-
{
|
|
71
|
-
zodError: bodyParseResult.error,
|
|
72
|
-
data: options.body,
|
|
73
|
-
},
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Convert headers to Property array
|
|
79
|
-
const headers: PartialMessage<Property>[] = [];
|
|
80
|
-
if (options.headers) {
|
|
81
|
-
for (const [key, value] of Object.entries(options.headers)) {
|
|
82
|
-
headers.push(this.createParam(key, value));
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Convert query params to Property array
|
|
87
|
-
const params: PartialMessage<Property>[] = [];
|
|
88
|
-
if (options.params) {
|
|
89
|
-
for (const [key, value] of Object.entries(options.params)) {
|
|
90
|
-
params.push(this.createParam(key, value));
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Build the request using raw HTTP mode
|
|
95
|
-
const request: RestApiRequest = {
|
|
96
|
-
openApiAction: "genericHttpRequest",
|
|
97
|
-
httpMethod: options.method.toUpperCase(),
|
|
98
|
-
urlPath: options.path,
|
|
99
|
-
headers,
|
|
100
|
-
params,
|
|
101
|
-
responseType: "json",
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
// Add body if provided
|
|
105
|
-
if (options.body !== undefined) {
|
|
106
|
-
request.body = JSON.stringify(options.body);
|
|
107
|
-
request.bodyType = "jsonBody";
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Execute the request
|
|
111
|
-
const result = await this.executeQuery(
|
|
112
|
-
request as Record<string, unknown>,
|
|
113
|
-
undefined,
|
|
114
|
-
metadata,
|
|
115
|
-
);
|
|
38
|
+
const result = await this.executeApiRequest(options, schema.body, metadata);
|
|
116
39
|
|
|
117
40
|
// Response schema is REQUIRED - always validate
|
|
118
41
|
const responseParseResult = schema.response.safeParse(result);
|
|
@@ -54,7 +54,7 @@ export default api({
|
|
|
54
54
|
const result = await ctx.integrations.bitbucket.apiRequest(
|
|
55
55
|
{
|
|
56
56
|
method: "GET",
|
|
57
|
-
path: `/
|
|
57
|
+
path: `/repositories/${workspace}`,
|
|
58
58
|
params: {
|
|
59
59
|
pagelen: 50,
|
|
60
60
|
},
|
|
@@ -78,7 +78,7 @@ export default api({
|
|
|
78
78
|
const repo = await ctx.integrations.bitbucket.apiRequest(
|
|
79
79
|
{
|
|
80
80
|
method: "GET",
|
|
81
|
-
path: `/
|
|
81
|
+
path: `/repositories/${workspace}/${repoSlug}`,
|
|
82
82
|
},
|
|
83
83
|
{ response: RepositorySchema },
|
|
84
84
|
);
|
|
@@ -111,7 +111,7 @@ const PullRequestSchema = z.object({
|
|
|
111
111
|
const pr = await ctx.integrations.bitbucket.apiRequest(
|
|
112
112
|
{
|
|
113
113
|
method: "POST",
|
|
114
|
-
path: `/
|
|
114
|
+
path: `/repositories/${workspace}/${repoSlug}/pullrequests`,
|
|
115
115
|
body: {
|
|
116
116
|
title: "Add new feature",
|
|
117
117
|
description: "This PR adds...",
|
|
@@ -143,7 +143,7 @@ const ListPRsResponseSchema = z.object({
|
|
|
143
143
|
const result = await ctx.integrations.bitbucket.apiRequest(
|
|
144
144
|
{
|
|
145
145
|
method: "GET",
|
|
146
|
-
path: `/
|
|
146
|
+
path: `/repositories/${workspace}/${repoSlug}/pullrequests`,
|
|
147
147
|
params: {
|
|
148
148
|
state: "OPEN", // OPEN, MERGED, DECLINED, SUPERSEDED
|
|
149
149
|
pagelen: 25,
|
|
@@ -172,7 +172,7 @@ const MergeResponseSchema = z.object({
|
|
|
172
172
|
const result = await ctx.integrations.bitbucket.apiRequest(
|
|
173
173
|
{
|
|
174
174
|
method: "POST",
|
|
175
|
-
path: `/
|
|
175
|
+
path: `/repositories/${workspace}/${repoSlug}/pullrequests/${prId}/merge`,
|
|
176
176
|
body: {
|
|
177
177
|
type: "pullrequest",
|
|
178
178
|
message: "Merge pull request #123",
|
|
@@ -209,7 +209,7 @@ const ListCommitsResponseSchema = z.object({
|
|
|
209
209
|
const result = await ctx.integrations.bitbucket.apiRequest(
|
|
210
210
|
{
|
|
211
211
|
method: "GET",
|
|
212
|
-
path: `/
|
|
212
|
+
path: `/repositories/${workspace}/${repoSlug}/commits`,
|
|
213
213
|
params: {
|
|
214
214
|
branch: "main",
|
|
215
215
|
pagelen: 10,
|
|
@@ -232,7 +232,7 @@ const BranchSchema = z.object({
|
|
|
232
232
|
const branch = await ctx.integrations.bitbucket.apiRequest(
|
|
233
233
|
{
|
|
234
234
|
method: "POST",
|
|
235
|
-
path: `/
|
|
235
|
+
path: `/repositories/${workspace}/${repoSlug}/refs/branches`,
|
|
236
236
|
body: {
|
|
237
237
|
name: "feature/new-feature",
|
|
238
238
|
target: {
|
|
@@ -251,7 +251,7 @@ const branch = await ctx.integrations.bitbucket.apiRequest(
|
|
|
251
251
|
const content = await ctx.integrations.bitbucket.apiRequest(
|
|
252
252
|
{
|
|
253
253
|
method: "GET",
|
|
254
|
-
path: `/
|
|
254
|
+
path: `/repositories/${workspace}/${repoSlug}/src/main/README.md`,
|
|
255
255
|
},
|
|
256
256
|
{ response: z.string() },
|
|
257
257
|
);
|
|
@@ -274,7 +274,7 @@ const CommentSchema = z.object({
|
|
|
274
274
|
const comment = await ctx.integrations.bitbucket.apiRequest(
|
|
275
275
|
{
|
|
276
276
|
method: "POST",
|
|
277
|
-
path: `/
|
|
277
|
+
path: `/repositories/${workspace}/${repoSlug}/pullrequests/${prId}/comments`,
|
|
278
278
|
body: {
|
|
279
279
|
content: {
|
|
280
280
|
raw: "LGTM! :+1:",
|
|
@@ -300,20 +300,20 @@ await bitbucket.listRepos();
|
|
|
300
300
|
|
|
301
301
|
// CORRECT - Use apiRequest
|
|
302
302
|
await ctx.integrations.bitbucket.apiRequest(
|
|
303
|
-
{ method: "POST", path: `/
|
|
303
|
+
{ method: "POST", path: `/repositories/${workspace}/${repo}/pullrequests`, body: { ... } },
|
|
304
304
|
{ response: PullRequestSchema }
|
|
305
305
|
);
|
|
306
306
|
```
|
|
307
307
|
|
|
308
308
|
### API Version in Path
|
|
309
309
|
|
|
310
|
-
|
|
310
|
+
The base URL already includes `/2.0`, so do not repeat it in paths:
|
|
311
311
|
|
|
312
312
|
```typescript
|
|
313
|
-
//
|
|
313
|
+
// CORRECT - No version prefix (base URL already has /2.0)
|
|
314
314
|
const path = `/repositories/${workspace}/${repo}`;
|
|
315
315
|
|
|
316
|
-
//
|
|
316
|
+
// WRONG - Duplicates version from base URL
|
|
317
317
|
const path = `/2.0/repositories/${workspace}/${repo}`;
|
|
318
318
|
```
|
|
319
319
|
|
|
@@ -323,10 +323,10 @@ Use workspace slug for API calls:
|
|
|
323
323
|
|
|
324
324
|
```typescript
|
|
325
325
|
// Workspace slug (preferred)
|
|
326
|
-
const path = `/
|
|
326
|
+
const path = `/repositories/myworkspace/myrepo`;
|
|
327
327
|
|
|
328
328
|
// Can also be username for personal repos
|
|
329
|
-
const path = `/
|
|
329
|
+
const path = `/repositories/myusername/myrepo`;
|
|
330
330
|
```
|
|
331
331
|
|
|
332
332
|
### Pagination
|
|
@@ -340,7 +340,7 @@ async function getAllPRs(
|
|
|
340
340
|
repo: string,
|
|
341
341
|
) {
|
|
342
342
|
const allPRs: PullRequest[] = [];
|
|
343
|
-
let url = `/
|
|
343
|
+
let url = `/repositories/${workspace}/${repo}/pullrequests`;
|
|
344
344
|
|
|
345
345
|
while (url) {
|
|
346
346
|
const result = await ctx.integrations.bitbucket.apiRequest(
|
|
@@ -385,7 +385,7 @@ Branch names with slashes need encoding:
|
|
|
385
385
|
```typescript
|
|
386
386
|
// Branch name: feature/my-feature
|
|
387
387
|
const branchName = encodeURIComponent("feature/my-feature");
|
|
388
|
-
const path = `/
|
|
388
|
+
const path = `/repositories/${workspace}/${repo}/src/${branchName}/file.txt`;
|
|
389
389
|
```
|
|
390
390
|
|
|
391
391
|
### Raw File Content
|
|
@@ -397,7 +397,7 @@ File content endpoints return raw text, not JSON:
|
|
|
397
397
|
const content = await ctx.integrations.bitbucket.apiRequest(
|
|
398
398
|
{
|
|
399
399
|
method: "GET",
|
|
400
|
-
path: `/
|
|
400
|
+
path: `/repositories/${workspace}/${repo}/src/main/file.txt`,
|
|
401
401
|
},
|
|
402
402
|
{ response: z.string() }, // Not a JSON schema
|
|
403
403
|
);
|
|
@@ -410,7 +410,7 @@ import { RestApiValidationError } from "@superblocksteam/sdk-api";
|
|
|
410
410
|
|
|
411
411
|
try {
|
|
412
412
|
const result = await ctx.integrations.bitbucket.apiRequest(
|
|
413
|
-
{ method: "GET", path: `/
|
|
413
|
+
{ method: "GET", path: `/repositories/${workspace}/${repo}` },
|
|
414
414
|
{ response: RepositorySchema },
|
|
415
415
|
);
|
|
416
416
|
} catch (error) {
|