Skip to content

Commit eed7d59

Browse files
authored
Add v2 Schedule Triggers (#1177)
Adds schedule triggers to the v2 namespace. - Replaces pub/sub with an http underlying function - Updated syntax to match with the other v2 triggers This change does not add the `invoker` property to the container contract. ``` export const sch = v2.scheduler.onSchedule("* * * * *", () => {}); export const sch = v2.scheduler.onSchedule( { schedule: "* * * * *", timeZone: "utc", }, () => {} ); ```
1 parent c0e0361 commit eed7d59

File tree

11 files changed

+982
-5
lines changed

11 files changed

+982
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
- Fixes a bug that disallowed setting customClaims and/or sessionClaims in blocking functions (#1199).
2+
- Add v2 Schedule Triggers (#1177).

integration_test/functions/src/index.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,32 @@ async function callScheduleTrigger(
101101
return;
102102
}
103103

104+
async function callV2ScheduleTrigger(
105+
functionName: string,
106+
region: string,
107+
accessToken: string
108+
) {
109+
const response = await fetch(
110+
`https://cloudscheduler.googleapis.com/v1/projects/${firebaseConfig.projectId}/locations/us-central1/jobs/firebase-schedule-${functionName}-${region}:run`,
111+
{
112+
method: 'POST',
113+
headers: {
114+
'Content-Type': 'application/json',
115+
Authorization: `Bearer ${accessToken}`,
116+
},
117+
}
118+
);
119+
if (!response.ok) {
120+
throw new Error(`Failed request with status ${response.status}!`);
121+
}
122+
const data = await response.text();
123+
functions.logger.log(
124+
`Successfully scheduled v2 function ${functionName}`,
125+
data
126+
);
127+
return;
128+
}
129+
104130
async function updateRemoteConfig(
105131
testId: string,
106132
accessToken: string
@@ -171,6 +197,8 @@ function v2Tests(testId: string, accessToken: string): Array<Promise<void>> {
171197
return [
172198
// Invoke a callable HTTPS trigger.
173199
callV2HttpsTrigger('v2-callabletests', { foo: 'bar', testId }, accessToken),
200+
// Invoke a scheduled trigger.
201+
callV2ScheduleTrigger('v2-schedule', 'us-central1', accessToken),
174202
];
175203
}
176204

integration_test/functions/src/v2/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ import { REGION } from '../region';
33
setGlobalOptions({ region: REGION });
44

55
export * from './https-tests';
6+
export * from './scheduled-tests';
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import * as admin from 'firebase-admin';
2+
import { onSchedule } from 'firebase-functions/v2/scheduler';
3+
import { REGION } from '../region';
4+
import { success, TestSuite } from '../testing';
5+
6+
export const schedule: any = onSchedule(
7+
{
8+
schedule: 'every 10 hours',
9+
region: REGION,
10+
},
11+
async (event) => {
12+
const db = admin.database();
13+
const snap = await db
14+
.ref('testRuns')
15+
.orderByChild('timestamp')
16+
.limitToLast(1)
17+
.once('value');
18+
const testId = Object.keys(snap.val())[0];
19+
return new TestSuite('scheduler scheduleOnRun')
20+
.it('should trigger when the scheduler fires', () => success())
21+
.run(testId, null);
22+
}
23+
);

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@
6666
"./v2/alerts/crashlytics": "./lib/v2/providers/alerts/crashlytics.js",
6767
"./v2/eventarc": "./lib/v2/providers/eventarc.js",
6868
"./v2/identity": "./lib/v2/providers/identity.js",
69-
"./v2/database": "./lib/v2/providers/database.js"
69+
"./v2/database": "./lib/v2/providers/database.js",
70+
"./v2/scheduler": "./lib/v2/providers/scheduler.js"
7071
},
7172
"typesVersions": {
7273
"*": {
@@ -153,6 +154,9 @@
153154
],
154155
"v2/tasks": [
155156
"lib/v2/providers/tasks"
157+
],
158+
"v2/scheduler": [
159+
"lib/v2/providers/scheduler"
156160
]
157161
}
158162
},

spec/v2/providers/scheduler.spec.ts

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// The MIT License (MIT)
2+
//
3+
// Copyright (c) 2022 Firebase
4+
//
5+
// Permission is hereby granted, free of charge, to any person obtaining a copy
6+
// of this software and associated documentation files (the "Software"), to deal
7+
// in the Software without restriction, including without limitation the rights
8+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
// copies of the Software, and to permit persons to whom the Software is
10+
// furnished to do so, subject to the following conditions:
11+
//
12+
// The above copyright notice and this permission notice shall be included in all
13+
// copies or substantial portions of the Software.
14+
//
15+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
// SOFTWARE.
22+
23+
import { expect } from 'chai';
24+
import * as schedule from '../../../src/v2/providers/scheduler';
25+
26+
describe('schedule', () => {
27+
describe('getOpts', () => {
28+
it('should handle a schedule', () => {
29+
expect(schedule.getOpts('* * * * *')).to.deep.eq({
30+
schedule: '* * * * *',
31+
opts: {},
32+
});
33+
});
34+
35+
it('should handle full options', () => {
36+
const options: schedule.ScheduleOptions = {
37+
schedule: '* * * * *',
38+
timeZone: 'utc',
39+
retryCount: 3,
40+
maxRetrySeconds: 1,
41+
minBackoffSeconds: 2,
42+
maxBackoffSeconds: 3,
43+
maxDoublings: 4,
44+
memory: '128MiB',
45+
region: 'us-central1',
46+
};
47+
48+
expect(schedule.getOpts(options)).to.deep.eq({
49+
schedule: '* * * * *',
50+
timeZone: 'utc',
51+
retryCount: 3,
52+
maxRetrySeconds: 1,
53+
minBackoffSeconds: 2,
54+
maxBackoffSeconds: 3,
55+
maxDoublings: 4,
56+
opts: {
57+
...options,
58+
memory: '128MiB',
59+
region: 'us-central1',
60+
},
61+
});
62+
});
63+
});
64+
65+
describe('onSchedule', () => {
66+
it('should create a schedule function given a schedule', () => {
67+
const schfn = schedule.onSchedule('* * * * *', (event) => console.log(1));
68+
69+
expect(schfn.__endpoint).to.deep.eq({
70+
platform: 'gcfv2',
71+
labels: {},
72+
scheduleTrigger: {
73+
schedule: '* * * * *',
74+
retryConfig: {},
75+
},
76+
});
77+
expect(schfn.__requiredAPIs).to.deep.eq([
78+
{
79+
api: 'cloudscheduler.googleapis.com',
80+
reason: 'Needed for scheduled functions.',
81+
},
82+
]);
83+
});
84+
85+
it('should create a schedule function given options', () => {
86+
const schfn = schedule.onSchedule(
87+
{
88+
schedule: '* * * * *',
89+
timeZone: 'utc',
90+
retryCount: 3,
91+
maxRetrySeconds: 10,
92+
minBackoffSeconds: 11,
93+
maxBackoffSeconds: 12,
94+
maxDoublings: 2,
95+
region: 'us-central1',
96+
labels: { key: 'val' },
97+
},
98+
(event) => {}
99+
);
100+
101+
expect(schfn.__endpoint).to.deep.eq({
102+
platform: 'gcfv2',
103+
labels: { key: 'val' },
104+
region: ['us-central1'],
105+
scheduleTrigger: {
106+
schedule: '* * * * *',
107+
timeZone: 'utc',
108+
retryConfig: {
109+
retryCount: 3,
110+
maxRetrySeconds: 10,
111+
minBackoffSeconds: 11,
112+
maxBackoffSeconds: 12,
113+
maxDoublings: 2,
114+
},
115+
},
116+
});
117+
expect(schfn.__requiredAPIs).to.deep.eq([
118+
{
119+
api: 'cloudscheduler.googleapis.com',
120+
reason: 'Needed for scheduled functions.',
121+
},
122+
]);
123+
});
124+
125+
it('should have a .run method', () => {
126+
const testObj = {
127+
foo: 'bar',
128+
};
129+
const schfn = schedule.onSchedule('* * * * *', (event) => {
130+
testObj.foo = 'newBar';
131+
});
132+
133+
schfn.run('input' as any);
134+
135+
expect(testObj).to.deep.eq({
136+
foo: 'newBar',
137+
});
138+
});
139+
});
140+
});

0 commit comments

Comments
 (0)