@fourier-labs/harbour-contracts 0.74.0 → 0.77.0
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 +4 -4
- package/contract-release.json +26 -26
- package/dist/gen-ts/app-integrations-declaration.d.ts +15 -1
- package/dist/gen-ts/contract-release.d.ts +6 -6
- package/dist/gen-ts/contract-release.js +6 -6
- package/dist/gen-ts/data-grant-v2.d.ts +5 -5
- package/dist/gen-ts/deploy-request.d.ts +3 -3
- package/dist/gen-ts/deployment-source.d.ts +4 -4
- package/dist/gen-ts/integration-execute-input-rules.d.ts +214 -0
- package/dist/gen-ts/integration-execute-input-rules.js +85 -0
- package/dist/gen-ts/integration-execute-request.d.ts +53 -6
- package/dist/gen-ts/integration-execute-result.d.ts +161 -1
- package/dist/gen-ts/integration-operations.d.ts +77 -0
- package/dist/gen-ts/integration-operations.js +99 -0
- package/dist/gen-ts/saas-connection.d.ts +22 -8
- package/gen-ts/app-integrations-declaration.ts +15 -1
- package/gen-ts/contract-release.ts +6 -6
- package/gen-ts/data-grant-v2.ts +15 -4
- package/gen-ts/deploy-request.ts +3 -3
- package/gen-ts/deployment-source.ts +4 -4
- package/gen-ts/integration-execute-input-rules.ts +86 -1
- package/gen-ts/integration-execute-request.ts +63 -5
- package/gen-ts/integration-execute-result.ts +161 -1
- package/gen-ts/integration-operations.ts +99 -0
- package/gen-ts/saas-connection.ts +32 -7
- package/package.json +1 -1
- package/schema/app-integrations-declaration.schema.json +233 -2
- package/schema/data-grant-v2.schema.json +4 -4
- package/schema/deploy-request.schema.json +4 -4
- package/schema/deployment-source.schema.json +9 -5
- package/schema/integration-execute-request.schema.json +529 -4
- package/schema/integration-execute-result.schema.json +542 -1
- package/schema/saas-connection.schema.json +35 -7
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"$id": "https://schemas.harbour.dev/integration-execute-result.schema.json",
|
|
4
4
|
"title": "IntegrationExecuteResult",
|
|
5
|
-
"description": "The `data` member of a successful execute response `{data, requestId}`; the SDK unwraps `data`. Exactly one of the operation results (
|
|
5
|
+
"description": "The `data` member of a successful execute response `{data, requestId}`; the SDK unwraps `data`. Exactly one of the operation results (twenty-four operations since 0.76.0, ADR 0018 and ADR 0022): slack.channel.history -> {messages, hasMore, nextCursor?} (plain text/IDs/time, plus the display name and emoji icon a per-app-named app post carries \u2014 authorName/authorIcon, 0.55.0 \u2014 never the provider payload; `nextCursor` when hasMore, to pass as the next read's `cursor`); slack.message.post, slack.message.update, slack.message.delete -> {messageId} (the Slack ts); slack.dm.send -> {messageId, channelId} (the ts and the DM channel it went to); slack.user.lookup -> {userId, displayName}; gmail.thread.list -> {threads, hasMore, nextPageToken?} (newest matching threads; each `messageId` is the thread's newest message, what gmail.message.read takes; `nextPageToken` when hasMore); gmail.thread.read -> {id, emails} (every message of the thread, oldest first, each shaped like a gmail.message.read result); gmail.message.read -> {id, threadId, subject, from, to, date, text, truncated, attachments?} (headers plus the plain text with html stripped, cut at 16 KiB with `truncated` set; `attachments` lists the files without their bytes, which gmail.attachment.read fetches by id); gmail.attachment.read -> {filename, mimeType, size, dataBase64}; gmail.message.send -> {messageId, threadId} (the Gmail ids of the sent message and its thread); warehouse.view.read -> {rows, truncated} (rows keyed by the selected columns, or for an aggregate read by the groupBy columns then the aggregate aliases; decimal/64-bit values as strings, dates as ISO strings; since 0.70.0 every server sends `truncated`, true when more rows matched than `limit` \u2014 the server reads limit+1 rows to know \u2014 so the rows are incomplete and must not be totalled or averaged; a server before 0.70.0 omitted it). Basecamp (0.76.0, ADR 0022; every id a string of digits, every page at most 100 items, `nextPageToken` only when hasMore, text cut at 16 KiB like Gmail): basecamp.project.list -> {projects, hasMore, nextPageToken?}; basecamp.todolist.list -> {todolists, hasMore, nextPageToken?}; basecamp.todo.list -> {todos, hasMore, nextPageToken?}; basecamp.message.list -> {boardMessages, hasMore, nextPageToken?} (`boardMessages`, not `messages`, which is the Slack history shape); basecamp.comment.list -> {comments, hasMore, nextPageToken?}; basecamp.people.list -> {people, hasMore, nextPageToken?}; basecamp.todo.create, basecamp.message.post, basecamp.comment.post, basecamp.campfire.post -> {id, appUrl} (the new recording and its Basecamp page); basecamp.todo.complete -> {id, completed} with completed always true. An unknown send outcome is never a result; it is an error with details.code SEND_OUTCOME_UNKNOWN. A whole result over 1 MiB is rejected, never cut down to fit \u2014 except a gmail.attachment.read, whose single attachment is bounded by its own 2 MiB.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"properties": {
|
|
@@ -139,6 +139,65 @@
|
|
|
139
139
|
"description": "One row keyed by the selected column names, or by the groupBy columns then the aggregate aliases."
|
|
140
140
|
}
|
|
141
141
|
},
|
|
142
|
+
"projects": {
|
|
143
|
+
"type": "array",
|
|
144
|
+
"maxItems": 100,
|
|
145
|
+
"items": {
|
|
146
|
+
"$ref": "#/definitions/BasecampProject"
|
|
147
|
+
},
|
|
148
|
+
"description": "basecamp.project.list (0.76.0): one page of the pinned account's projects the signed-in person can see."
|
|
149
|
+
},
|
|
150
|
+
"todolists": {
|
|
151
|
+
"type": "array",
|
|
152
|
+
"maxItems": 100,
|
|
153
|
+
"items": {
|
|
154
|
+
"$ref": "#/definitions/BasecampTodolist"
|
|
155
|
+
},
|
|
156
|
+
"description": "basecamp.todolist.list (0.76.0): one page of the project's to-do lists."
|
|
157
|
+
},
|
|
158
|
+
"todos": {
|
|
159
|
+
"type": "array",
|
|
160
|
+
"maxItems": 100,
|
|
161
|
+
"items": {
|
|
162
|
+
"$ref": "#/definitions/BasecampTodo"
|
|
163
|
+
},
|
|
164
|
+
"description": "basecamp.todo.list (0.76.0): one page of the list's open to-dos, or its completed ones when the read said `completed`."
|
|
165
|
+
},
|
|
166
|
+
"boardMessages": {
|
|
167
|
+
"type": "array",
|
|
168
|
+
"maxItems": 100,
|
|
169
|
+
"items": {
|
|
170
|
+
"$ref": "#/definitions/BasecampMessage"
|
|
171
|
+
},
|
|
172
|
+
"description": "basecamp.message.list (0.76.0): one page of the project's message board, newest first. Named apart from `messages`, the Slack history shape."
|
|
173
|
+
},
|
|
174
|
+
"comments": {
|
|
175
|
+
"type": "array",
|
|
176
|
+
"maxItems": 100,
|
|
177
|
+
"items": {
|
|
178
|
+
"$ref": "#/definitions/BasecampComment"
|
|
179
|
+
},
|
|
180
|
+
"description": "basecamp.comment.list (0.76.0): one page of the recording's comments, oldest first."
|
|
181
|
+
},
|
|
182
|
+
"people": {
|
|
183
|
+
"type": "array",
|
|
184
|
+
"maxItems": 100,
|
|
185
|
+
"items": {
|
|
186
|
+
"$ref": "#/definitions/BasecampPerson"
|
|
187
|
+
},
|
|
188
|
+
"description": "basecamp.people.list (0.76.0): one page of the people on the project."
|
|
189
|
+
},
|
|
190
|
+
"appUrl": {
|
|
191
|
+
"type": "string",
|
|
192
|
+
"format": "uri",
|
|
193
|
+
"minLength": 1,
|
|
194
|
+
"maxLength": 2048,
|
|
195
|
+
"description": "basecamp.todo.create / basecamp.message.post / basecamp.comment.post / basecamp.campfire.post (0.76.0): the Basecamp page of the new recording, for a person to open."
|
|
196
|
+
},
|
|
197
|
+
"completed": {
|
|
198
|
+
"type": "boolean",
|
|
199
|
+
"description": "basecamp.todo.complete (0.76.0): always true; the to-do is complete whether this call or an earlier one completed it."
|
|
200
|
+
},
|
|
142
201
|
"status": {
|
|
143
202
|
"type": "integer"
|
|
144
203
|
},
|
|
@@ -256,6 +315,98 @@
|
|
|
256
315
|
"sample"
|
|
257
316
|
]
|
|
258
317
|
}
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
"required": [
|
|
321
|
+
"projects",
|
|
322
|
+
"hasMore"
|
|
323
|
+
],
|
|
324
|
+
"propertyNames": {
|
|
325
|
+
"enum": [
|
|
326
|
+
"projects",
|
|
327
|
+
"hasMore",
|
|
328
|
+
"nextPageToken"
|
|
329
|
+
]
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
"required": [
|
|
334
|
+
"todolists",
|
|
335
|
+
"hasMore"
|
|
336
|
+
],
|
|
337
|
+
"propertyNames": {
|
|
338
|
+
"enum": [
|
|
339
|
+
"todolists",
|
|
340
|
+
"hasMore",
|
|
341
|
+
"nextPageToken"
|
|
342
|
+
]
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
"required": [
|
|
347
|
+
"todos",
|
|
348
|
+
"hasMore"
|
|
349
|
+
],
|
|
350
|
+
"propertyNames": {
|
|
351
|
+
"enum": [
|
|
352
|
+
"todos",
|
|
353
|
+
"hasMore",
|
|
354
|
+
"nextPageToken"
|
|
355
|
+
]
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
"required": [
|
|
360
|
+
"boardMessages",
|
|
361
|
+
"hasMore"
|
|
362
|
+
],
|
|
363
|
+
"propertyNames": {
|
|
364
|
+
"enum": [
|
|
365
|
+
"boardMessages",
|
|
366
|
+
"hasMore",
|
|
367
|
+
"nextPageToken"
|
|
368
|
+
]
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
"required": [
|
|
373
|
+
"comments",
|
|
374
|
+
"hasMore"
|
|
375
|
+
],
|
|
376
|
+
"propertyNames": {
|
|
377
|
+
"enum": [
|
|
378
|
+
"comments",
|
|
379
|
+
"hasMore",
|
|
380
|
+
"nextPageToken"
|
|
381
|
+
]
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
"required": [
|
|
386
|
+
"people",
|
|
387
|
+
"hasMore"
|
|
388
|
+
],
|
|
389
|
+
"propertyNames": {
|
|
390
|
+
"enum": [
|
|
391
|
+
"people",
|
|
392
|
+
"hasMore",
|
|
393
|
+
"nextPageToken"
|
|
394
|
+
]
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
"required": [
|
|
399
|
+
"id",
|
|
400
|
+
"appUrl"
|
|
401
|
+
],
|
|
402
|
+
"maxProperties": 2
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
"required": [
|
|
406
|
+
"id",
|
|
407
|
+
"completed"
|
|
408
|
+
],
|
|
409
|
+
"maxProperties": 2
|
|
259
410
|
}
|
|
260
411
|
],
|
|
261
412
|
"definitions": {
|
|
@@ -427,6 +578,280 @@
|
|
|
427
578
|
"description": "The emoji shortcode such a post carries as its icon (0.55.0), when it has one; never an image URL."
|
|
428
579
|
}
|
|
429
580
|
}
|
|
581
|
+
},
|
|
582
|
+
"BasecampPersonRef": {
|
|
583
|
+
"type": "object",
|
|
584
|
+
"additionalProperties": false,
|
|
585
|
+
"required": [
|
|
586
|
+
"id",
|
|
587
|
+
"name"
|
|
588
|
+
],
|
|
589
|
+
"description": "A person named on a Basecamp recording (0.76.0): an author or an assignee.",
|
|
590
|
+
"properties": {
|
|
591
|
+
"id": {
|
|
592
|
+
"$ref": "app-integrations-declaration.schema.json#/definitions/BasecampId"
|
|
593
|
+
},
|
|
594
|
+
"name": {
|
|
595
|
+
"type": "string",
|
|
596
|
+
"maxLength": 256
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
},
|
|
600
|
+
"BasecampTool": {
|
|
601
|
+
"type": "object",
|
|
602
|
+
"additionalProperties": false,
|
|
603
|
+
"required": [
|
|
604
|
+
"id",
|
|
605
|
+
"name",
|
|
606
|
+
"title"
|
|
607
|
+
],
|
|
608
|
+
"description": "One enabled tool of a Basecamp project (0.76.0), from its dock: `name` is Basecamp's kind (todoset, message_board, chat, schedule, vault, questionnaire, inbox, kanban_board), `title` what the project calls it.",
|
|
609
|
+
"properties": {
|
|
610
|
+
"id": {
|
|
611
|
+
"$ref": "app-integrations-declaration.schema.json#/definitions/BasecampId"
|
|
612
|
+
},
|
|
613
|
+
"name": {
|
|
614
|
+
"type": "string",
|
|
615
|
+
"minLength": 1,
|
|
616
|
+
"maxLength": 64
|
|
617
|
+
},
|
|
618
|
+
"title": {
|
|
619
|
+
"type": "string",
|
|
620
|
+
"maxLength": 256
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
},
|
|
624
|
+
"BasecampProject": {
|
|
625
|
+
"type": "object",
|
|
626
|
+
"additionalProperties": false,
|
|
627
|
+
"required": [
|
|
628
|
+
"id",
|
|
629
|
+
"name",
|
|
630
|
+
"appUrl",
|
|
631
|
+
"tools"
|
|
632
|
+
],
|
|
633
|
+
"description": "One Basecamp project of a basecamp.project.list result (0.76.0). `id` is what IT maps a project alias to on the connection.",
|
|
634
|
+
"properties": {
|
|
635
|
+
"id": {
|
|
636
|
+
"$ref": "app-integrations-declaration.schema.json#/definitions/BasecampId"
|
|
637
|
+
},
|
|
638
|
+
"name": {
|
|
639
|
+
"type": "string",
|
|
640
|
+
"maxLength": 256
|
|
641
|
+
},
|
|
642
|
+
"description": {
|
|
643
|
+
"type": "string",
|
|
644
|
+
"maxLength": 16384
|
|
645
|
+
},
|
|
646
|
+
"appUrl": {
|
|
647
|
+
"type": "string",
|
|
648
|
+
"format": "uri",
|
|
649
|
+
"minLength": 1,
|
|
650
|
+
"maxLength": 2048
|
|
651
|
+
},
|
|
652
|
+
"tools": {
|
|
653
|
+
"type": "array",
|
|
654
|
+
"maxItems": 32,
|
|
655
|
+
"items": {
|
|
656
|
+
"$ref": "#/definitions/BasecampTool"
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
},
|
|
661
|
+
"BasecampTodolist": {
|
|
662
|
+
"type": "object",
|
|
663
|
+
"additionalProperties": false,
|
|
664
|
+
"required": [
|
|
665
|
+
"id",
|
|
666
|
+
"name",
|
|
667
|
+
"completedRatio",
|
|
668
|
+
"appUrl"
|
|
669
|
+
],
|
|
670
|
+
"description": "One to-do list of a basecamp.todolist.list result (0.76.0); `id` is what basecamp.todo.list and basecamp.todo.create take as `todolistId`.",
|
|
671
|
+
"properties": {
|
|
672
|
+
"id": {
|
|
673
|
+
"$ref": "app-integrations-declaration.schema.json#/definitions/BasecampId"
|
|
674
|
+
},
|
|
675
|
+
"name": {
|
|
676
|
+
"type": "string",
|
|
677
|
+
"maxLength": 256
|
|
678
|
+
},
|
|
679
|
+
"description": {
|
|
680
|
+
"type": "string",
|
|
681
|
+
"maxLength": 16384
|
|
682
|
+
},
|
|
683
|
+
"completedRatio": {
|
|
684
|
+
"type": "string",
|
|
685
|
+
"maxLength": 32,
|
|
686
|
+
"description": "Basecamp's `completed_ratio`, e.g. `3/10`."
|
|
687
|
+
},
|
|
688
|
+
"appUrl": {
|
|
689
|
+
"type": "string",
|
|
690
|
+
"format": "uri",
|
|
691
|
+
"minLength": 1,
|
|
692
|
+
"maxLength": 2048
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
},
|
|
696
|
+
"BasecampTodo": {
|
|
697
|
+
"type": "object",
|
|
698
|
+
"additionalProperties": false,
|
|
699
|
+
"required": [
|
|
700
|
+
"id",
|
|
701
|
+
"content",
|
|
702
|
+
"description",
|
|
703
|
+
"completed",
|
|
704
|
+
"assignees",
|
|
705
|
+
"appUrl",
|
|
706
|
+
"createdAt"
|
|
707
|
+
],
|
|
708
|
+
"description": "One to-do of a basecamp.todo.list result (0.76.0): `content` is its title, `description` its notes as plain text (empty when none), each cut at 16 KiB.",
|
|
709
|
+
"properties": {
|
|
710
|
+
"id": {
|
|
711
|
+
"$ref": "app-integrations-declaration.schema.json#/definitions/BasecampId"
|
|
712
|
+
},
|
|
713
|
+
"content": {
|
|
714
|
+
"type": "string",
|
|
715
|
+
"maxLength": 16384
|
|
716
|
+
},
|
|
717
|
+
"description": {
|
|
718
|
+
"type": "string",
|
|
719
|
+
"maxLength": 16384
|
|
720
|
+
},
|
|
721
|
+
"completed": {
|
|
722
|
+
"type": "boolean"
|
|
723
|
+
},
|
|
724
|
+
"dueOn": {
|
|
725
|
+
"type": "string",
|
|
726
|
+
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
|
|
727
|
+
},
|
|
728
|
+
"assignees": {
|
|
729
|
+
"type": "array",
|
|
730
|
+
"maxItems": 100,
|
|
731
|
+
"items": {
|
|
732
|
+
"$ref": "#/definitions/BasecampPersonRef"
|
|
733
|
+
}
|
|
734
|
+
},
|
|
735
|
+
"appUrl": {
|
|
736
|
+
"type": "string",
|
|
737
|
+
"format": "uri",
|
|
738
|
+
"minLength": 1,
|
|
739
|
+
"maxLength": 2048
|
|
740
|
+
},
|
|
741
|
+
"createdAt": {
|
|
742
|
+
"type": "string",
|
|
743
|
+
"format": "date-time"
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
},
|
|
747
|
+
"BasecampMessage": {
|
|
748
|
+
"type": "object",
|
|
749
|
+
"additionalProperties": false,
|
|
750
|
+
"required": [
|
|
751
|
+
"id",
|
|
752
|
+
"subject",
|
|
753
|
+
"content",
|
|
754
|
+
"author",
|
|
755
|
+
"createdAt",
|
|
756
|
+
"appUrl",
|
|
757
|
+
"commentsCount"
|
|
758
|
+
],
|
|
759
|
+
"description": "One message-board post of a basecamp.message.list result (0.76.0); `content` is plain text cut at 16 KiB; `id` is a recordingId for basecamp.comment.list and basecamp.comment.post.",
|
|
760
|
+
"properties": {
|
|
761
|
+
"id": {
|
|
762
|
+
"$ref": "app-integrations-declaration.schema.json#/definitions/BasecampId"
|
|
763
|
+
},
|
|
764
|
+
"subject": {
|
|
765
|
+
"type": "string",
|
|
766
|
+
"maxLength": 2048
|
|
767
|
+
},
|
|
768
|
+
"content": {
|
|
769
|
+
"type": "string",
|
|
770
|
+
"maxLength": 16384
|
|
771
|
+
},
|
|
772
|
+
"author": {
|
|
773
|
+
"$ref": "#/definitions/BasecampPersonRef"
|
|
774
|
+
},
|
|
775
|
+
"createdAt": {
|
|
776
|
+
"type": "string",
|
|
777
|
+
"format": "date-time"
|
|
778
|
+
},
|
|
779
|
+
"appUrl": {
|
|
780
|
+
"type": "string",
|
|
781
|
+
"format": "uri",
|
|
782
|
+
"minLength": 1,
|
|
783
|
+
"maxLength": 2048
|
|
784
|
+
},
|
|
785
|
+
"commentsCount": {
|
|
786
|
+
"type": "integer",
|
|
787
|
+
"minimum": 0
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
},
|
|
791
|
+
"BasecampComment": {
|
|
792
|
+
"type": "object",
|
|
793
|
+
"additionalProperties": false,
|
|
794
|
+
"required": [
|
|
795
|
+
"id",
|
|
796
|
+
"content",
|
|
797
|
+
"author",
|
|
798
|
+
"createdAt",
|
|
799
|
+
"appUrl"
|
|
800
|
+
],
|
|
801
|
+
"description": "One comment of a basecamp.comment.list result (0.76.0); `content` is plain text cut at 16 KiB.",
|
|
802
|
+
"properties": {
|
|
803
|
+
"id": {
|
|
804
|
+
"$ref": "app-integrations-declaration.schema.json#/definitions/BasecampId"
|
|
805
|
+
},
|
|
806
|
+
"content": {
|
|
807
|
+
"type": "string",
|
|
808
|
+
"maxLength": 16384
|
|
809
|
+
},
|
|
810
|
+
"author": {
|
|
811
|
+
"$ref": "#/definitions/BasecampPersonRef"
|
|
812
|
+
},
|
|
813
|
+
"createdAt": {
|
|
814
|
+
"type": "string",
|
|
815
|
+
"format": "date-time"
|
|
816
|
+
},
|
|
817
|
+
"appUrl": {
|
|
818
|
+
"type": "string",
|
|
819
|
+
"format": "uri",
|
|
820
|
+
"minLength": 1,
|
|
821
|
+
"maxLength": 2048
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
},
|
|
825
|
+
"BasecampPerson": {
|
|
826
|
+
"type": "object",
|
|
827
|
+
"additionalProperties": false,
|
|
828
|
+
"required": [
|
|
829
|
+
"id",
|
|
830
|
+
"name",
|
|
831
|
+
"admin"
|
|
832
|
+
],
|
|
833
|
+
"description": "One person of a basecamp.people.list result (0.76.0); `id` is what basecamp.todo.create takes in `assigneeIds`. `email` and `title` when Basecamp shows them.",
|
|
834
|
+
"properties": {
|
|
835
|
+
"id": {
|
|
836
|
+
"$ref": "app-integrations-declaration.schema.json#/definitions/BasecampId"
|
|
837
|
+
},
|
|
838
|
+
"name": {
|
|
839
|
+
"type": "string",
|
|
840
|
+
"maxLength": 256
|
|
841
|
+
},
|
|
842
|
+
"email": {
|
|
843
|
+
"type": "string",
|
|
844
|
+
"maxLength": 254
|
|
845
|
+
},
|
|
846
|
+
"title": {
|
|
847
|
+
"type": "string",
|
|
848
|
+
"maxLength": 256
|
|
849
|
+
},
|
|
850
|
+
"admin": {
|
|
851
|
+
"type": "boolean",
|
|
852
|
+
"description": "Whether the person is an administrator of the Basecamp account."
|
|
853
|
+
}
|
|
854
|
+
}
|
|
430
855
|
}
|
|
431
856
|
},
|
|
432
857
|
"examples": [
|
|
@@ -594,6 +1019,122 @@
|
|
|
594
1019
|
],
|
|
595
1020
|
"truncated": false,
|
|
596
1021
|
"sample": true
|
|
1022
|
+
},
|
|
1023
|
+
{
|
|
1024
|
+
"projects": [
|
|
1025
|
+
{
|
|
1026
|
+
"id": "2085958499",
|
|
1027
|
+
"name": "Launch",
|
|
1028
|
+
"description": "Everything for the October launch.",
|
|
1029
|
+
"appUrl": "https://3.basecamp.com/195539477/projects/2085958499",
|
|
1030
|
+
"tools": [
|
|
1031
|
+
{
|
|
1032
|
+
"id": "1069479338",
|
|
1033
|
+
"name": "todoset",
|
|
1034
|
+
"title": "To-dos"
|
|
1035
|
+
},
|
|
1036
|
+
{
|
|
1037
|
+
"id": "1069479339",
|
|
1038
|
+
"name": "message_board",
|
|
1039
|
+
"title": "Message Board"
|
|
1040
|
+
},
|
|
1041
|
+
{
|
|
1042
|
+
"id": "1069479340",
|
|
1043
|
+
"name": "chat",
|
|
1044
|
+
"title": "Campfire"
|
|
1045
|
+
}
|
|
1046
|
+
]
|
|
1047
|
+
}
|
|
1048
|
+
],
|
|
1049
|
+
"hasMore": false
|
|
1050
|
+
},
|
|
1051
|
+
{
|
|
1052
|
+
"todolists": [
|
|
1053
|
+
{
|
|
1054
|
+
"id": "1069479520",
|
|
1055
|
+
"name": "Week 42",
|
|
1056
|
+
"completedRatio": "3/10",
|
|
1057
|
+
"appUrl": "https://3.basecamp.com/195539477/buckets/2085958499/todolists/1069479520"
|
|
1058
|
+
}
|
|
1059
|
+
],
|
|
1060
|
+
"hasMore": true,
|
|
1061
|
+
"nextPageToken": "2"
|
|
1062
|
+
},
|
|
1063
|
+
{
|
|
1064
|
+
"todos": [
|
|
1065
|
+
{
|
|
1066
|
+
"id": "1069479526",
|
|
1067
|
+
"content": "Ship the weekly report",
|
|
1068
|
+
"description": "Attach the PDF to the message board post.",
|
|
1069
|
+
"completed": false,
|
|
1070
|
+
"dueOn": "2026-10-17",
|
|
1071
|
+
"assignees": [
|
|
1072
|
+
{
|
|
1073
|
+
"id": "1049715914",
|
|
1074
|
+
"name": "Jane Doe"
|
|
1075
|
+
}
|
|
1076
|
+
],
|
|
1077
|
+
"appUrl": "https://3.basecamp.com/195539477/buckets/2085958499/todos/1069479526",
|
|
1078
|
+
"createdAt": "2026-10-11T08:00:00Z"
|
|
1079
|
+
}
|
|
1080
|
+
],
|
|
1081
|
+
"hasMore": false
|
|
1082
|
+
},
|
|
1083
|
+
{
|
|
1084
|
+
"boardMessages": [
|
|
1085
|
+
{
|
|
1086
|
+
"id": "1069479530",
|
|
1087
|
+
"subject": "Weekly report",
|
|
1088
|
+
"content": "The weekly report is attached.",
|
|
1089
|
+
"author": {
|
|
1090
|
+
"id": "1049715914",
|
|
1091
|
+
"name": "Jane Doe"
|
|
1092
|
+
},
|
|
1093
|
+
"createdAt": "2026-10-11T08:00:00Z",
|
|
1094
|
+
"appUrl": "https://3.basecamp.com/195539477/buckets/2085958499/messages/1069479530",
|
|
1095
|
+
"commentsCount": 1
|
|
1096
|
+
}
|
|
1097
|
+
],
|
|
1098
|
+
"hasMore": false
|
|
1099
|
+
},
|
|
1100
|
+
{
|
|
1101
|
+
"comments": [
|
|
1102
|
+
{
|
|
1103
|
+
"id": "1069479531",
|
|
1104
|
+
"content": "Shipped, thanks.",
|
|
1105
|
+
"author": {
|
|
1106
|
+
"id": "1049715915",
|
|
1107
|
+
"name": "Sam Lee"
|
|
1108
|
+
},
|
|
1109
|
+
"createdAt": "2026-10-11T09:00:00Z",
|
|
1110
|
+
"appUrl": "https://3.basecamp.com/195539477/buckets/2085958499/messages/1069479530#__recording_1069479531"
|
|
1111
|
+
}
|
|
1112
|
+
],
|
|
1113
|
+
"hasMore": false
|
|
1114
|
+
},
|
|
1115
|
+
{
|
|
1116
|
+
"people": [
|
|
1117
|
+
{
|
|
1118
|
+
"id": "1049715914",
|
|
1119
|
+
"name": "Jane Doe",
|
|
1120
|
+
"email": "jane@example.com",
|
|
1121
|
+
"title": "Product",
|
|
1122
|
+
"admin": true
|
|
1123
|
+
}
|
|
1124
|
+
],
|
|
1125
|
+
"hasMore": false
|
|
1126
|
+
},
|
|
1127
|
+
{
|
|
1128
|
+
"id": "1069479526",
|
|
1129
|
+
"appUrl": "https://3.basecamp.com/195539477/buckets/2085958499/todos/1069479526"
|
|
1130
|
+
},
|
|
1131
|
+
{
|
|
1132
|
+
"id": "1069479540",
|
|
1133
|
+
"appUrl": "https://3.basecamp.com/195539477/buckets/2085958499/chats/1069479340@1069479540"
|
|
1134
|
+
},
|
|
1135
|
+
{
|
|
1136
|
+
"id": "1069479526",
|
|
1137
|
+
"completed": true
|
|
597
1138
|
}
|
|
598
1139
|
]
|
|
599
1140
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"$id": "https://schemas.harbour.dev/saas-connection.schema.json",
|
|
4
4
|
"title": "SaasConnection",
|
|
5
|
-
"description": "Tenant-wide SaaS connection record (control table sk SAASCONN#<sourceId>, same tenant partition as database sources; alias uniqueness is enforced across both). One connection per tenant and provider: a single bot installation of the platform-owned provider app and a single logical channel map shared by every app environment (grants, requests and user consent links stay per environment). A Gmail connection has neither installation nor channels: user consent is the only step and its one resource is the fixed `inbox` alias. Contains references, never secrets: installation tokens live under their own token records. The pre-T25 per-environment shape (`clientId`, `clientSecretRef`, `environments`, `installations`) is deprecated: the control plane normalises such a stored record on read and never writes it.",
|
|
5
|
+
"description": "Tenant-wide SaaS connection record (control table sk SAASCONN#<sourceId>, same tenant partition as database sources; alias uniqueness is enforced across both). One connection per tenant and provider: a single bot installation of the platform-owned provider app and a single logical channel map shared by every app environment (grants, requests and user consent links stay per environment). A Gmail connection has neither installation nor channels: user consent is the only step and its one resource is the fixed `inbox` alias. A Basecamp connection (0.76.0, ADR 0022) has no installation either: IT pins the company's Basecamp `account` (chosen from the accounts IT's own Basecamp sign-in belongs to) and maps project aliases to project ids in `channels`; basecamp.project.list runs on the fixed `account` alias. Contains references, never secrets: installation tokens live under their own token records. The pre-T25 per-environment shape (`clientId`, `clientSecretRef`, `environments`, `installations`) is deprecated: the control plane normalises such a stored record on read and never writes it.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"required": ["schema", "tenantId", "sourceId", "kind", "provider", "displayName", "status", "version", "channels", "operations", "developmentPreapproval", "createdAt", "updatedAt"],
|
|
@@ -16,21 +16,22 @@
|
|
|
16
16
|
"status": { "$ref": "#/definitions/SaasConnectionStatus" },
|
|
17
17
|
"version": { "type": "integer", "minimum": 1, "description": "Optimistic-concurrency version; every write is conditional on it." },
|
|
18
18
|
"installation": { "$ref": "#/definitions/SaasInstallation" },
|
|
19
|
+
"account": { "$ref": "#/definitions/BasecampAccount" },
|
|
19
20
|
"channels": {
|
|
20
21
|
"type": "object",
|
|
21
22
|
"maxProperties": 64,
|
|
22
23
|
"propertyNames": { "$ref": "app-integrations-declaration.schema.json#/definitions/IntegrationAlias" },
|
|
23
|
-
"additionalProperties": { "type": "string", "pattern": "^[A-
|
|
24
|
-
"description": "Logical
|
|
24
|
+
"additionalProperties": { "type": "string", "pattern": "^[A-Za-z0-9]{1,32}$", "description": "A Slack channel id (public, non-archived) or a Basecamp project id (digits)." },
|
|
25
|
+
"description": "Logical alias → provider resource id: a Slack channel id or a Basecamp project id. Always empty for Gmail."
|
|
25
26
|
},
|
|
26
27
|
"operations": {
|
|
27
28
|
"type": "array",
|
|
28
29
|
"minItems": 1,
|
|
29
|
-
"maxItems":
|
|
30
|
+
"maxItems": 22,
|
|
30
31
|
"uniqueItems": true,
|
|
31
32
|
"items": { "$ref": "app-integrations-declaration.schema.json#/definitions/IntegrationOperation" },
|
|
32
33
|
"not": { "contains": { "const": "warehouse.view.read" } },
|
|
33
|
-
"description": "Operations IT allows apps to request on this connection: the provider's own SaaS operations (Slack: slack.channel.history, slack.message.post, slack.message.update, slack.message.delete, slack.dm.send, slack.user.lookup; Gmail: gmail.thread.list, gmail.thread.read, gmail.message.read, gmail.message.send, gmail.attachment.read). Never warehouse.view.read."
|
|
34
|
+
"description": "Operations IT allows apps to request on this connection: the provider's own SaaS operations (Slack: slack.channel.history, slack.message.post, slack.message.update, slack.message.delete, slack.dm.send, slack.user.lookup; Gmail: gmail.thread.list, gmail.thread.read, gmail.message.read, gmail.message.send, gmail.attachment.read; Basecamp (0.76.0): basecamp.project.list, basecamp.todolist.list, basecamp.todo.list, basecamp.todo.create, basecamp.todo.complete, basecamp.message.list, basecamp.message.post, basecamp.comment.list, basecamp.comment.post, basecamp.campfire.post, basecamp.people.list). Never warehouse.view.read."
|
|
34
35
|
},
|
|
35
36
|
"developmentPreapproval": { "$ref": "#/definitions/DevelopmentPreapproval" },
|
|
36
37
|
"oauthClient": { "$ref": "#/definitions/SaasOauthClient" },
|
|
@@ -76,8 +77,18 @@
|
|
|
76
77
|
"definitions": {
|
|
77
78
|
"SaasProvider": {
|
|
78
79
|
"type": "string",
|
|
79
|
-
"enum": ["slack", "gmail"],
|
|
80
|
-
"description": "Slack carries an installation and a channel map; Gmail is user-consent only
|
|
80
|
+
"enum": ["slack", "gmail", "basecamp"],
|
|
81
|
+
"description": "Slack carries an installation and a channel map; Gmail is user-consent only; Basecamp is user-consent only with an IT-pinned account and a project map in `channels`."
|
|
82
|
+
},
|
|
83
|
+
"BasecampAccount": {
|
|
84
|
+
"type": "object",
|
|
85
|
+
"additionalProperties": false,
|
|
86
|
+
"required": ["id", "name"],
|
|
87
|
+
"description": "The pinned Basecamp account (0.76.0, Basecamp only): the one account, among those IT's own Basecamp sign-in belongs to, that every user token on this connection must also belong to and that every project id in `channels` lives in. Absent until IT chooses it; governance refuses every basecamp.* call until then.",
|
|
88
|
+
"properties": {
|
|
89
|
+
"id": { "type": "string", "pattern": "^[0-9]{1,20}$", "description": "The Basecamp account id, the first path segment of the API base URL." },
|
|
90
|
+
"name": { "type": "string", "minLength": 1, "maxLength": 120, "description": "The account's name as Basecamp shows it, for IT to recognise." }
|
|
91
|
+
}
|
|
81
92
|
},
|
|
82
93
|
"SaasOauthClient": {
|
|
83
94
|
"type": "object",
|
|
@@ -237,6 +248,23 @@
|
|
|
237
248
|
"externalRecipients": { "mode": "allowlist", "allowedDomains": ["partner.example.com"] },
|
|
238
249
|
"createdAt": "2026-09-11T08:00:00Z",
|
|
239
250
|
"updatedAt": "2026-09-11T08:00:00Z"
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
"schema": "harbour.saas-connection/1.0",
|
|
254
|
+
"tenantId": "fourier",
|
|
255
|
+
"sourceId": "company-basecamp",
|
|
256
|
+
"kind": "saas",
|
|
257
|
+
"provider": "basecamp",
|
|
258
|
+
"displayName": "Company Basecamp",
|
|
259
|
+
"status": "ACTIVE",
|
|
260
|
+
"version": 2,
|
|
261
|
+
"account": { "id": "195539477", "name": "Fourier Labs" },
|
|
262
|
+
"channels": { "launch": "2085958499" },
|
|
263
|
+
"operations": ["basecamp.project.list", "basecamp.todolist.list", "basecamp.todo.list", "basecamp.todo.create", "basecamp.todo.complete", "basecamp.message.list", "basecamp.message.post", "basecamp.comment.list", "basecamp.comment.post", "basecamp.campfire.post", "basecamp.people.list"],
|
|
264
|
+
"developmentPreapproval": { "enabled": true, "hours": 24 },
|
|
265
|
+
"userModeApproval": "automatic",
|
|
266
|
+
"createdAt": "2026-10-11T08:00:00Z",
|
|
267
|
+
"updatedAt": "2026-10-11T08:00:00Z"
|
|
240
268
|
}
|
|
241
269
|
]
|
|
242
270
|
}
|