@fourier-labs/harbour-contracts 0.70.0 → 0.72.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 +3 -3
- package/contract-release.json +27 -25
- package/dist/gen-ts/app-integrations-declaration.d.ts +24 -11
- 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/deployment-app-registration.d.ts +1 -1
- package/dist/gen-ts/deployment-preview-retirement.d.ts +1 -1
- package/dist/gen-ts/index.d.ts +1 -0
- package/dist/gen-ts/index.js +1 -0
- package/dist/gen-ts/integration-execute-input-rules.d.ts +193 -3
- package/dist/gen-ts/integration-execute-input-rules.js +60 -3
- package/dist/gen-ts/integration-execute-request.d.ts +79 -8
- package/dist/gen-ts/integration-execute-result.d.ts +74 -3
- package/dist/gen-ts/integration-operations.d.ts +87 -0
- package/dist/gen-ts/integration-operations.js +117 -0
- package/dist/gen-ts/saas-connection.d.ts +62 -4
- package/gen-ts/app-integrations-declaration.ts +24 -11
- package/gen-ts/contract-release.ts +6 -6
- package/gen-ts/data-grant-v2.ts +10 -4
- package/gen-ts/deployment-app-registration.ts +1 -1
- package/gen-ts/deployment-preview-retirement.ts +1 -1
- package/gen-ts/index.ts +1 -0
- package/gen-ts/integration-execute-input-rules.ts +60 -4
- package/gen-ts/integration-execute-request.ts +84 -7
- package/gen-ts/integration-execute-result.ts +74 -3
- package/gen-ts/integration-operations.ts +118 -0
- package/gen-ts/saas-connection.ts +67 -3
- package/package.json +1 -1
- package/schema/app-integrations-declaration.schema.json +31 -9
- package/schema/data-grant-v2.schema.json +2 -2
- package/schema/deployment-app-registration.schema.json +1 -1
- package/schema/deployment-preview-retirement.schema.json +1 -1
- package/schema/integration-execute-request.schema.json +157 -11
- package/schema/integration-execute-result.schema.json +82 -7
- package/schema/saas-connection.schema.json +74 -5
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
// Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
|
|
3
|
+
|
|
4
|
+
/** Declaration and execute metadata from the contract schemas. */
|
|
5
|
+
export const IntegrationOperations = {
|
|
6
|
+
"slack.channel.history": {
|
|
7
|
+
"kind": "saas",
|
|
8
|
+
"identities": [
|
|
9
|
+
"user",
|
|
10
|
+
"app"
|
|
11
|
+
],
|
|
12
|
+
"readOnly": true,
|
|
13
|
+
"declaredMode": true,
|
|
14
|
+
"idempotencyKey": false
|
|
15
|
+
},
|
|
16
|
+
"slack.message.post": {
|
|
17
|
+
"kind": "saas",
|
|
18
|
+
"identities": [
|
|
19
|
+
"app",
|
|
20
|
+
"user"
|
|
21
|
+
],
|
|
22
|
+
"readOnly": false,
|
|
23
|
+
"declaredMode": true,
|
|
24
|
+
"idempotencyKey": true
|
|
25
|
+
},
|
|
26
|
+
"slack.message.update": {
|
|
27
|
+
"kind": "saas",
|
|
28
|
+
"identities": [
|
|
29
|
+
"app",
|
|
30
|
+
"user"
|
|
31
|
+
],
|
|
32
|
+
"readOnly": false,
|
|
33
|
+
"declaredMode": true,
|
|
34
|
+
"idempotencyKey": false
|
|
35
|
+
},
|
|
36
|
+
"slack.message.delete": {
|
|
37
|
+
"kind": "saas",
|
|
38
|
+
"identities": [
|
|
39
|
+
"app",
|
|
40
|
+
"user"
|
|
41
|
+
],
|
|
42
|
+
"readOnly": false,
|
|
43
|
+
"declaredMode": true,
|
|
44
|
+
"idempotencyKey": false
|
|
45
|
+
},
|
|
46
|
+
"slack.dm.send": {
|
|
47
|
+
"kind": "saas",
|
|
48
|
+
"identities": [
|
|
49
|
+
"app"
|
|
50
|
+
],
|
|
51
|
+
"readOnly": false,
|
|
52
|
+
"declaredMode": false,
|
|
53
|
+
"idempotencyKey": true
|
|
54
|
+
},
|
|
55
|
+
"slack.user.lookup": {
|
|
56
|
+
"kind": "saas",
|
|
57
|
+
"identities": [
|
|
58
|
+
"app"
|
|
59
|
+
],
|
|
60
|
+
"readOnly": true,
|
|
61
|
+
"declaredMode": false,
|
|
62
|
+
"idempotencyKey": false
|
|
63
|
+
},
|
|
64
|
+
"gmail.thread.list": {
|
|
65
|
+
"kind": "saas",
|
|
66
|
+
"identities": [
|
|
67
|
+
"user"
|
|
68
|
+
],
|
|
69
|
+
"readOnly": true,
|
|
70
|
+
"declaredMode": false,
|
|
71
|
+
"idempotencyKey": false
|
|
72
|
+
},
|
|
73
|
+
"gmail.thread.read": {
|
|
74
|
+
"kind": "saas",
|
|
75
|
+
"identities": [
|
|
76
|
+
"user"
|
|
77
|
+
],
|
|
78
|
+
"readOnly": true,
|
|
79
|
+
"declaredMode": false,
|
|
80
|
+
"idempotencyKey": false
|
|
81
|
+
},
|
|
82
|
+
"gmail.message.read": {
|
|
83
|
+
"kind": "saas",
|
|
84
|
+
"identities": [
|
|
85
|
+
"user"
|
|
86
|
+
],
|
|
87
|
+
"readOnly": true,
|
|
88
|
+
"declaredMode": false,
|
|
89
|
+
"idempotencyKey": false
|
|
90
|
+
},
|
|
91
|
+
"gmail.message.send": {
|
|
92
|
+
"kind": "saas",
|
|
93
|
+
"identities": [
|
|
94
|
+
"user"
|
|
95
|
+
],
|
|
96
|
+
"readOnly": false,
|
|
97
|
+
"declaredMode": false,
|
|
98
|
+
"idempotencyKey": true
|
|
99
|
+
},
|
|
100
|
+
"gmail.attachment.read": {
|
|
101
|
+
"kind": "saas",
|
|
102
|
+
"identities": [
|
|
103
|
+
"user"
|
|
104
|
+
],
|
|
105
|
+
"readOnly": true,
|
|
106
|
+
"declaredMode": false,
|
|
107
|
+
"idempotencyKey": false
|
|
108
|
+
},
|
|
109
|
+
"warehouse.view.read": {
|
|
110
|
+
"kind": "database",
|
|
111
|
+
"identities": [
|
|
112
|
+
"app"
|
|
113
|
+
],
|
|
114
|
+
"readOnly": true,
|
|
115
|
+
"declaredMode": false,
|
|
116
|
+
"idempotencyKey": false
|
|
117
|
+
}
|
|
118
|
+
} as const;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Code generated by tools/gen/gen.mjs from schema/*.json. DO NOT EDIT.
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Slack carries an installation and a channel map; Gmail is user-consent only
|
|
5
|
+
* Slack carries an installation and a channel map; Gmail is user-consent only.
|
|
6
6
|
*/
|
|
7
7
|
export type SaasProvider = 'slack' | 'gmail';
|
|
8
8
|
/**
|
|
@@ -10,6 +10,18 @@ export type SaasProvider = 'slack' | 'gmail';
|
|
|
10
10
|
*/
|
|
11
11
|
export type SaasConnectionStatus = 'ACTIVE' | 'DISABLED';
|
|
12
12
|
export type SaasInstallationStatus = 'installed' | 'reconnect_required' | 'revoked';
|
|
13
|
+
/**
|
|
14
|
+
* How a user-mode access request on this connection is approved (0.71.0): automatic — the person's own consent is the approval, since the app only ever acts as them; it — an IT admin reviews it like an app-mode request. Absent means it.
|
|
15
|
+
*/
|
|
16
|
+
export type UserModeApproval = 'automatic' | 'it';
|
|
17
|
+
/**
|
|
18
|
+
* signed_in_person — only the person using the app; channel_members — the members of the named channel; anyone — any workspace member.
|
|
19
|
+
*/
|
|
20
|
+
export type DirectMessageAudience = 'signed_in_person' | 'channel_members' | 'anyone';
|
|
21
|
+
/**
|
|
22
|
+
* block — every recipient must be internal (what a new Gmail connection is given; a connection stored without a policy reads as allow); allowlist — internal or in `allowedDomains`; allow — any address.
|
|
23
|
+
*/
|
|
24
|
+
export type ExternalRecipientMode = 'block' | 'allowlist' | 'allow';
|
|
13
25
|
/**
|
|
14
26
|
*/
|
|
15
27
|
export type InstallationName = string;
|
|
@@ -43,20 +55,30 @@ export interface SaasConnection {
|
|
|
43
55
|
[k: string]: string;
|
|
44
56
|
};
|
|
45
57
|
/**
|
|
46
|
-
* Operations IT allows apps to request on this connection: the provider's own SaaS operations (Slack: slack.channel.history, slack.message.post; Gmail: gmail.thread.list, gmail.message.read, gmail.message.send). Never warehouse.view.read.
|
|
58
|
+
* 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.
|
|
47
59
|
*
|
|
48
60
|
* @minItems 1
|
|
49
|
-
* @maxItems
|
|
61
|
+
* @maxItems 11
|
|
50
62
|
*/
|
|
51
63
|
operations: (
|
|
52
64
|
| 'slack.channel.history'
|
|
53
65
|
| 'slack.message.post'
|
|
66
|
+
| 'slack.message.update'
|
|
67
|
+
| 'slack.message.delete'
|
|
68
|
+
| 'slack.dm.send'
|
|
69
|
+
| 'slack.user.lookup'
|
|
54
70
|
| 'gmail.thread.list'
|
|
71
|
+
| 'gmail.thread.read'
|
|
55
72
|
| 'gmail.message.read'
|
|
56
73
|
| 'gmail.message.send'
|
|
74
|
+
| 'gmail.attachment.read'
|
|
57
75
|
| 'warehouse.view.read'
|
|
58
76
|
)[];
|
|
59
77
|
developmentPreapproval: DevelopmentPreapproval;
|
|
78
|
+
oauthClient?: SaasOauthClient;
|
|
79
|
+
userModeApproval?: UserModeApproval;
|
|
80
|
+
directMessages?: DirectMessagePolicy;
|
|
81
|
+
externalRecipients?: ExternalRecipientPolicy;
|
|
60
82
|
/**
|
|
61
83
|
* Lower-cased builder emails excluded from development preapproval.
|
|
62
84
|
*
|
|
@@ -125,6 +147,48 @@ export interface DevelopmentPreapproval {
|
|
|
125
147
|
*/
|
|
126
148
|
hours: number;
|
|
127
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* The tenant's own OAuth client for this provider (0.71.0; the ADR 0068 follow-up): when present, installation and user consent run through the company's Slack app or Google Cloud project instead of the platform-owned one, so the tenant's IT sees and controls the app in their own admin console. A reference to the secret, never the secret.
|
|
152
|
+
*/
|
|
153
|
+
export interface SaasOauthClient {
|
|
154
|
+
clientId: string;
|
|
155
|
+
/**
|
|
156
|
+
* Where the client secret lives.
|
|
157
|
+
*/
|
|
158
|
+
secretRef: string;
|
|
159
|
+
setAt: string;
|
|
160
|
+
/**
|
|
161
|
+
* The IT admin who saved it (lower-cased email).
|
|
162
|
+
*/
|
|
163
|
+
setBy: string;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Whom an app may direct-message through slack.dm.send (0.71.0), set by IT on the connection and enforced by governance on every send.
|
|
167
|
+
*/
|
|
168
|
+
export interface DirectMessagePolicy {
|
|
169
|
+
allowed: DirectMessageAudience;
|
|
170
|
+
/**
|
|
171
|
+
* Tenant-wide logical alias of a connection (governance sourceId) or of a resource under it.
|
|
172
|
+
*/
|
|
173
|
+
channel?: string;
|
|
174
|
+
/**
|
|
175
|
+
* Distinct recipients one app may message per UTC day on this connection; the cap refuses the send that would exceed it.
|
|
176
|
+
*/
|
|
177
|
+
dailyRecipientCap: number;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Whether a gmail.message.send may address people outside the company (0.71.0). An external recipient is one whose email domain is not one of the tenant's verified domains.
|
|
181
|
+
*/
|
|
182
|
+
export interface ExternalRecipientPolicy {
|
|
183
|
+
mode: ExternalRecipientMode;
|
|
184
|
+
/**
|
|
185
|
+
* allowlist only: the external domains a send may address; a recipient's domain must equal one of them.
|
|
186
|
+
*
|
|
187
|
+
* @minItems 1
|
|
188
|
+
* @maxItems 50
|
|
189
|
+
*/
|
|
190
|
+
allowedDomains?: string[];
|
|
191
|
+
}
|
|
128
192
|
/**
|
|
129
193
|
* @deprecated
|
|
130
194
|
*/
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"$id": "https://schemas.harbour.dev/app-integrations-declaration.schema.json",
|
|
4
4
|
"title": "AppIntegrationsDeclaration",
|
|
5
|
-
"description": "Committed `.isomorph/integrations.json`: the app's declared use of tenant-wide integration connections. Keys are logical connection aliases (the governance sourceId); resources are logical names (Slack channel alias, warehouse view name), never IDs, tokens or URLs. The operation set is closed (
|
|
5
|
+
"description": "Committed `.isomorph/integrations.json`: the app's declared use of tenant-wide integration connections. Keys are logical connection aliases (the governance sourceId); resources are logical names (Slack channel alias, warehouse view name), never IDs, tokens or URLs. The operation set is closed (twelve operations since 0.71.0, ADR 0018). Each operation carries a fixed identity mode — slack.dm.send=app, slack.user.lookup=app, every gmail.* operation=user, warehouse.view.read=app — except the declared-mode Slack operations (slack.message.post, and since 0.71.0 slack.channel.history, slack.message.update and slack.message.delete), whose mode the app DECLARES: `app` acts as the company's Slack app (the one audited bot identity; a post goes out under the IT-approved per-app display name when the connection declares `presentation`), `user` acts as the signed-in person after their own consent (reads what they can read, edits what they wrote). The declared mode is the one the app requests access for; an app may hold approved grants for both modes after two requests, and each declared-mode call names which approved mode it runs under (`mode` on the execute request, 0.55.0) — a mode IT did not approve is refused, never swapped for the other. A `saas` connection may declare only the Slack and Gmail operations and a `database` connection only warehouse.view.read; `presentation` is a `saas` connection's optional per-app display name and icon for app-mode Slack posts, shown to IT at approval time and applied only from the approved grant. Gmail is user-mode only (reads, one send); its single resource is the fixed `inbox` alias. slack.dm.send and slack.user.lookup address people by email rather than a channel, so their single resource is the fixed `workspace` alias (the connection's installed workspace), the way Gmail's is `inbox`. This file also owns the shared IntegrationOperation / IntegrationIdentityMode / IntegrationEnvironment / IntegrationConnectionKind / IntegrationPresentation definitions referenced by the other integration schemas.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"required": ["schema", "connections"],
|
|
@@ -25,13 +25,13 @@
|
|
|
25
25
|
"IntegrationConnectionKind": { "type": "string", "enum": ["saas", "database"] },
|
|
26
26
|
"IntegrationOperation": {
|
|
27
27
|
"type": "string",
|
|
28
|
-
"enum": ["slack.channel.history", "slack.message.post", "gmail.thread.list", "gmail.message.read", "gmail.message.send", "warehouse.view.read"],
|
|
29
|
-
"description": "Closed operation set. Extending it is a contracts change. gmail.message.send (0.50.0) sends one
|
|
28
|
+
"enum": ["slack.channel.history", "slack.message.post", "slack.message.update", "slack.message.delete", "slack.dm.send", "slack.user.lookup", "gmail.thread.list", "gmail.thread.read", "gmail.message.read", "gmail.message.send", "gmail.attachment.read", "warehouse.view.read"],
|
|
29
|
+
"description": "Closed operation set. Extending it is a contracts change. gmail.message.send (0.50.0) sends one email as the signed-in person: a send like slack.message.post (explicit Send, idempotencyKey, write receipt) under user identity; since 0.71.0 it may carry an HTML body and attachments. slack.message.post (0.53.0) runs in the mode the app declares: app (the company's Slack app, under the IT-approved per-app name) or user (the signed-in person, after their consent); since 0.55.0 each call names which of the approved modes it runs under. 0.71.0 (ADR 0018) adds slack.message.update and slack.message.delete (declared mode, like post), slack.dm.send (app mode: a direct message to 1..8 workspace members named by email), slack.user.lookup (app mode: email to Slack user id), gmail.thread.read (every message of one thread) and gmail.attachment.read (one attachment's bytes), and lets slack.channel.history run in app mode as well as user mode."
|
|
30
30
|
},
|
|
31
31
|
"IntegrationIdentityMode": {
|
|
32
32
|
"type": "string",
|
|
33
33
|
"enum": ["app", "user"],
|
|
34
|
-
"description": "Whose authority an operation runs under. Fixed per operation, except slack.message.post: declared per app for the access request, and named per call among the app's approved modes (execute request `mode`, 0.55.0)."
|
|
34
|
+
"description": "Whose authority an operation runs under. Fixed per operation, except the declared-mode Slack operations (slack.message.post; since 0.71.0 also slack.channel.history, slack.message.update, slack.message.delete): declared per app for the access request, and named per call among the app's approved modes (execute request `mode`, 0.55.0)."
|
|
35
35
|
},
|
|
36
36
|
"IntegrationEnvironment": {
|
|
37
37
|
"type": "string",
|
|
@@ -92,12 +92,22 @@
|
|
|
92
92
|
"type": "object",
|
|
93
93
|
"additionalProperties": false,
|
|
94
94
|
"required": ["identity", "resources"],
|
|
95
|
-
"description": "slack.message.post: the app declares the mode — app (
|
|
95
|
+
"description": "slack.message.post, slack.channel.history, slack.message.update, slack.message.delete: the app declares the mode — app (acts as the company's Slack app) or user (acts as the signed-in person).",
|
|
96
96
|
"properties": {
|
|
97
97
|
"identity": { "$ref": "#/definitions/IntegrationIdentityMode" },
|
|
98
98
|
"resources": { "$ref": "#/definitions/ResourceNames" }
|
|
99
99
|
}
|
|
100
100
|
},
|
|
101
|
+
"AppModeChannelOperation": {
|
|
102
|
+
"type": "object",
|
|
103
|
+
"additionalProperties": false,
|
|
104
|
+
"required": ["identity", "resources"],
|
|
105
|
+
"description": "slack.dm.send, slack.user.lookup (0.71.0): app mode only — the company's Slack app messages people or resolves them by email; the resource is the fixed `workspace` alias.",
|
|
106
|
+
"properties": {
|
|
107
|
+
"identity": { "type": "string", "const": "app" },
|
|
108
|
+
"resources": { "$ref": "#/definitions/ResourceNames" }
|
|
109
|
+
}
|
|
110
|
+
},
|
|
101
111
|
"AppModeViewOperation": {
|
|
102
112
|
"type": "object",
|
|
103
113
|
"additionalProperties": false,
|
|
@@ -125,11 +135,17 @@
|
|
|
125
135
|
"additionalProperties": false,
|
|
126
136
|
"minProperties": 1,
|
|
127
137
|
"properties": {
|
|
128
|
-
"slack.channel.history": { "$ref": "#/definitions/
|
|
138
|
+
"slack.channel.history": { "$ref": "#/definitions/DeclaredModeChannelOperation" },
|
|
129
139
|
"slack.message.post": { "$ref": "#/definitions/DeclaredModeChannelOperation" },
|
|
140
|
+
"slack.message.update": { "$ref": "#/definitions/DeclaredModeChannelOperation" },
|
|
141
|
+
"slack.message.delete": { "$ref": "#/definitions/DeclaredModeChannelOperation" },
|
|
142
|
+
"slack.dm.send": { "$ref": "#/definitions/AppModeChannelOperation" },
|
|
143
|
+
"slack.user.lookup": { "$ref": "#/definitions/AppModeChannelOperation" },
|
|
130
144
|
"gmail.thread.list": { "$ref": "#/definitions/UserModeChannelOperation" },
|
|
145
|
+
"gmail.thread.read": { "$ref": "#/definitions/UserModeChannelOperation" },
|
|
131
146
|
"gmail.message.read": { "$ref": "#/definitions/UserModeChannelOperation" },
|
|
132
147
|
"gmail.message.send": { "$ref": "#/definitions/UserModeChannelOperation" },
|
|
148
|
+
"gmail.attachment.read": { "$ref": "#/definitions/UserModeChannelOperation" },
|
|
133
149
|
"warehouse.view.read": { "$ref": "#/definitions/AppModeViewOperation" }
|
|
134
150
|
}
|
|
135
151
|
}
|
|
@@ -140,7 +156,7 @@
|
|
|
140
156
|
"not": { "required": ["presentation"] },
|
|
141
157
|
"properties": {
|
|
142
158
|
"operations": {
|
|
143
|
-
"not": { "anyOf": [{ "required": ["slack.channel.history"] }, { "required": ["slack.message.post"] }, { "required": ["gmail.thread.list"] }, { "required": ["gmail.message.read"] }, { "required": ["gmail.message.send"] }] }
|
|
159
|
+
"not": { "anyOf": [{ "required": ["slack.channel.history"] }, { "required": ["slack.message.post"] }, { "required": ["slack.message.update"] }, { "required": ["slack.message.delete"] }, { "required": ["slack.dm.send"] }, { "required": ["slack.user.lookup"] }, { "required": ["gmail.thread.list"] }, { "required": ["gmail.thread.read"] }, { "required": ["gmail.message.read"] }, { "required": ["gmail.message.send"] }, { "required": ["gmail.attachment.read"] }] }
|
|
144
160
|
}
|
|
145
161
|
}
|
|
146
162
|
}
|
|
@@ -155,15 +171,21 @@
|
|
|
155
171
|
"presentation": { "displayName": "Lunch Vote", "iconEmoji": ":sandwich:" },
|
|
156
172
|
"operations": {
|
|
157
173
|
"slack.channel.history": { "identity": "user", "resources": ["team-updates"] },
|
|
158
|
-
"slack.message.post": { "identity": "app", "resources": ["team-updates"] }
|
|
174
|
+
"slack.message.post": { "identity": "app", "resources": ["team-updates"] },
|
|
175
|
+
"slack.message.update": { "identity": "app", "resources": ["team-updates"] },
|
|
176
|
+
"slack.message.delete": { "identity": "app", "resources": ["team-updates"] },
|
|
177
|
+
"slack.dm.send": { "identity": "app", "resources": ["workspace"] },
|
|
178
|
+
"slack.user.lookup": { "identity": "app", "resources": ["workspace"] }
|
|
159
179
|
}
|
|
160
180
|
},
|
|
161
181
|
"gmail": {
|
|
162
182
|
"kind": "saas",
|
|
163
183
|
"operations": {
|
|
164
184
|
"gmail.thread.list": { "identity": "user", "resources": ["inbox"] },
|
|
185
|
+
"gmail.thread.read": { "identity": "user", "resources": ["inbox"] },
|
|
165
186
|
"gmail.message.read": { "identity": "user", "resources": ["inbox"] },
|
|
166
|
-
"gmail.message.send": { "identity": "user", "resources": ["inbox"] }
|
|
187
|
+
"gmail.message.send": { "identity": "user", "resources": ["inbox"] },
|
|
188
|
+
"gmail.attachment.read": { "identity": "user", "resources": ["inbox"] }
|
|
167
189
|
}
|
|
168
190
|
},
|
|
169
191
|
"sales-warehouse": {
|
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
"status": { "$ref": "#/definitions/DataGrantV2Status" },
|
|
18
18
|
"operations": {
|
|
19
19
|
"type": "array",
|
|
20
|
-
"maxItems":
|
|
20
|
+
"maxItems": 11,
|
|
21
21
|
"uniqueItems": true,
|
|
22
22
|
"items": { "$ref": "app-integrations-declaration.schema.json#/definitions/IntegrationOperation" },
|
|
23
|
-
"description": "Approved operations; each must match this grant's mode. Empty until the first approval."
|
|
23
|
+
"description": "Approved operations; each must match this grant's mode. Empty until the first approval. At most the eleven SaaS operations (0.71.0); a database grant holds one."
|
|
24
24
|
},
|
|
25
25
|
"resources": {
|
|
26
26
|
"type": "array",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"$id": "https://contracts.harbour.dev/deployment-app-registration.schema.json",
|
|
4
4
|
"title": "DeploymentAppRegistration",
|
|
5
|
-
"description": "IAM/SigV4 POST /v1/deployment/apps/register body. Governance derives tenant identity-provider configuration; no identity secrets are supplied. apiHostnames explicitly identifies API hosts among backendHostnames; runtime validates that subset. Omission preserves legacy .api. inference during the coordinated V2 auth migration.",
|
|
5
|
+
"description": "IAM/SigV4 POST /v1/internal/deployment/apps/{appId}/register (Internal API) or POST /v1/deployment/apps/register body. Governance derives tenant identity-provider configuration; no identity secrets are supplied. apiHostnames explicitly identifies API hosts among backendHostnames; runtime validates that subset. Omission preserves legacy .api. inference during the coordinated V2 auth migration.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"required": ["tenantId", "appId", "origin"],
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"$id": "https://contracts.harbour.dev/deployment-preview-retirement.schema.json",
|
|
4
4
|
"title": "DeploymentPreviewRetirement",
|
|
5
|
-
"description": "IAM-protected POST /v1/deployment/apps/retire-preview request after production is LIVE and the deployment plane holds the preview retirement reservation. Disables only the registered preview version's serving routes and workload authority.",
|
|
5
|
+
"description": "IAM-protected POST /v1/internal/deployment/apps/{appId}/retire-preview (Internal API) or POST /v1/deployment/apps/retire-preview request after production is LIVE and the deployment plane holds the preview retirement reservation. Disables only the registered preview version's serving routes and workload authority.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"required": ["tenantId", "appId", "previewVersionId"],
|