|
|
@ -72,6 +72,7 @@ type Trigger = MetaRowRecord<"_grist_Triggers">;
|
|
|
|
export interface WebHookSecret {
|
|
|
|
export interface WebHookSecret {
|
|
|
|
url: string;
|
|
|
|
url: string;
|
|
|
|
unsubscribeKey: string;
|
|
|
|
unsubscribeKey: string;
|
|
|
|
|
|
|
|
authorization?: string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Work to do after fetching values from the document
|
|
|
|
// Work to do after fetching values from the document
|
|
|
@ -259,6 +260,7 @@ export class DocTriggers {
|
|
|
|
const getTableId = docData.getMetaTable("_grist_Tables").getRowPropFunc("tableId");
|
|
|
|
const getTableId = docData.getMetaTable("_grist_Tables").getRowPropFunc("tableId");
|
|
|
|
const getColId = docData.getMetaTable("_grist_Tables_column").getRowPropFunc("colId");
|
|
|
|
const getColId = docData.getMetaTable("_grist_Tables_column").getRowPropFunc("colId");
|
|
|
|
const getUrl = async (id: string) => (await this._getWebHook(id))?.url ?? '';
|
|
|
|
const getUrl = async (id: string) => (await this._getWebHook(id))?.url ?? '';
|
|
|
|
|
|
|
|
const getAuthorization = async (id: string) => (await this._getWebHook(id))?.authorization ?? '';
|
|
|
|
const getUnsubscribeKey = async (id: string) => (await this._getWebHook(id))?.unsubscribeKey ?? '';
|
|
|
|
const getUnsubscribeKey = async (id: string) => (await this._getWebHook(id))?.unsubscribeKey ?? '';
|
|
|
|
const resultTable: WebhookSummary[] = [];
|
|
|
|
const resultTable: WebhookSummary[] = [];
|
|
|
|
|
|
|
|
|
|
|
@ -271,6 +273,7 @@ export class DocTriggers {
|
|
|
|
for (const act of webhookActions) {
|
|
|
|
for (const act of webhookActions) {
|
|
|
|
// Url, probably should be hidden for non-owners (but currently this API is owners only).
|
|
|
|
// Url, probably should be hidden for non-owners (but currently this API is owners only).
|
|
|
|
const url = await getUrl(act.id);
|
|
|
|
const url = await getUrl(act.id);
|
|
|
|
|
|
|
|
const authorization = await getAuthorization(act.id);
|
|
|
|
// Same story, should be hidden.
|
|
|
|
// Same story, should be hidden.
|
|
|
|
const unsubscribeKey = await getUnsubscribeKey(act.id);
|
|
|
|
const unsubscribeKey = await getUnsubscribeKey(act.id);
|
|
|
|
if (!url || !unsubscribeKey) {
|
|
|
|
if (!url || !unsubscribeKey) {
|
|
|
@ -285,6 +288,7 @@ export class DocTriggers {
|
|
|
|
fields: {
|
|
|
|
fields: {
|
|
|
|
// Url, probably should be hidden for non-owners (but currently this API is owners only).
|
|
|
|
// Url, probably should be hidden for non-owners (but currently this API is owners only).
|
|
|
|
url,
|
|
|
|
url,
|
|
|
|
|
|
|
|
authorization,
|
|
|
|
unsubscribeKey,
|
|
|
|
unsubscribeKey,
|
|
|
|
// Other fields used to register this webhook.
|
|
|
|
// Other fields used to register this webhook.
|
|
|
|
eventTypes: decodeObject(t.eventTypes) as string[],
|
|
|
|
eventTypes: decodeObject(t.eventTypes) as string[],
|
|
|
@ -683,6 +687,7 @@ export class DocTriggers {
|
|
|
|
const batch = _.takeWhile(this._webHookEventQueue.slice(0, 100), {id});
|
|
|
|
const batch = _.takeWhile(this._webHookEventQueue.slice(0, 100), {id});
|
|
|
|
const body = JSON.stringify(batch.map(e => e.payload));
|
|
|
|
const body = JSON.stringify(batch.map(e => e.payload));
|
|
|
|
const url = await this._getWebHookUrl(id);
|
|
|
|
const url = await this._getWebHookUrl(id);
|
|
|
|
|
|
|
|
const authorization = (await this._getWebHook(id))?.authorization || "";
|
|
|
|
if (this._loopAbort.signal.aborted) {
|
|
|
|
if (this._loopAbort.signal.aborted) {
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -698,7 +703,8 @@ export class DocTriggers {
|
|
|
|
this._activeDoc.logTelemetryEvent(null, 'sendingWebhooks', {
|
|
|
|
this._activeDoc.logTelemetryEvent(null, 'sendingWebhooks', {
|
|
|
|
limited: {numEvents: meta.numEvents},
|
|
|
|
limited: {numEvents: meta.numEvents},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
success = await this._sendWebhookWithRetries(id, url, body, batch.length, this._loopAbort.signal);
|
|
|
|
success = await this._sendWebhookWithRetries(
|
|
|
|
|
|
|
|
id, url, authorization, body, batch.length, this._loopAbort.signal);
|
|
|
|
if (this._loopAbort.signal.aborted) {
|
|
|
|
if (this._loopAbort.signal.aborted) {
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -770,7 +776,8 @@ export class DocTriggers {
|
|
|
|
return this._drainingQueue ? Math.min(5, TRIGGER_MAX_ATTEMPTS) : TRIGGER_MAX_ATTEMPTS;
|
|
|
|
return this._drainingQueue ? Math.min(5, TRIGGER_MAX_ATTEMPTS) : TRIGGER_MAX_ATTEMPTS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private async _sendWebhookWithRetries(id: string, url: string, body: string, size: number, signal: AbortSignal) {
|
|
|
|
private async _sendWebhookWithRetries(
|
|
|
|
|
|
|
|
id: string, url: string, authorization: string, body: string, size: number, signal: AbortSignal) {
|
|
|
|
const maxWait = 64;
|
|
|
|
const maxWait = 64;
|
|
|
|
let wait = 1;
|
|
|
|
let wait = 1;
|
|
|
|
for (let attempt = 0; attempt < this._maxWebhookAttempts; attempt++) {
|
|
|
|
for (let attempt = 0; attempt < this._maxWebhookAttempts; attempt++) {
|
|
|
@ -786,6 +793,7 @@ export class DocTriggers {
|
|
|
|
body,
|
|
|
|
body,
|
|
|
|
headers: {
|
|
|
|
headers: {
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
|
|
|
...(authorization ? {'Authorization': authorization} : {}),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
signal,
|
|
|
|
signal,
|
|
|
|
agent: proxyAgent(new URL(url)),
|
|
|
|
agent: proxyAgent(new URL(url)),
|
|
|
|