-
Notifications
You must be signed in to change notification settings - Fork 946
Add internal API to collect platform version info #2368
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a few style comments. Also left some comments on the design doc.
// version components. | ||
return providers | ||
.map(provider => { | ||
const service = provider.getImmediate(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const service = provider.getImmediate() as VersionService;
and then you can remove the other casts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
const component = provider.getComponent(); | ||
if (service && component && component.type === ComponentType.VERSION) { | ||
const platformString = | ||
platformLogString[(service as VersionService).library] || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
??
instead of ||
🙂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Waiting for parent branch to merge so I can rebase and upgrade Typescript version to 3.7.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
.map(provider => { | ||
const service = provider.getImmediate(); | ||
const component = provider.getComponent(); | ||
if (service && component && component.type === ComponentType.VERSION) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
service && component?.type === ComponentType.VERSION
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
import { platformLogString } from './constants'; | ||
|
||
export class PlatformLoggerService { | ||
constructor(private container: ComponentContainer) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
readonly
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
packages/app/src/constants.ts
Outdated
@@ -16,3 +16,17 @@ | |||
*/ | |||
|
|||
export const DEFAULT_ENTRY_NAME = '[DEFAULT]'; | |||
|
|||
export const platformLogString: { [key: string]: string } = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it necessary to widen the type? Do we expect this const to be extended or modified?
Also, CONST_CASE
? 🙂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
} | ||
} | ||
|
||
describe('Platform Logger Service Unit Tests', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can nest the two describe
blocks in one main describe
block.
describe('Platform Logger Service', () => {
describe('Unit Tests', () => {
// ...
}
describe('Integration Tests', () => {
// ...
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
return null; | ||
} | ||
}) | ||
.filter((logString: string | null) => logString) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TS should be able to infer this type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type removed.
packages/app/src/constants.ts
Outdated
'remote-config': 'fire-rc', | ||
'storage': 'fire-gcs', | ||
'firestore': 'fire-fst' | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} as const;
to make them readonly
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
return registerComponent( | ||
new Component( | ||
`${library}-version` as Name, | ||
() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
() => new VersionService(library, version),
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
7853fd0
to
1b8607d
Compare
} | ||
registerComponent( | ||
new Component( | ||
`${library}-version` as Name, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a fan of this cast TBH, since it defeats the purpose of the Name
type. Maybe we shouldn't consider versions actual Component
s and instead create a different type for them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that it might be a good idea in the long term to have separate Map in ComponentContainer
in addition to the Providers
Map, and this new Map just stores key/value pairs. Can be used for versions now, and other possible key/value pair data going forward. But that requires a bit of modification to ComponentContainer that might not be a good idea right now as we're planning to do our first release with the migration to the component model next week right before a holiday. Since the actual registerVersion
API wouldn't change we can change the implementation later when the component model is in use and stable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, sounds good.
6caf8ef
to
ae956b1
Compare
b339263
to
d527708
Compare
dcab55e
to
5cb2514
Compare
5cb2514
to
f085e2a
Compare
packages/app/src/firebaseApp.ts
Outdated
this._addComponent( | ||
new Component( | ||
'platform-logger', | ||
(container: ComponentContainer) => new PlatformLoggerService(container), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TS should be able to infer this type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
// a good whitelist system. | ||
const platformString = | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
(PLATFORM_LOG_STRING as any)[service.library] ?? service.library; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of as any
you can do this: PLATFORM_LOG_STRING[service.library as keyof typeof PLATFORM_LOG_STRING]
. It's a bit longer but you wouldn't have to enable any
.
Or in VersionService
you can set the type of library
to that, but that prevents setting arbitrary library versions and I think that was one of the requirements.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
@@ -318,7 +318,6 @@ export class WindowController extends BaseController { | |||
message_time: data[FN_CAMPAIGN_TIME], | |||
message_device_time: Math.floor(Date.now() / 1000) | |||
} | |||
/* eslint-enable camelcase */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But why? :(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Argh, that's a bad merge, I keep putting it back in after merges/rebases but it keeps removing it again. I'll put it back.
packages/app/src/firebaseApp.ts
Outdated
@@ -60,6 +64,28 @@ export class FirebaseAppImpl implements FirebaseApp { | |||
|
|||
// add itself to container | |||
this._addComponent(new Component('app', () => this, ComponentType.PUBLIC)); | |||
this._addComponent( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The component registrations should be moved to index files at the top level, e.g. app/index.ts
, app/index.node.ts
and etc.
The reason FirebaseApp
registration has to happen here is because the container lives within an app instance, but I plan to move the container outside, so FirebaseApp
can register just like the other components.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made a registerPlatformLogger
function for the shared registration code between these files.
@@ -234,6 +236,22 @@ export function createFirebaseNamespaceCore( | |||
: null; | |||
} | |||
|
|||
function registerVersion(library: string, version: string): void { | |||
if (library.match(/\s|\//)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we check version
too? I'd also change the warning message to include version
and to make it clear it is registering a version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be a pretty robust warning message now!
// version components. | ||
return providers | ||
.map(provider => { | ||
const service = provider.getImmediate() as VersionService; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
provider.getImmediate()
will throw in case of placeholder providers. You can call it with optional
flag to make getImmediate()
return null
instead of throwing. Then check if service is null
.
const service = provider.getImmediate({optional: true}) as VersionService;
We can also remove the type cast by using a custom type guard, but it needs a hack:
provider => {
if (isVersionServiceProvider(provider)) {
const service = provider.getImmediate();
// TODO: We can use this check to whitelist strings when/if we set up
// a good whitelist system.
const platformString =
PLATFORM_LOG_STRING[
service.library as keyof typeof PLATFORM_LOG_STRING
] ?? service.library;
return `${platformString}/${service.version}`;
} else {
return null;
}
}
/**
*
* @param provider check if this provider provides a VersionService
*
* NOTE: Using Provider<'app-version'> is a hack to indicate that the provider provides VersionService
* The provider is not necessarily a 'app-version' provider.
*/
function isVersionServiceProvider(provider: Provider<Name>): provider is Provider<'app-version'> {
const component = provider.getComponent();
return component?.type === ComponentType.VERSION;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
packages/app/src/version-service.ts
Outdated
* limitations under the License. | ||
*/ | ||
|
||
export class VersionService { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feel like we can just use a plain Object instead of a class to save an additional file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH classes have very little overhead, but if we're going to use a plain object, could we also export and use a function like this:
function makeVersionService(library: string, service: string) {
return {library, service};
}
It would make things more readable and would be useful in case we want to change the fields later or something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We also type this object with an interface, so it's still type safe.
I remember classes gave us treeshaking problems before (#2006), so I'm a little cautious of using them, but the output you linked doesn't have Object.create
which was the culprit. Maybe Object.create
is being avoided now, or it is only emitted under certain condition?
packages/functions/src/config.ts
Outdated
@@ -48,4 +50,5 @@ export function registerFunctions(instance: _FirebaseNamespace): void { | |||
.setServiceProps(namespaceExports) | |||
.setMultipleInstances(true) | |||
); | |||
instance.registerVersion('functions', version); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you planning to add this to other SDKs in a separate PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, planning to do a separate PR for every approver group.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Just a couple of nits.
) | ||
); | ||
// Register `app` package. | ||
firebase.registerVersion(name, version); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we rename registerPlatformLogger
to registerComponentsAndVersions()
or something else that's not specific to platform logger?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to registerCoreComponents
} | ||
function isVersionServiceProvider( | ||
provider: Provider<Name> | ||
): provider is Provider<'app-version'> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please add a comment that Provider<'app-version'>
is a hack to indicate that it's a version component?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added.
Adds an internal
VersionService
component that each package can create and register to report its own version.Adds an internal
PlatformLoggerService
component initialized by theapp
package, with one method,getPlatformInfoString()
, which collects version info from all version services and returns a string formatted according to Firebase's cross-platformX-firebase-client
header standards. This string can then be sent to the backend as appropriate (to be implemented in future PRs).