Skip to content

Commit 8d3093f

Browse files
authored
feat(core): Support useEmulators, more DI, and FCM fixes (#2652)
* Adding `USE_EMULATOR` to auth, database, firestore, and functions * Adding `VAPID_KEY` and `SERVICE_WORKER` DI tokens to messaging * Adding `SETTINGS`, `TENANT_ID`, `LANGUAGE_CODE`, `USE_DEVICE_LANGUAGE`, and `PERSISTENCE` DI tokens to auth * Adding `NEW_ORIGIN_BEHAVIOR` DI token to functions, to opt-into the new way of setting `ORIGIN` * Adding `MAX_UPLOAD_RETRY_TIME` and `MAX_OPERATION_RETRY_TIME` DI tokens to storage * `tokenChanges` will now listen for notification permission changes and trip token detection as expected * `httpsCallable` function now takes in `HttpsCallableOptions` * `deleteToken`'s token argument is now optional, reflecting Firebase v8 changes * Firestore snapshots now include metadata by default
1 parent 6c2473a commit 8d3093f

32 files changed

+534
-132
lines changed

sample/.firebaserc

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,8 @@
77
]
88
}
99
}
10+
},
11+
"projects": {
12+
"default": "aftest-94085"
1013
}
11-
}
14+
}

sample/angular.json

