Skip to content

Commit 5cb2514

Browse files
Feiyang1hsubox76
authored andcommitted
Add platform version logging functionality.
1 parent 57a0f2b commit 5cb2514

23 files changed

+314
-7
lines changed

config/tsconfig.base.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
],
1818
"module": "ES2015",
1919
"moduleResolution": "node",
20+
"resolveJsonModule": true,
2021
"sourceMap": true,
2122
"target": "es5",
2223
"typeRoots": [

packages/analytics/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import {
2828
} from '@firebase/component';
2929
import { ERROR_FACTORY, AnalyticsError } from './src/errors';
3030

31+
import { version } from './package.json';
32+
3133
declare global {
3234
interface Window {
3335
[key: string]: unknown;
@@ -62,6 +64,8 @@ export function registerAnalytics(instance: _FirebaseNamespace): void {
6264
new Component('analytics-internal', internalFactory, ComponentType.PRIVATE)
6365
);
6466

67+
instance.registerVersion('ANALYTICS_TYPE', version);
68+
6569
function internalFactory(
6670
container: ComponentContainer
6771
): FirebaseAnalyticsInternal {

packages/analytics/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"rollup-plugin-commonjs": "10.1.0",
3636
"rollup-plugin-json": "4.0.0",
3737
"rollup-plugin-node-resolve": "5.2.0",
38+
"rollup-plugin-typescript2": "0.25.2",
3839
"rollup-plugin-uglify": "6.0.3",
3940
"typescript": "3.7.2"
4041
},

packages/app-types/index.d.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,26 @@ export interface FirebaseNamespace {
9898
*/
9999
apps: FirebaseApp[];
100100

101+
/**
102+
* Registers a library's name and version for platform logging purposes.
103+
* @param library Name of 1p or 3p library (e.g. firestore, angularfire)
104+
* @param version Current version of that library.
105+
*/
106+
registerVersion(library: string, version: string): void;
107+
101108
// The current SDK version.
102109
SDK_VERSION: string;
103110
}
104111

112+
export interface VersionService {
113+
library: string;
114+
version: string;
115+
}
116+
105117
declare module '@firebase/component' {
106118
interface NameServiceMapping {
107119
'app': FirebaseApp;
120+
'app-version': VersionService;
121+
'platform-identifier': VersionService;
108122
}
109123
}

packages/app-types/private.d.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ export interface FirebaseServiceFactory {
5656
): FirebaseService;
5757
}
5858

59+
export interface PlatformLoggerService {
60+
getPlatformInfoString(): string;
61+
}
62+
5963
/**
6064
* All ServiceNamespaces extend from FirebaseServiceNamespace
6165
*/
@@ -154,3 +158,9 @@ export interface _FirebaseNamespace extends FirebaseNamespace {
154158
ErrorFactory: typeof ErrorFactory;
155159
};
156160
}
161+
162+
declare module '@firebase/component' {
163+
interface NameServiceMapping {
164+
'platform-logger': PlatformLoggerService;
165+
}
166+
}

packages/app/src/constants.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,23 @@
1616
*/
1717

1818
export const DEFAULT_ENTRY_NAME = '[DEFAULT]';
19+
20+
export const PLATFORM_LOG_STRING = {
21+
'app': 'fire-core',
22+
'analytics': 'fire-analytics',
23+
'auth': 'fire-auth',
24+
'database': 'fire-rtdb',
25+
'functions': 'fire-fn',
26+
'messaging': 'fire-fcm',
27+
'performance': 'fire-perf',
28+
'remote-config': 'fire-rc',
29+
'storage': 'fire-gcs',
30+
'firestore': 'fire-fst',
31+
'fire-js': 'fire-js', // Platform identifier for JS SDK.
32+
'fire-js-all-app': 'fire-js-all-app', // firebase/app import
33+
'fire-js-all': 'fire-js-all', // 'firebase' import
34+
'fire-js-all-node': 'fire-js-all-node',
35+
'fire-js-all-rn': 'fire-js-all-rn',
36+
'fire-js-all-lite': 'fire-js-all-lite',
37+
'fire-js-all-cdn': 'fire-js-all-cdn'
38+
} as const;

packages/app/src/firebaseApp.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ import {
3535
import { AppError, ERROR_FACTORY } from './errors';
3636
import { DEFAULT_ENTRY_NAME } from './constants';
3737
import { logger } from './logger';
38+
import { PlatformLoggerService } from './platformLoggerService';
39+
import { VersionService } from './version-service';
40+
41+
import { version } from '../package.json';
3842

3943
/**
4044
* Global context object for a collection of services using
@@ -60,6 +64,28 @@ export class FirebaseAppImpl implements FirebaseApp {
6064

6165
// add itself to container
6266
this._addComponent(new Component('app', () => this, ComponentType.PUBLIC));
67+
this._addComponent(
68+
new Component(
69+
'platform-logger',
70+
(container: ComponentContainer) => new PlatformLoggerService(container),
71+
ComponentType.PRIVATE
72+
)
73+
);
74+
this._addComponent(
75+
new Component(
76+
'app-version',
77+
() => new VersionService('app', version),
78+
ComponentType.VERSION
79+
)
80+
);
81+
this._addComponent(
82+
new Component(
83+
'platform-identifier',
84+
// Adds a string identifying version header as belonging to JS SDK
85+
() => new VersionService('fire-js', ''),
86+
ComponentType.VERSION
87+
)
88+
);
6389
// populate ComponentContainer with existing components
6490
for (const component of this.firebase_.INTERNAL.components.values()) {
6591
this._addComponent(component);

packages/app/src/firebaseNamespaceCore.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ import { FirebaseAppLiteImpl } from './lite/firebaseAppLite';
3434
import { DEFAULT_ENTRY_NAME } from './constants';
3535
import { version } from '../../firebase/package.json';
3636
import { logger } from './logger';
37-
import { Component, ComponentType } from '@firebase/component';
37+
import { Component, ComponentType, Name } from '@firebase/component';
38+
import { VersionService } from './version-service';
3839

3940
/**
4041
* Because auth can't share code with other components, we attach the utility functions
@@ -59,6 +60,7 @@ export function createFirebaseNamespaceCore(
5960
initializeApp,
6061
// @ts-ignore
6162
app,
63+
registerVersion,
6264
// @ts-ignore
6365
apps: null,
6466
SDK_VERSION: version,
@@ -234,6 +236,22 @@ export function createFirebaseNamespaceCore(
234236
: null;
235237
}
236238

239+
function registerVersion(library: string, version: string): void {
240+
if (library.match(/\s|\//)) {
241+
logger.warn(
242+
`Could not register ${library}: it contains illegal characters.`
243+
);
244+
return;
245+
}
246+
registerComponent(
247+
new Component(
248+
`${library}-version` as Name,
249+
() => new VersionService(library, version),
250+
ComponentType.VERSION
251+
)
252+
);
253+
}
254+
237255
// Map the requested service to a registered service name
238256
// (used to map auth to serverAuth service when needed).
239257
function useAsService(app: FirebaseApp, name: string): string | null {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* @license
3+
* Copyright 2019 Google Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { ComponentContainer, ComponentType } from '@firebase/component';
19+
import { VersionService } from './version-service';
20+
import { PLATFORM_LOG_STRING } from './constants';
21+
22+
export class PlatformLoggerService {
23+
constructor(private readonly container: ComponentContainer) {}
24+
// In initial implementation, this will be called by installations on
25+
// auth token refresh, and installations will send this string.
26+
getPlatformInfoString(): string {
27+
const providers = this.container.getProviders();
28+
// Loop through providers and get library/version pairs from any that are
29+
// version components.
30+
return providers
31+
.map(provider => {
32+
const service = provider.getImmediate() as VersionService;
33+
const component = provider.getComponent();
34+
if (service && component?.type === ComponentType.VERSION) {
35+
// TODO: We can use this check to whitelist strings when/if we set up
36+
// a good whitelist system.
37+
const platformString =
38+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
39+
(PLATFORM_LOG_STRING as any)[service.library] ?? service.library;
40+
return `${platformString}/${service.version}`;
41+
} else {
42+
return null;
43+
}
44+
})
45+
.filter(logString => logString)
46+
.join(' ');
47+
}
48+
}

packages/app/src/version-service.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* @license
3+
* Copyright 2019 Google Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
export class VersionService {
19+
constructor(readonly library: string, readonly version: string) {}
20+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/**
2+
* @license
3+
* Copyright 2017 Google Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { FirebaseNamespace } from '@firebase/app-types';
19+
import { _FirebaseApp, _FirebaseNamespace } from '@firebase/app-types/private';
20+
import { createFirebaseNamespace } from '../src/firebaseNamespace';
21+
import { expect } from 'chai';
22+
import './setup';
23+
import { PlatformLoggerService } from '../src/platformLoggerService';
24+
import {
25+
Component,
26+
ComponentType,
27+
ComponentContainer
28+
} from '@firebase/component';
29+
import { VersionService } from '../src/version-service';
30+
31+
declare module '@firebase/component' {
32+
interface NameServiceMapping {
33+
'vs1': VersionService;
34+
'vs2': VersionService;
35+
'test-shell': Promise<void>;
36+
}
37+
}
38+
39+
describe('Platform Logger Service', () => {
40+
describe('Unit Tests', () => {
41+
it(`logs core version`, () => {
42+
const container = new ComponentContainer('testContainer');
43+
container.addComponent(
44+
new Component(
45+
'vs1',
46+
() => new VersionService('vs1', '1.2.3'),
47+
ComponentType.VERSION
48+
)
49+
);
50+
container.addComponent(
51+
new Component(
52+
'vs2',
53+
() => new VersionService('vs2', '3.02.01'),
54+
ComponentType.VERSION
55+
)
56+
);
57+
const platformLoggerService = new PlatformLoggerService(container);
58+
const platformInfoString = platformLoggerService.getPlatformInfoString();
59+
expect(platformInfoString).to.include('vs1/1.2.3');
60+
expect(platformInfoString).to.include('vs2/3.02.01');
61+
});
62+
});
63+
64+
describe('Integration Tests', () => {
65+
let firebase: FirebaseNamespace;
66+
67+
beforeEach(() => {
68+
firebase = createFirebaseNamespace();
69+
});
70+
71+
it(`logs core version`, () => {
72+
firebase.initializeApp({});
73+
(firebase as _FirebaseNamespace).INTERNAL.registerComponent(
74+
new Component(
75+
'test-shell',
76+
async (container: ComponentContainer) => {
77+
const platformLoggerProvider = container.getProvider(
78+
'platform-logger'
79+
);
80+
const platformLogger = (await platformLoggerProvider.get()) as PlatformLoggerService;
81+
const platformInfoString = platformLogger.getPlatformInfoString();
82+
expect(platformInfoString).to.include('fire-core/');
83+
expect(platformInfoString).to.include('fire-js/ ');
84+
},
85+
ComponentType.PUBLIC
86+
)
87+
);
88+
(firebase as any)['test-shell']();
89+
});
90+
91+
it(`logs other components' versions`, () => {
92+
firebase.initializeApp({});
93+
(firebase as _FirebaseNamespace).registerVersion('analytics', '1.2.3');
94+
(firebase as _FirebaseNamespace).INTERNAL.registerComponent(
95+
new Component(
96+
'test-shell',
97+
async (container: ComponentContainer) => {
98+
const platformLoggerProvider = container.getProvider(
99+
'platform-logger'
100+
);
101+
const platformLogger = (await platformLoggerProvider.get()) as PlatformLoggerService;
102+
const platformInfoString = platformLogger.getPlatformInfoString();
103+
expect(platformInfoString).to.include('fire-analytics/1.2.3');
104+
expect(platformInfoString).to.include('fire-js/ ');
105+
},
106+
ComponentType.PUBLIC
107+
)
108+
);
109+
(firebase as any)['test-shell']();
110+
});
111+
});
112+
});

packages/component/src/provider.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ export class Provider<T extends Name> {
100100
}
101101
throw Error(`Service ${this.name} is not available`);
102102
}
103-
104103
return instance;
105104
} catch (e) {
106105
if (optional) {
@@ -111,6 +110,10 @@ export class Provider<T extends Name> {
111110
}
112111
}
113112

113+
getComponent(): Component<T> | null {
114+
return this.component;
115+
}
116+
114117
setComponent(component: Component<T>): void {
115118
if (component.name !== this.name) {
116119
throw Error(

packages/component/src/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ export const enum InstantiationMode {
3232
*/
3333
export const enum ComponentType {
3434
PUBLIC = 'PUBLIC',
35-
PRIVATE = 'PRIVATE'
35+
PRIVATE = 'PRIVATE',
36+
VERSION = 'VERSION'
3637
}
3738

3839
/**

0 commit comments

Comments
 (0)