@pipedream/grain 0.1.0 → 1.0.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/actions/get-recording/get-recording.mjs +66 -31
- package/actions/get-transcript/get-transcript.mjs +45 -0
- package/actions/list-recordings/list-recordings.mjs +89 -0
- package/common/constants.mjs +6 -13
- package/grain.app.mjs +93 -41
- package/package.json +2 -2
- package/sources/common/base.mjs +30 -8
- package/sources/common/highlight.mjs +38 -0
- package/sources/common/recording.mjs +66 -0
- package/sources/new-highlight-instant/new-highlight-instant.mjs +5 -19
- package/sources/new-highlight-instant/test-event.mjs +12 -8
- package/sources/new-recording-instant/new-recording-instant.mjs +5 -19
- package/sources/new-recording-instant/test-event.mjs +23 -8
- package/sources/new-story-instant/new-story-instant.mjs +10 -18
- package/sources/new-story-instant/test-event.mjs +6 -4
- package/sources/removed-highlight-instant/removed-highlight-instant.mjs +5 -19
- package/sources/removed-highlight-instant/test-event.mjs +3 -3
- package/sources/removed-recording-instant/removed-recording-instant.mjs +4 -18
- package/sources/removed-recording-instant/test-event.mjs +2 -2
- package/sources/removed-story-instant/removed-story-instant.mjs +4 -18
- package/sources/removed-story-instant/test-event.mjs +2 -2
- package/sources/updated-highlight-instant/test-event.mjs +12 -8
- package/sources/updated-highlight-instant/updated-highlight-instant.mjs +6 -20
- package/sources/updated-recording-instant/test-event.mjs +23 -8
- package/sources/updated-recording-instant/updated-recording-instant.mjs +6 -20
- package/sources/updated-story-instant/test-event.mjs +6 -4
- package/sources/updated-story-instant/updated-story-instant.mjs +11 -19
- package/common/utils.mjs +0 -24
|
@@ -1,57 +1,92 @@
|
|
|
1
|
-
import {
|
|
2
|
-
INTELLIGENCE_NOTES_FORMAT_OPTIONS,
|
|
3
|
-
TRANSCRIPT_FORMAT_OPTIONS,
|
|
4
|
-
} from "../../common/constants.mjs";
|
|
5
|
-
import { parseObject } from "../../common/utils.mjs";
|
|
6
1
|
import grain from "../../grain.app.mjs";
|
|
7
2
|
|
|
8
3
|
export default {
|
|
9
4
|
key: "grain-get-recording",
|
|
10
5
|
name: "Get Recording",
|
|
11
|
-
description: "Fetches a specific recording by its ID from Grain,
|
|
12
|
-
|
|
6
|
+
description: "Fetches a specific recording by its ID from Grain, returning its metadata (title, times, URL, tags, teams, meeting type)."
|
|
7
|
+
+ " Enable the optional include props to add highlights, participants, AI action items, AI summary, calendar event, HubSpot data, or screenshares to the response."
|
|
8
|
+
+ " Use **List Recordings** to find recording IDs, and **Get Transcript** to fetch the full transcript."
|
|
9
|
+
+ " [See the documentation](https://developers.grain.com/#get-recording)",
|
|
10
|
+
version: "1.0.0",
|
|
11
|
+
annotations: {
|
|
12
|
+
destructiveHint: false,
|
|
13
|
+
openWorldHint: true,
|
|
14
|
+
readOnlyHint: true,
|
|
15
|
+
},
|
|
13
16
|
type: "action",
|
|
14
17
|
props: {
|
|
15
18
|
grain,
|
|
16
|
-
|
|
19
|
+
recordingId: {
|
|
17
20
|
propDefinition: [
|
|
18
21
|
grain,
|
|
19
|
-
"
|
|
22
|
+
"recordingId",
|
|
20
23
|
],
|
|
21
24
|
},
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
optional: true,
|
|
25
|
+
highlights: {
|
|
26
|
+
propDefinition: [
|
|
27
|
+
grain,
|
|
28
|
+
"highlights",
|
|
29
|
+
],
|
|
28
30
|
},
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
participants: {
|
|
32
|
+
propDefinition: [
|
|
33
|
+
grain,
|
|
34
|
+
"participants",
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
aiActionItems: {
|
|
38
|
+
propDefinition: [
|
|
39
|
+
grain,
|
|
40
|
+
"aiActionItems",
|
|
41
|
+
],
|
|
35
42
|
},
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
aiSummary: {
|
|
44
|
+
propDefinition: [
|
|
45
|
+
grain,
|
|
46
|
+
"aiSummary",
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
calendarEvent: {
|
|
50
|
+
propDefinition: [
|
|
51
|
+
grain,
|
|
52
|
+
"calendarEvent",
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
hubspot: {
|
|
56
|
+
propDefinition: [
|
|
57
|
+
grain,
|
|
58
|
+
"hubspot",
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
screenshares: {
|
|
62
|
+
type: "boolean",
|
|
63
|
+
label: "Include Screenshares",
|
|
64
|
+
description: "Include the recording's screenshare ranges in the response",
|
|
40
65
|
optional: true,
|
|
41
66
|
},
|
|
42
67
|
},
|
|
43
68
|
async run({ $ }) {
|
|
69
|
+
const include = {
|
|
70
|
+
highlights: this.highlights,
|
|
71
|
+
participants: this.participants,
|
|
72
|
+
ai_action_items: this.aiActionItems,
|
|
73
|
+
ai_summary: this.aiSummary,
|
|
74
|
+
calendar_event: this.calendarEvent,
|
|
75
|
+
hubspot: this.hubspot,
|
|
76
|
+
screenshares: this.screenshares,
|
|
77
|
+
};
|
|
78
|
+
|
|
44
79
|
const response = await this.grain.fetchRecording({
|
|
45
80
|
$,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
81
|
+
recordingId: this.recordingId,
|
|
82
|
+
data: {
|
|
83
|
+
include: Object.fromEntries(Object.entries(include).filter(([
|
|
84
|
+
, value,
|
|
85
|
+
]) => value)),
|
|
51
86
|
},
|
|
52
87
|
});
|
|
53
88
|
|
|
54
|
-
$.export("$summary", `Successfully fetched recording with ID ${this.
|
|
89
|
+
$.export("$summary", `Successfully fetched recording with ID ${this.recordingId}`);
|
|
55
90
|
return response;
|
|
56
91
|
},
|
|
57
92
|
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { TRANSCRIPT_FORMAT_OPTIONS } from "../../common/constants.mjs";
|
|
2
|
+
import grain from "../../grain.app.mjs";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
key: "grain-get-transcript",
|
|
6
|
+
name: "Get Transcript",
|
|
7
|
+
description: "Fetches the full transcript of a Grain recording."
|
|
8
|
+
+ " The `json` format returns structured segments with speaker, participant ID, start/end times in milliseconds, and text;"
|
|
9
|
+
+ " `txt`, `vtt`, and `srt` return plain text or subtitle formats."
|
|
10
|
+
+ " Use **List Recordings** to find recording IDs; use **Get Recording** for the recording's metadata instead of its transcript."
|
|
11
|
+
+ " [See the documentation](https://developers.grain.com/#get-recording-transcript-json)",
|
|
12
|
+
version: "0.0.1",
|
|
13
|
+
annotations: {
|
|
14
|
+
destructiveHint: false,
|
|
15
|
+
openWorldHint: true,
|
|
16
|
+
readOnlyHint: true,
|
|
17
|
+
},
|
|
18
|
+
type: "action",
|
|
19
|
+
props: {
|
|
20
|
+
grain,
|
|
21
|
+
recordingId: {
|
|
22
|
+
propDefinition: [
|
|
23
|
+
grain,
|
|
24
|
+
"recordingId",
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
format: {
|
|
28
|
+
type: "string",
|
|
29
|
+
label: "Format",
|
|
30
|
+
description: "Format for the transcript",
|
|
31
|
+
options: TRANSCRIPT_FORMAT_OPTIONS,
|
|
32
|
+
default: "json",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
async run({ $ }) {
|
|
36
|
+
const response = await this.grain.fetchTranscript({
|
|
37
|
+
$,
|
|
38
|
+
recordingId: this.recordingId,
|
|
39
|
+
format: this.format,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
$.export("$summary", `Successfully fetched transcript for recording ${this.recordingId}`);
|
|
43
|
+
return response;
|
|
44
|
+
},
|
|
45
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import grain from "../../grain.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "grain-list-recordings",
|
|
5
|
+
name: "List Recordings",
|
|
6
|
+
description: "Lists Grain recordings, optionally filtered by start datetime range (ISO8601), title search, or participant scope."
|
|
7
|
+
+ " Automatically paginates and returns up to Max Results recordings."
|
|
8
|
+
+ " Use this to find recording IDs for **Get Recording** and **Get Transcript**."
|
|
9
|
+
+ " [See the documentation](https://developers.grain.com/#list-recordings)",
|
|
10
|
+
version: "0.0.1",
|
|
11
|
+
annotations: {
|
|
12
|
+
destructiveHint: false,
|
|
13
|
+
openWorldHint: true,
|
|
14
|
+
readOnlyHint: true,
|
|
15
|
+
},
|
|
16
|
+
type: "action",
|
|
17
|
+
props: {
|
|
18
|
+
grain,
|
|
19
|
+
beforeDatetime: {
|
|
20
|
+
type: "string",
|
|
21
|
+
label: "Before Datetime",
|
|
22
|
+
description: "Only return recordings that started before this ISO8601 datetime. E.g. `2025-01-01T00:00:00Z`",
|
|
23
|
+
optional: true,
|
|
24
|
+
},
|
|
25
|
+
afterDatetime: {
|
|
26
|
+
type: "string",
|
|
27
|
+
label: "After Datetime",
|
|
28
|
+
description: "Only return recordings that started after this ISO8601 datetime. E.g. `2025-01-01T00:00:00Z`",
|
|
29
|
+
optional: true,
|
|
30
|
+
},
|
|
31
|
+
titleSearch: {
|
|
32
|
+
type: "string",
|
|
33
|
+
label: "Title Search",
|
|
34
|
+
description: "Only return recordings whose title matches this search string",
|
|
35
|
+
optional: true,
|
|
36
|
+
},
|
|
37
|
+
participantScope: {
|
|
38
|
+
type: "string",
|
|
39
|
+
label: "Participant Scope",
|
|
40
|
+
description: "Only return recordings whose participants are all internal, or that include external participants",
|
|
41
|
+
options: [
|
|
42
|
+
"internal",
|
|
43
|
+
"external",
|
|
44
|
+
],
|
|
45
|
+
optional: true,
|
|
46
|
+
},
|
|
47
|
+
maxResults: {
|
|
48
|
+
type: "integer",
|
|
49
|
+
label: "Max Results",
|
|
50
|
+
description: "Maximum number of recordings to return. Must be a positive integer.",
|
|
51
|
+
optional: true,
|
|
52
|
+
default: 100,
|
|
53
|
+
min: 1,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
async run({ $ }) {
|
|
57
|
+
const filter = {
|
|
58
|
+
before_datetime: this.beforeDatetime,
|
|
59
|
+
after_datetime: this.afterDatetime,
|
|
60
|
+
title_search: this.titleSearch,
|
|
61
|
+
participant_scope: this.participantScope,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const recordings = [];
|
|
65
|
+
let cursor;
|
|
66
|
+
do {
|
|
67
|
+
const {
|
|
68
|
+
recordings: page, cursor: nextCursor,
|
|
69
|
+
} = await this.grain.listRecordings({
|
|
70
|
+
$,
|
|
71
|
+
data: {
|
|
72
|
+
cursor,
|
|
73
|
+
filter,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
recordings.push(...page);
|
|
77
|
+
cursor = nextCursor;
|
|
78
|
+
} while (cursor && recordings.length < this.maxResults);
|
|
79
|
+
|
|
80
|
+
if (recordings.length > this.maxResults) {
|
|
81
|
+
recordings.length = this.maxResults;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
$.export("$summary", `Successfully fetched ${recordings.length} recording${recordings.length === 1
|
|
85
|
+
? ""
|
|
86
|
+
: "s"}`);
|
|
87
|
+
return recordings;
|
|
88
|
+
},
|
|
89
|
+
};
|
package/common/constants.mjs
CHANGED
|
@@ -4,22 +4,15 @@ export const TRANSCRIPT_FORMAT_OPTIONS = [
|
|
|
4
4
|
value: "json",
|
|
5
5
|
},
|
|
6
6
|
{
|
|
7
|
-
label: "
|
|
8
|
-
value: "
|
|
9
|
-
},
|
|
10
|
-
];
|
|
11
|
-
|
|
12
|
-
export const INTELLIGENCE_NOTES_FORMAT_OPTIONS = [
|
|
13
|
-
{
|
|
14
|
-
label: "JSON",
|
|
15
|
-
value: "json",
|
|
7
|
+
label: "Text",
|
|
8
|
+
value: "txt",
|
|
16
9
|
},
|
|
17
10
|
{
|
|
18
|
-
label: "
|
|
19
|
-
value: "
|
|
11
|
+
label: "VTT",
|
|
12
|
+
value: "vtt",
|
|
20
13
|
},
|
|
21
14
|
{
|
|
22
|
-
label: "
|
|
23
|
-
value: "
|
|
15
|
+
label: "SRT",
|
|
16
|
+
value: "srt",
|
|
24
17
|
},
|
|
25
18
|
];
|
package/grain.app.mjs
CHANGED
|
@@ -4,16 +4,16 @@ export default {
|
|
|
4
4
|
type: "app",
|
|
5
5
|
app: "grain",
|
|
6
6
|
propDefinitions: {
|
|
7
|
-
|
|
7
|
+
recordingId: {
|
|
8
8
|
type: "string",
|
|
9
|
-
label: "
|
|
10
|
-
description: "The ID of the recording to fetch",
|
|
11
|
-
async options({ prevContext
|
|
9
|
+
label: "Recording ID",
|
|
10
|
+
description: "The ID of the recording to fetch. Use **List Recordings** to find recording IDs.",
|
|
11
|
+
async options({ prevContext }) {
|
|
12
12
|
const {
|
|
13
13
|
recordings, cursor,
|
|
14
14
|
} = await this.listRecordings({
|
|
15
|
-
|
|
16
|
-
cursor: nextPage,
|
|
15
|
+
data: {
|
|
16
|
+
cursor: prevContext?.nextPage,
|
|
17
17
|
},
|
|
18
18
|
});
|
|
19
19
|
return {
|
|
@@ -29,42 +29,63 @@ export default {
|
|
|
29
29
|
};
|
|
30
30
|
},
|
|
31
31
|
},
|
|
32
|
-
|
|
33
|
-
type: "
|
|
34
|
-
label: "
|
|
35
|
-
description: "
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
32
|
+
highlights: {
|
|
33
|
+
type: "boolean",
|
|
34
|
+
label: "Include Highlights",
|
|
35
|
+
description: "Whether to include the recording's highlights",
|
|
36
|
+
optional: true,
|
|
37
|
+
},
|
|
38
|
+
participants: {
|
|
39
|
+
type: "boolean",
|
|
40
|
+
label: "Include Participants",
|
|
41
|
+
description: "Whether to include the recording's participants",
|
|
42
|
+
optional: true,
|
|
43
|
+
},
|
|
44
|
+
calendarEvent: {
|
|
45
|
+
type: "boolean",
|
|
46
|
+
label: "Include Calendar Event",
|
|
47
|
+
description: "Whether to include the recording's calendar event data",
|
|
48
|
+
optional: true,
|
|
49
|
+
},
|
|
50
|
+
hubspot: {
|
|
51
|
+
type: "boolean",
|
|
52
|
+
label: "Include HubSpot Data",
|
|
53
|
+
description: "Whether to include associated HubSpot data",
|
|
54
|
+
optional: true,
|
|
55
|
+
},
|
|
56
|
+
aiActionItems: {
|
|
57
|
+
type: "boolean",
|
|
58
|
+
label: "Include AI Action Items",
|
|
59
|
+
description: "Whether to include the recording's AI action items",
|
|
60
|
+
optional: true,
|
|
61
|
+
},
|
|
62
|
+
aiSummary: {
|
|
63
|
+
type: "boolean",
|
|
64
|
+
label: "Include AI Summary",
|
|
65
|
+
description: "Whether to include the recording's AI summary",
|
|
66
|
+
optional: true,
|
|
67
|
+
},
|
|
68
|
+
transcript: {
|
|
69
|
+
type: "boolean",
|
|
70
|
+
label: "Include Transcript",
|
|
71
|
+
description: "Whether to include the highlight's transcript",
|
|
72
|
+
optional: true,
|
|
73
|
+
},
|
|
74
|
+
speakers: {
|
|
75
|
+
type: "boolean",
|
|
76
|
+
label: "Include Speakers",
|
|
77
|
+
description: "Whether to include the highlight's speakers",
|
|
78
|
+
optional: true,
|
|
59
79
|
},
|
|
60
80
|
},
|
|
61
81
|
methods: {
|
|
62
82
|
_baseUrl() {
|
|
63
|
-
return "https://grain.com/_/public-api";
|
|
83
|
+
return "https://api.grain.com/_/public-api/v2";
|
|
64
84
|
},
|
|
65
85
|
_headers() {
|
|
66
86
|
return {
|
|
67
|
-
Authorization: `Bearer ${this.$auth.oauth_access_token}`,
|
|
87
|
+
"Authorization": `Bearer ${this.$auth.oauth_access_token}`,
|
|
88
|
+
"Public-Api-Version": "2025-10-31",
|
|
68
89
|
};
|
|
69
90
|
},
|
|
70
91
|
_makeRequest({
|
|
@@ -76,33 +97,64 @@ export default {
|
|
|
76
97
|
...opts,
|
|
77
98
|
});
|
|
78
99
|
},
|
|
100
|
+
/**
|
|
101
|
+
* Fetch a page of recordings matching the supplied filters.
|
|
102
|
+
* @param {object} [opts={}] Request context and data containing filter, include, and cursor.
|
|
103
|
+
* @returns {Promise<object>} Recordings and the cursor for the next page.
|
|
104
|
+
*/
|
|
79
105
|
listRecordings(opts = {}) {
|
|
80
106
|
return this._makeRequest({
|
|
107
|
+
method: "POST",
|
|
81
108
|
path: "/recordings",
|
|
82
109
|
...opts,
|
|
83
110
|
});
|
|
84
111
|
},
|
|
85
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Fetch recording metadata and optional related data.
|
|
114
|
+
* @param {object} opts Request context, recordingId, and data containing include options.
|
|
115
|
+
* @returns {Promise<object>} The recording.
|
|
116
|
+
*/
|
|
117
|
+
fetchRecording({
|
|
118
|
+
recordingId, ...opts
|
|
119
|
+
}) {
|
|
86
120
|
return this._makeRequest({
|
|
87
|
-
|
|
121
|
+
method: "POST",
|
|
122
|
+
path: `/recordings/${recordingId}`,
|
|
88
123
|
...opts,
|
|
89
124
|
});
|
|
90
125
|
},
|
|
91
|
-
|
|
92
|
-
|
|
126
|
+
/**
|
|
127
|
+
* Fetch a recording's transcript in the requested format.
|
|
128
|
+
* @param {object} opts Request context, recordingId, and format (json, txt, vtt, or srt).
|
|
129
|
+
* @returns {Promise<object[]|string>} Transcript segments for JSON, or transcript text.
|
|
130
|
+
*/
|
|
131
|
+
fetchTranscript({
|
|
132
|
+
recordingId, format, ...opts
|
|
93
133
|
}) {
|
|
94
134
|
return this._makeRequest({
|
|
95
|
-
path: `/recordings/${
|
|
135
|
+
path: `/recordings/${recordingId}/transcript${format === "json"
|
|
136
|
+
? ""
|
|
137
|
+
: `.${format}`}`,
|
|
96
138
|
...opts,
|
|
97
139
|
});
|
|
98
140
|
},
|
|
141
|
+
/**
|
|
142
|
+
* Register a webhook for a Grain event type.
|
|
143
|
+
* @param {object} [opts={}] Request options with hook_url, hook_type, and include in data.
|
|
144
|
+
* @returns {Promise<object>} The registered hook, including its ID.
|
|
145
|
+
*/
|
|
99
146
|
createWebhook(opts = {}) {
|
|
100
147
|
return this._makeRequest({
|
|
101
148
|
method: "POST",
|
|
102
|
-
path: "/hooks",
|
|
149
|
+
path: "/hooks/create",
|
|
103
150
|
...opts,
|
|
104
151
|
});
|
|
105
152
|
},
|
|
153
|
+
/**
|
|
154
|
+
* Remove a webhook registration.
|
|
155
|
+
* @param {string} hookId The ID returned when the hook was created.
|
|
156
|
+
* @returns {Promise<object>} The API's success response.
|
|
157
|
+
*/
|
|
106
158
|
deleteWebhook(hookId) {
|
|
107
159
|
return this._makeRequest({
|
|
108
160
|
method: "DELETE",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pipedream/grain",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Pipedream Grain Components",
|
|
5
5
|
"main": "grain.app.mjs",
|
|
6
6
|
"keywords": [
|
|
@@ -13,6 +13,6 @@
|
|
|
13
13
|
"access": "public"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@pipedream/platform": "^3.
|
|
16
|
+
"@pipedream/platform": "^3.1.1"
|
|
17
17
|
}
|
|
18
18
|
}
|
package/sources/common/base.mjs
CHANGED
|
@@ -3,7 +3,10 @@ import grain from "../../grain.app.mjs";
|
|
|
3
3
|
export default {
|
|
4
4
|
props: {
|
|
5
5
|
grain,
|
|
6
|
-
http:
|
|
6
|
+
http: {
|
|
7
|
+
type: "$.interface.http",
|
|
8
|
+
customResponse: true,
|
|
9
|
+
},
|
|
7
10
|
db: "$.service.db",
|
|
8
11
|
},
|
|
9
12
|
methods: {
|
|
@@ -13,30 +16,49 @@ export default {
|
|
|
13
16
|
_setHookId(hookId) {
|
|
14
17
|
this.db.set("hookId", hookId);
|
|
15
18
|
},
|
|
19
|
+
getInclude() {
|
|
20
|
+
return undefined;
|
|
21
|
+
},
|
|
22
|
+
getTimestamp() {
|
|
23
|
+
return Date.now();
|
|
24
|
+
},
|
|
16
25
|
},
|
|
17
26
|
hooks: {
|
|
18
27
|
async activate() {
|
|
19
28
|
const response = await this.grain.createWebhook({
|
|
20
29
|
data: {
|
|
21
|
-
version: 2,
|
|
22
30
|
hook_url: this.http.endpoint,
|
|
23
|
-
|
|
24
|
-
|
|
31
|
+
hook_type: this.getHookType(),
|
|
32
|
+
include: this.getInclude(),
|
|
25
33
|
},
|
|
26
34
|
});
|
|
27
35
|
this._setHookId(response.id);
|
|
28
36
|
},
|
|
29
37
|
async deactivate() {
|
|
30
38
|
const webhookId = this._getHookId();
|
|
31
|
-
|
|
39
|
+
if (webhookId) {
|
|
40
|
+
await this.grain.deleteWebhook(webhookId);
|
|
41
|
+
}
|
|
32
42
|
},
|
|
33
43
|
},
|
|
34
44
|
async run({ body }) {
|
|
35
|
-
|
|
45
|
+
this.http.respond({
|
|
46
|
+
status: 200,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (!body?.data?.id || body.type !== this.getHookType()) return;
|
|
50
|
+
|
|
51
|
+
const ts = this.getTimestamp(body);
|
|
52
|
+
// Grain doesn't document a delivery ID. Added/deleted events dedupe on the
|
|
53
|
+
// resource ID alone (there's only ever one). Updated events concatenate the
|
|
54
|
+
// resource ID with the payload-derived timestamp so retries of the same
|
|
55
|
+
// update share an ID while a later, distinct update gets a new one.
|
|
56
|
+
const id = body.type.endsWith("_updated")
|
|
57
|
+
? `${body.data.id}:${ts}`
|
|
58
|
+
: body.data.id;
|
|
36
59
|
|
|
37
|
-
const ts = Date.parse(new Date());
|
|
38
60
|
this.$emit(body, {
|
|
39
|
-
id
|
|
61
|
+
id,
|
|
40
62
|
summary: this.getSummary(body),
|
|
41
63
|
ts,
|
|
42
64
|
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import common from "./base.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
...common,
|
|
5
|
+
props: {
|
|
6
|
+
...common.props,
|
|
7
|
+
transcript: {
|
|
8
|
+
propDefinition: [
|
|
9
|
+
common.props.grain,
|
|
10
|
+
"transcript",
|
|
11
|
+
],
|
|
12
|
+
},
|
|
13
|
+
speakers: {
|
|
14
|
+
propDefinition: [
|
|
15
|
+
common.props.grain,
|
|
16
|
+
"speakers",
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
methods: {
|
|
21
|
+
...common.methods,
|
|
22
|
+
getInclude() {
|
|
23
|
+
const include = {
|
|
24
|
+
transcript: this.transcript,
|
|
25
|
+
speakers: this.speakers,
|
|
26
|
+
};
|
|
27
|
+
return Object.fromEntries(Object.entries(include).filter(([
|
|
28
|
+
, value,
|
|
29
|
+
]) => value));
|
|
30
|
+
},
|
|
31
|
+
getTimestamp({ data }) {
|
|
32
|
+
const ts = Date.parse(data.created_datetime);
|
|
33
|
+
return Number.isNaN(ts)
|
|
34
|
+
? Date.now()
|
|
35
|
+
: ts;
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import common from "./base.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
...common,
|
|
5
|
+
props: {
|
|
6
|
+
...common.props,
|
|
7
|
+
highlights: {
|
|
8
|
+
propDefinition: [
|
|
9
|
+
common.props.grain,
|
|
10
|
+
"highlights",
|
|
11
|
+
],
|
|
12
|
+
},
|
|
13
|
+
participants: {
|
|
14
|
+
propDefinition: [
|
|
15
|
+
common.props.grain,
|
|
16
|
+
"participants",
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
calendarEvent: {
|
|
20
|
+
propDefinition: [
|
|
21
|
+
common.props.grain,
|
|
22
|
+
"calendarEvent",
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
hubspot: {
|
|
26
|
+
propDefinition: [
|
|
27
|
+
common.props.grain,
|
|
28
|
+
"hubspot",
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
aiActionItems: {
|
|
32
|
+
propDefinition: [
|
|
33
|
+
common.props.grain,
|
|
34
|
+
"aiActionItems",
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
aiSummary: {
|
|
38
|
+
propDefinition: [
|
|
39
|
+
common.props.grain,
|
|
40
|
+
"aiSummary",
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
methods: {
|
|
45
|
+
...common.methods,
|
|
46
|
+
getInclude() {
|
|
47
|
+
const include = {
|
|
48
|
+
highlights: this.highlights,
|
|
49
|
+
participants: this.participants,
|
|
50
|
+
calendar_event: this.calendarEvent,
|
|
51
|
+
hubspot: this.hubspot,
|
|
52
|
+
ai_action_items: this.aiActionItems,
|
|
53
|
+
ai_summary: this.aiSummary,
|
|
54
|
+
};
|
|
55
|
+
return Object.fromEntries(Object.entries(include).filter(([
|
|
56
|
+
, value,
|
|
57
|
+
]) => value));
|
|
58
|
+
},
|
|
59
|
+
getTimestamp({ data }) {
|
|
60
|
+
const ts = Date.parse(data.end_datetime);
|
|
61
|
+
return Number.isNaN(ts)
|
|
62
|
+
? Date.now()
|
|
63
|
+
: ts;
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
};
|
|
@@ -1,32 +1,18 @@
|
|
|
1
|
-
import common from "../common/
|
|
1
|
+
import common from "../common/highlight.mjs";
|
|
2
2
|
import sampleEmit from "./test-event.mjs";
|
|
3
3
|
|
|
4
4
|
export default {
|
|
5
5
|
...common,
|
|
6
6
|
key: "grain-new-highlight-instant",
|
|
7
7
|
name: "New Highlight (Instant)",
|
|
8
|
-
description: "Emit new event when a highlight
|
|
9
|
-
version: "0.0
|
|
8
|
+
description: "Emit new event when a highlight is added. [See the documentation](https://developers.grain.com/#create-hook)",
|
|
9
|
+
version: "1.0.0",
|
|
10
10
|
type: "source",
|
|
11
11
|
dedupe: "unique",
|
|
12
|
-
props: {
|
|
13
|
-
...common.props,
|
|
14
|
-
viewId: {
|
|
15
|
-
propDefinition: [
|
|
16
|
-
common.props.grain,
|
|
17
|
-
"viewId",
|
|
18
|
-
() => ({
|
|
19
|
-
type: "highlights",
|
|
20
|
-
}),
|
|
21
|
-
],
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
12
|
methods: {
|
|
25
13
|
...common.methods,
|
|
26
|
-
|
|
27
|
-
return
|
|
28
|
-
"added",
|
|
29
|
-
];
|
|
14
|
+
getHookType() {
|
|
15
|
+
return "highlight_added";
|
|
30
16
|
},
|
|
31
17
|
getSummary({ data }) {
|
|
32
18
|
return `New highlight added: ${data.id}`;
|
|
@@ -2,16 +2,20 @@ export default {
|
|
|
2
2
|
"type": "highlight_added",
|
|
3
3
|
"user_id": "aea95745-99e9-4609-8623-c9efa2926b82",
|
|
4
4
|
"data": {
|
|
5
|
-
"id": "
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
"id": "a14e5af9-d28e-43e9-902b-bc07419082eb",
|
|
6
|
+
"recording_id": "b5185ccb-9a08-458c-9be1-db17a03fb14c",
|
|
7
|
+
"text": "testing 123 #test",
|
|
8
8
|
"transcript": "expected, that there was a mews in a lane which runs down by one wall of the garden. I lent the ostlers a hand in rubbing down their horses, and received in exchange twopence, a glass of half-and-half, two fills of shag tobacco, and as much information as I could desire about Miss Adler, to say nothing of half a dozen other people in",
|
|
9
|
-
"speakers": [
|
|
9
|
+
"speakers": [
|
|
10
|
+
"Andy Arbol"
|
|
11
|
+
],
|
|
10
12
|
"timestamp": 3080,
|
|
11
13
|
"duration": 15000,
|
|
12
|
-
|
|
13
|
-
"url": "https://grain.com/highlight/
|
|
14
|
+
"created_datetime": "2021-07-29T23:16:34Z",
|
|
15
|
+
"url": "https://grain.com/highlight/a14e5af9-d28e-43e9-902b-bc07419082eb",
|
|
14
16
|
"thumbnail_url": "https://media.grain.com/clips/v1/a14e5af9-d28e-43e9-902b-bc07419082eb/57zB8z52l7BKPoOvkS9KNyUi7LDSsNEh.jpeg",
|
|
15
|
-
"tags": [
|
|
17
|
+
"tags": [
|
|
18
|
+
"test"
|
|
19
|
+
]
|
|
16
20
|
}
|
|
17
|
-
}
|
|
21
|
+
};
|
|
@@ -1,32 +1,18 @@
|
|
|
1
|
-
import common from "../common/
|
|
1
|
+
import common from "../common/recording.mjs";
|
|
2
2
|
import sampleEmit from "./test-event.mjs";
|
|
3
3
|
|
|
4
4
|
export default {
|
|
5
5
|
...common,
|
|
6
6
|
key: "grain-new-recording-instant",
|
|
7
7
|
name: "New Recording (Instant)",
|
|
8
|
-
description: "Emit new event when a recording
|
|
9
|
-
version: "0.0
|
|
8
|
+
description: "Emit new event when a recording is added. [See the documentation](https://developers.grain.com/#create-hook)",
|
|
9
|
+
version: "1.0.0",
|
|
10
10
|
type: "source",
|
|
11
11
|
dedupe: "unique",
|
|
12
|
-
props: {
|
|
13
|
-
...common.props,
|
|
14
|
-
viewId: {
|
|
15
|
-
propDefinition: [
|
|
16
|
-
common.props.grain,
|
|
17
|
-
"viewId",
|
|
18
|
-
() => ({
|
|
19
|
-
type: "recordings",
|
|
20
|
-
}),
|
|
21
|
-
],
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
12
|
methods: {
|
|
25
13
|
...common.methods,
|
|
26
|
-
|
|
27
|
-
return
|
|
28
|
-
"added",
|
|
29
|
-
];
|
|
14
|
+
getHookType() {
|
|
15
|
+
return "recording_added";
|
|
30
16
|
},
|
|
31
17
|
getSummary({ data }) {
|
|
32
18
|
return `New recording added: ${data.id}`;
|
|
@@ -2,11 +2,26 @@ export default {
|
|
|
2
2
|
"type": "recording_added",
|
|
3
3
|
"user_id": "aea95745-99e9-4609-8623-c9efa2926b82",
|
|
4
4
|
"data": {
|
|
5
|
-
"id": "
|
|
6
|
-
"title": "
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
"id": "pppp6666-qq77-rr88-ss99-tttt00000000",
|
|
6
|
+
"title": "All Hands",
|
|
7
|
+
"start_datetime": "2025-01-01T09:30:00Z",
|
|
8
|
+
"end_datetime": "2025-01-01T10:00:00Z",
|
|
9
|
+
"duration_ms": 1800000,
|
|
10
|
+
"media_type": "video",
|
|
11
|
+
"source": "zoom",
|
|
12
|
+
"url": "https://grain.com/share/recording/pppp6666-qq77-rr88-ss99-tttt00000000",
|
|
13
|
+
"thumbnail_url": "https://media.grain.com/public_thumbnails/recordings/pppp6666",
|
|
14
|
+
"tags": [],
|
|
15
|
+
"teams": [
|
|
16
|
+
{
|
|
17
|
+
"id": "aaaa1111-bb22-cc33-dd44-eeee55555555",
|
|
18
|
+
"name": "My Team",
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
"meeting_type": {
|
|
22
|
+
"id": "ffff6666-gg77-hh88-ii99-jjjj00000000",
|
|
23
|
+
"name": "Project & Team Coordination",
|
|
24
|
+
"scope": "internal",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
};
|
|
@@ -5,28 +5,20 @@ export default {
|
|
|
5
5
|
...common,
|
|
6
6
|
key: "grain-new-story-instant",
|
|
7
7
|
name: "New Story (Instant)",
|
|
8
|
-
description: "Emit new event when a story
|
|
9
|
-
version: "0.0
|
|
8
|
+
description: "Emit new event when a story is added. [See the documentation](https://developers.grain.com/#create-hook)",
|
|
9
|
+
version: "1.0.0",
|
|
10
10
|
type: "source",
|
|
11
11
|
dedupe: "unique",
|
|
12
|
-
props: {
|
|
13
|
-
...common.props,
|
|
14
|
-
viewId: {
|
|
15
|
-
propDefinition: [
|
|
16
|
-
common.props.grain,
|
|
17
|
-
"viewId",
|
|
18
|
-
() => ({
|
|
19
|
-
type: "stories",
|
|
20
|
-
}),
|
|
21
|
-
],
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
12
|
methods: {
|
|
25
13
|
...common.methods,
|
|
26
|
-
|
|
27
|
-
return
|
|
28
|
-
|
|
29
|
-
|
|
14
|
+
getHookType() {
|
|
15
|
+
return "story_added";
|
|
16
|
+
},
|
|
17
|
+
getTimestamp({ data }) {
|
|
18
|
+
const ts = Date.parse(data.created_datetime);
|
|
19
|
+
return Number.isNaN(ts)
|
|
20
|
+
? Date.now()
|
|
21
|
+
: ts;
|
|
30
22
|
},
|
|
31
23
|
getSummary({ data }) {
|
|
32
24
|
return `New story added: ${data.id}`;
|
|
@@ -8,8 +8,10 @@ export default {
|
|
|
8
8
|
"url": "https://grain.com/app/stories/89bd4a02-25f5-42c0-bd40-aa4c94be13ce",
|
|
9
9
|
"public_url": "https://grain.com/share/story/89bd4a02-25f5-42c0-bd40-aa4c94be13ce/2hAEpxLsIN8hDQ48aQ1Yi1MIirv1qCPSJNhxXEoj",
|
|
10
10
|
"banner_image_url": "https://media.grain.com/public/story_thumbnails/07.png",
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"tags": [
|
|
11
|
+
"created_datetime": "2021-07-29T23:16:34Z",
|
|
12
|
+
"last_edited_datetime": "2021-08-29T23:16:34Z",
|
|
13
|
+
"tags": [
|
|
14
|
+
"customer"
|
|
15
|
+
]
|
|
14
16
|
}
|
|
15
|
-
}
|
|
17
|
+
};
|
|
@@ -5,31 +5,17 @@ export default {
|
|
|
5
5
|
...common,
|
|
6
6
|
key: "grain-removed-highlight-instant",
|
|
7
7
|
name: "New Highlight Removed (Instant)",
|
|
8
|
-
description: "Emit new event when a highlight is removed.",
|
|
9
|
-
version: "0.0
|
|
8
|
+
description: "Emit new event when a highlight is removed. [See the documentation](https://developers.grain.com/#create-hook)",
|
|
9
|
+
version: "1.0.0",
|
|
10
10
|
type: "source",
|
|
11
11
|
dedupe: "unique",
|
|
12
|
-
props: {
|
|
13
|
-
...common.props,
|
|
14
|
-
viewId: {
|
|
15
|
-
propDefinition: [
|
|
16
|
-
common.props.grain,
|
|
17
|
-
"viewId",
|
|
18
|
-
() => ({
|
|
19
|
-
type: "highlights",
|
|
20
|
-
}),
|
|
21
|
-
],
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
12
|
methods: {
|
|
25
13
|
...common.methods,
|
|
26
|
-
|
|
27
|
-
return
|
|
28
|
-
"removed",
|
|
29
|
-
];
|
|
14
|
+
getHookType() {
|
|
15
|
+
return "highlight_deleted";
|
|
30
16
|
},
|
|
31
17
|
getSummary({ data }) {
|
|
32
|
-
return `Highlight removed
|
|
18
|
+
return `Highlight removed: ${data.id}`;
|
|
33
19
|
},
|
|
34
20
|
},
|
|
35
21
|
sampleEmit,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export default {
|
|
2
|
-
"type": "
|
|
2
|
+
"type": "highlight_deleted",
|
|
3
3
|
"user_id": "aea95745-99e9-4609-8623-c9efa2926b82",
|
|
4
4
|
"data": {
|
|
5
|
-
"id": "
|
|
5
|
+
"id": "a14e5af9-d28e-43e9-902b-bc07419082eb",
|
|
6
6
|
"recording_id": "b5185ccb-9a08-458c-9be1-db17a03fb14c"
|
|
7
7
|
}
|
|
8
|
-
}
|
|
8
|
+
};
|
|
@@ -5,28 +5,14 @@ export default {
|
|
|
5
5
|
...common,
|
|
6
6
|
key: "grain-removed-recording-instant",
|
|
7
7
|
name: "New Recording Removed (Instant)",
|
|
8
|
-
description: "Emit new event when a recording is removed.",
|
|
9
|
-
version: "0.0
|
|
8
|
+
description: "Emit new event when a recording is removed. [See the documentation](https://developers.grain.com/#create-hook)",
|
|
9
|
+
version: "1.0.0",
|
|
10
10
|
type: "source",
|
|
11
11
|
dedupe: "unique",
|
|
12
|
-
props: {
|
|
13
|
-
...common.props,
|
|
14
|
-
viewId: {
|
|
15
|
-
propDefinition: [
|
|
16
|
-
common.props.grain,
|
|
17
|
-
"viewId",
|
|
18
|
-
() => ({
|
|
19
|
-
type: "recordings",
|
|
20
|
-
}),
|
|
21
|
-
],
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
12
|
methods: {
|
|
25
13
|
...common.methods,
|
|
26
|
-
|
|
27
|
-
return
|
|
28
|
-
"removed",
|
|
29
|
-
];
|
|
14
|
+
getHookType() {
|
|
15
|
+
return "recording_deleted";
|
|
30
16
|
},
|
|
31
17
|
getSummary({ data }) {
|
|
32
18
|
return `Recording removed: ${data.id}`;
|
|
@@ -5,28 +5,14 @@ export default {
|
|
|
5
5
|
...common,
|
|
6
6
|
key: "grain-removed-story-instant",
|
|
7
7
|
name: "New Story Removed (Instant)",
|
|
8
|
-
description: "Emit new event when a story is removed.",
|
|
9
|
-
version: "0.0
|
|
8
|
+
description: "Emit new event when a story is removed. [See the documentation](https://developers.grain.com/#create-hook)",
|
|
9
|
+
version: "1.0.0",
|
|
10
10
|
type: "source",
|
|
11
11
|
dedupe: "unique",
|
|
12
|
-
props: {
|
|
13
|
-
...common.props,
|
|
14
|
-
viewId: {
|
|
15
|
-
propDefinition: [
|
|
16
|
-
common.props.grain,
|
|
17
|
-
"viewId",
|
|
18
|
-
() => ({
|
|
19
|
-
type: "stories",
|
|
20
|
-
}),
|
|
21
|
-
],
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
12
|
methods: {
|
|
25
13
|
...common.methods,
|
|
26
|
-
|
|
27
|
-
return
|
|
28
|
-
"removed",
|
|
29
|
-
];
|
|
14
|
+
getHookType() {
|
|
15
|
+
return "story_deleted";
|
|
30
16
|
},
|
|
31
17
|
getSummary({ data }) {
|
|
32
18
|
return `New story removed: ${data.id}`;
|
|
@@ -2,16 +2,20 @@ export default {
|
|
|
2
2
|
"type": "highlight_updated",
|
|
3
3
|
"user_id": "aea95745-99e9-4609-8623-c9efa2926b82",
|
|
4
4
|
"data": {
|
|
5
|
-
"id": "
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
"id": "a14e5af9-d28e-43e9-902b-bc07419082eb",
|
|
6
|
+
"recording_id": "b5185ccb-9a08-458c-9be1-db17a03fb14c",
|
|
7
|
+
"text": "testing 123 #test",
|
|
8
8
|
"transcript": "expected, that there was a mews in a lane which runs down by one wall of the garden. I lent the ostlers a hand in rubbing down their horses, and received in exchange twopence, a glass of half-and-half, two fills of shag tobacco, and as much information as I could desire about Miss Adler, to say nothing of half a dozen other people in",
|
|
9
|
-
"speakers": [
|
|
9
|
+
"speakers": [
|
|
10
|
+
"Andy Arbol"
|
|
11
|
+
],
|
|
10
12
|
"timestamp": 3080,
|
|
11
13
|
"duration": 15000,
|
|
12
|
-
|
|
13
|
-
"url": "https://grain.com/highlight/
|
|
14
|
+
"created_datetime": "2021-07-29T23:16:34Z",
|
|
15
|
+
"url": "https://grain.com/highlight/a14e5af9-d28e-43e9-902b-bc07419082eb",
|
|
14
16
|
"thumbnail_url": "https://media.grain.com/clips/v1/a14e5af9-d28e-43e9-902b-bc07419082eb/57zB8z52l7BKPoOvkS9KNyUi7LDSsNEh.jpeg",
|
|
15
|
-
"tags": [
|
|
17
|
+
"tags": [
|
|
18
|
+
"test"
|
|
19
|
+
]
|
|
16
20
|
}
|
|
17
|
-
}
|
|
21
|
+
};
|
|
@@ -1,32 +1,18 @@
|
|
|
1
|
-
import common from "../common/
|
|
1
|
+
import common from "../common/highlight.mjs";
|
|
2
2
|
import sampleEmit from "./test-event.mjs";
|
|
3
3
|
|
|
4
4
|
export default {
|
|
5
5
|
...common,
|
|
6
6
|
key: "grain-updated-highlight-instant",
|
|
7
7
|
name: "New Highlight Updated (Instant)",
|
|
8
|
-
description: "Emit new event when a highlight is updated.",
|
|
9
|
-
version: "0.0
|
|
8
|
+
description: "Emit new event when a highlight is updated. Each webhook delivery emits an event, including retries. [See the documentation](https://developers.grain.com/#create-hook)",
|
|
9
|
+
version: "1.0.0",
|
|
10
10
|
type: "source",
|
|
11
|
-
|
|
12
|
-
props: {
|
|
13
|
-
...common.props,
|
|
14
|
-
viewId: {
|
|
15
|
-
propDefinition: [
|
|
16
|
-
common.props.grain,
|
|
17
|
-
"viewId",
|
|
18
|
-
() => ({
|
|
19
|
-
type: "highlights",
|
|
20
|
-
}),
|
|
21
|
-
],
|
|
22
|
-
},
|
|
23
|
-
},
|
|
11
|
+
// Grain does not document a delivery ID; deduping by resource ID would discard later updates.
|
|
24
12
|
methods: {
|
|
25
13
|
...common.methods,
|
|
26
|
-
|
|
27
|
-
return
|
|
28
|
-
"updated",
|
|
29
|
-
];
|
|
14
|
+
getHookType() {
|
|
15
|
+
return "highlight_updated";
|
|
30
16
|
},
|
|
31
17
|
getSummary({ data }) {
|
|
32
18
|
return `New highlight updated: ${data.id}`;
|
|
@@ -2,11 +2,26 @@ export default {
|
|
|
2
2
|
"type": "recording_updated",
|
|
3
3
|
"user_id": "aea95745-99e9-4609-8623-c9efa2926b82",
|
|
4
4
|
"data": {
|
|
5
|
-
"id": "
|
|
6
|
-
"title": "
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
"id": "pppp6666-qq77-rr88-ss99-tttt00000000",
|
|
6
|
+
"title": "All Hands — Updated",
|
|
7
|
+
"start_datetime": "2025-01-01T09:30:00Z",
|
|
8
|
+
"end_datetime": "2025-01-01T10:00:00Z",
|
|
9
|
+
"duration_ms": 1800000,
|
|
10
|
+
"media_type": "video",
|
|
11
|
+
"source": "zoom",
|
|
12
|
+
"url": "https://grain.com/share/recording/pppp6666-qq77-rr88-ss99-tttt00000000",
|
|
13
|
+
"thumbnail_url": "https://media.grain.com/public_thumbnails/recordings/pppp6666",
|
|
14
|
+
"tags": [],
|
|
15
|
+
"teams": [
|
|
16
|
+
{
|
|
17
|
+
"id": "aaaa1111-bb22-cc33-dd44-eeee55555555",
|
|
18
|
+
"name": "My Team",
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
"meeting_type": {
|
|
22
|
+
"id": "ffff6666-gg77-hh88-ii99-jjjj00000000",
|
|
23
|
+
"name": "Project & Team Coordination",
|
|
24
|
+
"scope": "internal",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
};
|
|
@@ -1,32 +1,18 @@
|
|
|
1
|
-
import common from "../common/
|
|
1
|
+
import common from "../common/recording.mjs";
|
|
2
2
|
import sampleEmit from "./test-event.mjs";
|
|
3
3
|
|
|
4
4
|
export default {
|
|
5
5
|
...common,
|
|
6
6
|
key: "grain-updated-recording-instant",
|
|
7
7
|
name: "New Recording Updated (Instant)",
|
|
8
|
-
description: "Emit new event when a recording is updated.",
|
|
9
|
-
version: "0.0
|
|
8
|
+
description: "Emit new event when a recording is updated. Each webhook delivery emits an event, including retries. [See the documentation](https://developers.grain.com/#create-hook)",
|
|
9
|
+
version: "1.0.0",
|
|
10
10
|
type: "source",
|
|
11
|
-
|
|
12
|
-
props: {
|
|
13
|
-
...common.props,
|
|
14
|
-
viewId: {
|
|
15
|
-
propDefinition: [
|
|
16
|
-
common.props.grain,
|
|
17
|
-
"viewId",
|
|
18
|
-
() => ({
|
|
19
|
-
type: "recordings",
|
|
20
|
-
}),
|
|
21
|
-
],
|
|
22
|
-
},
|
|
23
|
-
},
|
|
11
|
+
// Grain does not document a delivery ID; deduping by resource ID would discard later updates.
|
|
24
12
|
methods: {
|
|
25
13
|
...common.methods,
|
|
26
|
-
|
|
27
|
-
return
|
|
28
|
-
"updated",
|
|
29
|
-
];
|
|
14
|
+
getHookType() {
|
|
15
|
+
return "recording_updated";
|
|
30
16
|
},
|
|
31
17
|
getSummary({ data }) {
|
|
32
18
|
return `New recording updated: ${data.id}`;
|
|
@@ -8,8 +8,10 @@ export default {
|
|
|
8
8
|
"url": "https://grain.com/app/stories/89bd4a02-25f5-42c0-bd40-aa4c94be13ce",
|
|
9
9
|
"public_url": "https://grain.com/share/story/89bd4a02-25f5-42c0-bd40-aa4c94be13ce/2hAEpxLsIN8hDQ48aQ1Yi1MIirv1qCPSJNhxXEoj",
|
|
10
10
|
"banner_image_url": "https://media.grain.com/public/story_thumbnails/07.png",
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"tags": [
|
|
11
|
+
"created_datetime": "2021-07-29T23:16:34Z",
|
|
12
|
+
"last_edited_datetime": "2021-08-29T23:16:34Z",
|
|
13
|
+
"tags": [
|
|
14
|
+
"customer"
|
|
15
|
+
]
|
|
14
16
|
}
|
|
15
|
-
}
|
|
17
|
+
};
|
|
@@ -5,28 +5,20 @@ export default {
|
|
|
5
5
|
...common,
|
|
6
6
|
key: "grain-updated-story-instant",
|
|
7
7
|
name: "New Story Updated (Instant)",
|
|
8
|
-
description: "Emit new event when a story is updated.",
|
|
9
|
-
version: "0.0
|
|
8
|
+
description: "Emit new event when a story is updated. Each webhook delivery emits an event, including retries. [See the documentation](https://developers.grain.com/#create-hook)",
|
|
9
|
+
version: "1.0.0",
|
|
10
10
|
type: "source",
|
|
11
|
-
|
|
12
|
-
props: {
|
|
13
|
-
...common.props,
|
|
14
|
-
viewId: {
|
|
15
|
-
propDefinition: [
|
|
16
|
-
common.props.grain,
|
|
17
|
-
"viewId",
|
|
18
|
-
() => ({
|
|
19
|
-
type: "stories",
|
|
20
|
-
}),
|
|
21
|
-
],
|
|
22
|
-
},
|
|
23
|
-
},
|
|
11
|
+
// Grain does not document a delivery ID; deduping by resource ID would discard later updates.
|
|
24
12
|
methods: {
|
|
25
13
|
...common.methods,
|
|
26
|
-
|
|
27
|
-
return
|
|
28
|
-
|
|
29
|
-
|
|
14
|
+
getHookType() {
|
|
15
|
+
return "story_updated";
|
|
16
|
+
},
|
|
17
|
+
getTimestamp({ data }) {
|
|
18
|
+
const ts = Date.parse(data.last_edited_datetime);
|
|
19
|
+
return Number.isNaN(ts)
|
|
20
|
+
? Date.now()
|
|
21
|
+
: ts;
|
|
30
22
|
},
|
|
31
23
|
getSummary({ data }) {
|
|
32
24
|
return `New story updated: ${data.id}`;
|
package/common/utils.mjs
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export const parseObject = (obj) => {
|
|
2
|
-
if (!obj) return undefined;
|
|
3
|
-
|
|
4
|
-
if (Array.isArray(obj)) {
|
|
5
|
-
return obj.map((item) => {
|
|
6
|
-
if (typeof item === "string") {
|
|
7
|
-
try {
|
|
8
|
-
return JSON.parse(item);
|
|
9
|
-
} catch (e) {
|
|
10
|
-
return item;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
return item;
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
if (typeof obj === "string") {
|
|
17
|
-
try {
|
|
18
|
-
return JSON.parse(obj);
|
|
19
|
-
} catch (e) {
|
|
20
|
-
return obj;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return obj;
|
|
24
|
-
};
|