+11
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@
3232
"scripts": []
3333
},
3434
"configurations": {
35+
"emulated": {
36+
"fileReplacements": [
37+
{
38+
"replace": "src/environments/environment.ts",
39+
"with": "src/environments/environment.emulated.ts"
40+
}
41+
]
42+
},
3543
"production": {
3644
"fileReplacements": [
3745
{
@@ -71,6 +79,9 @@
7179
"configurations": {
7280
"production": {
7381
"browserTarget": "sample:build:production"
82+
},
83+
"emulated": {
84+
"browserTarget": "sample:build:emulated"
7485
}
7586
}
7687
},

sample/firebase.json

+33-9
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@
66
"ignore": [
77
"**/.*"
88
],
9-
"headers": [{
10-
"source": "*.[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].+(css|js)",
11-
"headers": [{
12-
"key": "Cache-Control",
13-
"value": "public,max-age=31536000,immutable"
14-
}]
15-
}],
9+
"headers": [
10+
{
11+
"source": "*.[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].+(css|js)",
12+
"headers": [
13+
{
14+
"key": "Cache-Control",
15+
"value": "public,max-age=31536000,immutable"
16+
}
17+
]
18+
}
19+
],
1620
"rewrites": [
1721
{
1822
"source": "**",
@@ -22,6 +26,26 @@
2226
}
2327
],
2428
"functions": {
25-
"source": "dist/sample"
29+
"source": "functions"
30+
},
31+
"emulators": {
32+
"functions": {
33+
"port": 5001
34+
},
35+
"firestore": {
36+
"port": 8080
37+
},
38+
"database": {
39+
"port": 9000
40+
},
41+
"hosting": {
42+
"port": 5000
43+
},
44+
"auth": {
45+
"port": 9099
46+
},
47+
"ui": {
48+
"enabled": true
49+
}
2650
}
27-
}
51+
}

sample/functions/.eslintrc.js

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
module.exports = {
2+
env: {
3+
browser: true,
4+
es6: true,
5+
node: true,
6+
},
7+
extends: [
8+
"plugin:import/errors",
9+
"plugin:import/warnings",
10+
"plugin:import/typescript",
11+
],
12+
parser: "@typescript-eslint/parser",
13+
parserOptions: {
14+
project: "tsconfig.json",
15+
sourceType: "module",
16+
},
17+
plugins: [
18+
"@typescript-eslint",
19+
"import",
20+
],
21+
rules: {
22+
"@typescript-eslint/adjacent-overload-signatures": "error",
23+
"@typescript-eslint/no-empty-function": "error",
24+
"@typescript-eslint/no-empty-interface": "warn",
25+
"@typescript-eslint/no-floating-promises": "error",
26+
"@typescript-eslint/no-namespace": "error",
27+
"@typescript-eslint/no-unnecessary-type-assertion": "error",
28+
"@typescript-eslint/prefer-for-of": "warn",
29+
"@typescript-eslint/triple-slash-reference": "error",
30+
"@typescript-eslint/unified-signatures": "warn",
31+
"comma-dangle": ["error", "always-multiline"],
32+
"constructor-super": "error",
33+
eqeqeq: ["warn", "always"],
34+
"import/no-deprecated": "warn",
35+
"import/no-extraneous-dependencies": "error",
36+
"import/no-unassigned-import": "warn",
37+
"no-cond-assign": "error",
38+
"no-duplicate-case": "error",
39+
"no-duplicate-imports": "error",
40+
"no-empty": [
41+
"error",
42+
{
43+
allowEmptyCatch: true,
44+
},
45+
],
46+
"no-invalid-this": "error",
47+
"no-new-wrappers": "error",
48+
"no-param-reassign": "error",
49+
"no-redeclare": "error",
50+
"no-sequences": "error",
51+
"no-shadow": [
52+
"error",
53+
{
54+
hoist: "all",
55+
},
56+
],
57+
"no-throw-literal": "error",
58+
"no-unsafe-finally": "error",
59+
"no-unused-labels": "error",
60+
"no-var": "warn",
61+
"no-void": "error",
62+
"prefer-const": "warn",
63+
},
64+
settings: {
65+
jsdoc: {
66+
tagNamePreference: {
67+
returns: "return",
68+
},
69+
},
70+
},
71+
};

sample/functions/.gitignore

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Compiled JavaScript files
2+
**/*.js
3+
**/*.js.map
4+
5+
# Except the ESLint config file
6+
!.eslintrc.js
7+
8+
# TypeScript v1 declaration files
9+
typings/
10+
11+
# Node.js dependency directory
12+
node_modules/

sample/functions/package.json

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"name": "functions",
3+
"scripts": {
4+
"lint": "eslint \"src/**/*\"",
5+
"build": "tsc",
6+
"serve": "npm run build && firebase emulators:start --only functions",
7+
"shell": "npm run build && firebase functions:shell",
8+
"start": "npm run shell",
9+
"deploy": "firebase deploy --only functions",
10+
"logs": "firebase functions:log"
11+
},
12+
"engines": {
13+
"node": "12"
14+
},
15+
"main": "lib/index.js",
16+
"dependencies": {
17+
"firebase-admin": "^9.2.0",
18+
"firebase-functions": "^3.11.0",
19+
"ssr-functions": "file:../dist/sample"
20+
},
21+
"devDependencies": {
22+
"@typescript-eslint/eslint-plugin": "^3.9.1",
23+
"@typescript-eslint/parser": "^3.8.0",
24+
"eslint": "^7.6.0",
25+
"eslint-plugin-import": "^2.22.0",
26+
"typescript": "^3.8.0",
27+
"firebase-functions-test": "^0.2.0"
28+
},
29+
"private": true
30+
}

sample/functions/src/index.ts

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import * as functions from 'firebase-functions';
2+
3+
// // Start writing Firebase Functions
4+
// // https://firebase.google.com/docs/functions/typescript
5+
//
6+
// export const helloWorld = functions.https.onRequest((request, response) => {
7+
// functions.logger.info("Hello logs!", {structuredData: true});
8+
// response.send("Hello from Firebase!");
9+
// });
10+
11+
// @ts-ignore
12+
export const ssr = require('ssr-functions').ssr;
13+
14+
export const yada = functions.https.onCall(() => {
15+
return { time: new Date().getTime() };
16+
});

sample/functions/tsconfig.json

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"compilerOptions": {
3+
"module": "commonjs",
4+
"noImplicitReturns": true,
5+
"noUnusedLocals": true,
6+
"outDir": "lib",
7+
"sourceMap": true,
8+
"strict": true,
9+
"target": "es2017"
10+
},
11+
"compileOnSave": true,
12+
"include": [
13+
"src"
14+
]
15+
}

sample/package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"scripts": {
55
"ng": "ng",
66
"start": "ng serve",
7+
"started:emulated": "concurrently -n ng,firebase -c red,yellow \"ng serve -c emulated\" \"firebase emulators:start\"",
78
"build": "ng build",
89
"test": "ng test",
910
"lint": "ng lint",
@@ -47,14 +48,14 @@
4748
"@nguniversal/builders": "^10.1.0",
4849
"@types/jasmine": "~3.5.0",
4950
"@types/jasminewd2": "~2.0.3",
50-
"@types/node": "^14.11.2",
5151
"codelyzer": "^6.0.0",
52+
"concurrently": "^5.3.0",
5253
"express": "^4.17.1",
5354
"express-serve-static-core": "^0.1.1",
5455
"firebase-admin": "^8.13.0",
5556
"firebase-functions": "^3.11.0",
5657
"firebase-functions-test": "^0.2.2",
57-
"firebase-tools": "^8.11.1",
58+
"firebase-tools": "^8.16.1",
5859
"fuzzy": "^0.1.3",
5960
"inquirer": "^6.2.2",
6061
"inquirer-autocomplete-prompt": "^1.0.1",

sample/src/app/app.module.ts

+18-16
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ import {
1616
} from '@angular/fire/analytics';
1717

1818
import { FirestoreComponent } from './firestore/firestore.component';
19-
import { AngularFireDatabaseModule, URL as DATABASE_URL } from '@angular/fire/database';
20-
import { AngularFirestoreModule, SETTINGS as FIRESTORE_SETTINGS } from '@angular/fire/firestore';
19+
import { AngularFireDatabaseModule, USE_EMULATOR as USE_DATABASE_EMULATOR } from '@angular/fire/database';
20+
import { AngularFirestoreModule, USE_EMULATOR as USE_FIRESTORE_EMULATOR } from '@angular/fire/firestore';
2121
import { AngularFireStorageModule } from '@angular/fire/storage';
22-
import { AngularFireAuthModule } from '@angular/fire/auth';
23-
import { AngularFireMessagingModule } from '@angular/fire/messaging';
24-
import { AngularFireFunctionsModule, ORIGIN as FUNCTIONS_ORIGIN } from '@angular/fire/functions';
22+
import { AngularFireAuthModule, USE_DEVICE_LANGUAGE, USE_EMULATOR as USE_AUTH_EMULATOR } from '@angular/fire/auth';
23+
import { AngularFireMessagingModule, SERVICE_WORKER, VAPID_KEY } from '@angular/fire/messaging';
24+
import { AngularFireFunctionsModule, USE_EMULATOR as USE_FUNCTIONS_EMULATOR, ORIGIN as FUNCTIONS_ORIGIN, NEW_ORIGIN_BEHAVIOR } from '@angular/fire/functions';
2525
import { AngularFireRemoteConfigModule, SETTINGS as REMOTE_CONFIG_SETTINGS, DEFAULTS as REMOTE_CONFIG_DEFAULTS } from '@angular/fire/remote-config';
2626
import { AngularFirePerformanceModule, PerformanceMonitoringService } from '@angular/fire/performance';
2727
import { AngularFireAuthGuardModule } from '@angular/fire/auth-guard';
@@ -31,8 +31,7 @@ import { RemoteConfigComponent } from './remote-config/remote-config.component';
3131
import { HomeComponent } from './home/home.component';
3232
import { AuthComponent } from './auth/auth.component';
3333
import { MessagingComponent } from './messaging/messaging.component';
34-
35-
const shouldUseEmulator = () => false;
34+
import { FunctionsComponent } from './functions/functions.component';
3635

3736
@NgModule({
3837
declarations: [
@@ -43,7 +42,8 @@ const shouldUseEmulator = () => false;
4342
RemoteConfigComponent,
4443
HomeComponent,
4544
AuthComponent,
46-
MessagingComponent
45+
MessagingComponent,
46+
FunctionsComponent,
4747
],
4848
imports: [
4949
BrowserModule.withServerTransition({ appId: 'serverApp' }),
@@ -73,16 +73,18 @@ const shouldUseEmulator = () => false;
7373
useFactory: () => isDevMode()
7474
},
7575
*/
76-
{
77-
provide: DATABASE_URL,
78-
useFactory: () => shouldUseEmulator() ? `http://localhost:9000?ns=${environment.firebase.projectId}` : undefined
79-
},
80-
{ provide: FIRESTORE_SETTINGS, useFactory: () => shouldUseEmulator() ? { host: 'localhost:8080', ssl: false } : {} },
81-
{ provide: FUNCTIONS_ORIGIN, useFactory: () => shouldUseEmulator() ? 'http://localhost:9999' : undefined },
76+
{ provide: USE_AUTH_EMULATOR, useValue: environment.useEmulators ? ['localhost', 9099] : undefined },
77+
{ provide: USE_DATABASE_EMULATOR, useValue: environment.useEmulators ? ['localhost', 9000] : undefined },
78+
{ provide: USE_FIRESTORE_EMULATOR, useValue: environment.useEmulators ? ['localhost', 8080] : undefined },
79+
{ provide: USE_FUNCTIONS_EMULATOR, useValue: environment.useEmulators ? ['localhost', 5001] : undefined },
80+
{ provide: NEW_ORIGIN_BEHAVIOR, useValue: true },
81+
{ provide: FUNCTIONS_ORIGIN, useFactory: () => isDevMode() ? undefined : location.origin },
8282
{ provide: REMOTE_CONFIG_SETTINGS, useFactory: () => isDevMode() ? { minimumFetchIntervalMillis: 10_000 } : {} },
8383
{ provide: REMOTE_CONFIG_DEFAULTS, useValue: { background_color: 'red' } },
84+
{ provide: USE_DEVICE_LANGUAGE, useValue: true },
85+
{ provide: VAPID_KEY, useValue: environment.vapidKey },
86+
{ provide: SERVICE_WORKER, useFactory: () => navigator?.serviceWorker?.getRegistration() ?? undefined },
8487
],
8588
bootstrap: [AppComponent]
8689
})
87-
export class AppModule {
88-
}
90+
export class AppModule { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { FunctionsComponent } from './functions.component';
4+
5+
describe('FunctionsComponent', () => {
6+
let component: FunctionsComponent;
7+
let fixture: ComponentFixture<FunctionsComponent>;
8+
9+
beforeEach(waitForAsync(() => {
10+
TestBed.configureTestingModule({
11+
declarations: [ FunctionsComponent ]
12+
})
13+
.compileComponents();
14+
}));
15+
16+
beforeEach(() => {
17+
fixture = TestBed.createComponent(FunctionsComponent);
18+
component = fixture.componentInstance;
19+
fixture.detectChanges();
20+
});
21+
22+
it('should create', () => {
23+
expect(component).toBeTruthy();
24+
});
25+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Component, OnInit } from '@angular/core';
2+
import { AngularFireFunctions } from '@angular/fire/functions';
3+
import { EMPTY, Observable } from 'rxjs';
4+
5+
@Component({
6+
selector: 'app-functions',
7+
template: `
8+
<p>
9+
Functions!
10+
{{ response$ | async | json }}
11+
<button (click)="request()">Call!</button>
12+
</p>
13+
`,
14+
styles: []
15+
})
16+
export class FunctionsComponent implements OnInit {
17+
18+
response$: Observable<any>;
19+
20+
constructor(public readonly functions: AngularFireFunctions) {
21+
this.response$ = EMPTY;
22+
}
23+
24+
ngOnInit(): void {}
25+
26+
request() {
27+
this.response$ = this.functions.httpsCallable('yada', { timeout: 3 })({});
28+
}
29+
30+
}

0 commit comments

Comments
 (0)