From e5ff3ae694a0f3dd46a23ede09daf4ed998b13a6 Mon Sep 17 00:00:00 2001 From: Tri Cao Date: Wed, 22 May 2019 08:44:26 -0400 Subject: [PATCH 01/13] Added AppMetadata type --- src/project-management/AppMetadata.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/project-management/AppMetadata.ts diff --git a/src/project-management/AppMetadata.ts b/src/project-management/AppMetadata.ts new file mode 100644 index 0000000000..2769aa0be0 --- /dev/null +++ b/src/project-management/AppMetadata.ts @@ -0,0 +1,27 @@ +/*! + * Copyright 2019 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class AppMetadata { + public readonly appId: string; + public readonly displayName: string; + public readonly platform: AppPlatform; +} + +export enum AppPlatform { + PLATFORM_UNKNOWN = 'PLATFORM_UNKNOWN', + IOS = 'IOS', + ANDROID = 'ANDROID', +} From 6821a150b82e118bd6cefc7e600a5944b300b876 Mon Sep 17 00:00:00 2001 From: Tri Cao Date: Wed, 22 May 2019 09:06:30 -0400 Subject: [PATCH 02/13] Declare setDisplayName and listAppMetadata (unimplemented) --- src/project-management/project-management.ts | 17 +++++++++++++++++ .../project-management.spec.ts | 14 ++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/project-management/project-management.ts b/src/project-management/project-management.ts index 02123b88a2..0a251c726c 100644 --- a/src/project-management/project-management.ts +++ b/src/project-management/project-management.ts @@ -22,6 +22,7 @@ import * as validator from '../utils/validator'; import { AndroidApp, ShaCertificate } from './android-app'; import { IosApp } from './ios-app'; import { ProjectManagementRequestHandler, assertServerResponse } from './project-management-api-request'; +import { AppMetadata } from './AppMetadata'; /** * Internals of a Project Management instance. @@ -147,6 +148,22 @@ export class ProjectManagement implements FirebaseServiceInterface { }); } + /** + * Lists summary of all apps in the project + */ + public listAppMetadata(): Promise { + throw new FirebaseProjectManagementError( + 'service-unavailable', 'This service is not available'); + } + + /** + * Update display name of the project + */ + public setDisplayName(displayName: string): Promise { + throw new FirebaseProjectManagementError( + 'service-unavailable', 'This service is not available'); + } + /** * Lists up to 100 Firebase apps for a specified platform, associated with this Firebase project. */ diff --git a/test/unit/project-management/project-management.spec.ts b/test/unit/project-management/project-management.spec.ts index 08ef23d867..3898d06cde 100644 --- a/test/unit/project-management/project-management.spec.ts +++ b/test/unit/project-management/project-management.spec.ts @@ -380,5 +380,19 @@ describe('ProjectManagement', () => { return projectManagement.createIosApp(BUNDLE_ID) .should.eventually.deep.equal(createdIosApp); }); + + describe('listAppMetadata', () => { + it('should throw service-unavailable error', () => { + expect(() => projectManagement.listAppMetadata()) + .to.throw('This service is not available'); + }); + }); + + describe('setDisplayName', () => { + it('should throw service-unavailable error', () => { + expect(() => projectManagement.setDisplayName('new project name')) + .to.throw('This service is not available'); + }); + }); }); }); From 6feb2c1e7e09202f6fd59b5611f86414b45eeb38 Mon Sep 17 00:00:00 2001 From: Tri Cao Date: Wed, 22 May 2019 11:46:18 -0400 Subject: [PATCH 03/13] Declare listAppMetadata and setDisplayName methods in Project Management class (#12) * Added AppMetadata type * Declare setDisplayName and listAppMetadata (unimplemented) --- src/project-management/AppMetadata.ts | 27 +++++++++++++++++++ src/project-management/project-management.ts | 17 ++++++++++++ .../project-management.spec.ts | 14 ++++++++++ 3 files changed, 58 insertions(+) create mode 100644 src/project-management/AppMetadata.ts diff --git a/src/project-management/AppMetadata.ts b/src/project-management/AppMetadata.ts new file mode 100644 index 0000000000..2769aa0be0 --- /dev/null +++ b/src/project-management/AppMetadata.ts @@ -0,0 +1,27 @@ +/*! + * Copyright 2019 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class AppMetadata { + public readonly appId: string; + public readonly displayName: string; + public readonly platform: AppPlatform; +} + +export enum AppPlatform { + PLATFORM_UNKNOWN = 'PLATFORM_UNKNOWN', + IOS = 'IOS', + ANDROID = 'ANDROID', +} diff --git a/src/project-management/project-management.ts b/src/project-management/project-management.ts index 02123b88a2..0a251c726c 100644 --- a/src/project-management/project-management.ts +++ b/src/project-management/project-management.ts @@ -22,6 +22,7 @@ import * as validator from '../utils/validator'; import { AndroidApp, ShaCertificate } from './android-app'; import { IosApp } from './ios-app'; import { ProjectManagementRequestHandler, assertServerResponse } from './project-management-api-request'; +import { AppMetadata } from './AppMetadata'; /** * Internals of a Project Management instance. @@ -147,6 +148,22 @@ export class ProjectManagement implements FirebaseServiceInterface { }); } + /** + * Lists summary of all apps in the project + */ + public listAppMetadata(): Promise { + throw new FirebaseProjectManagementError( + 'service-unavailable', 'This service is not available'); + } + + /** + * Update display name of the project + */ + public setDisplayName(displayName: string): Promise { + throw new FirebaseProjectManagementError( + 'service-unavailable', 'This service is not available'); + } + /** * Lists up to 100 Firebase apps for a specified platform, associated with this Firebase project. */ diff --git a/test/unit/project-management/project-management.spec.ts b/test/unit/project-management/project-management.spec.ts index 08ef23d867..3898d06cde 100644 --- a/test/unit/project-management/project-management.spec.ts +++ b/test/unit/project-management/project-management.spec.ts @@ -380,5 +380,19 @@ describe('ProjectManagement', () => { return projectManagement.createIosApp(BUNDLE_ID) .should.eventually.deep.equal(createdIosApp); }); + + describe('listAppMetadata', () => { + it('should throw service-unavailable error', () => { + expect(() => projectManagement.listAppMetadata()) + .to.throw('This service is not available'); + }); + }); + + describe('setDisplayName', () => { + it('should throw service-unavailable error', () => { + expect(() => projectManagement.setDisplayName('new project name')) + .to.throw('This service is not available'); + }); + }); }); }); From 8c103fbf594f01782080d2cca06aa66fcebf7283 Mon Sep 17 00:00:00 2001 From: Tri Cao Date: Wed, 22 May 2019 15:43:40 -0400 Subject: [PATCH 04/13] Update AndroidAppMetadata and IosAppMetadata --- src/index.d.ts | 24 +++++++++++++------ src/project-management/AppMetadata.ts | 14 +++++++++++ src/project-management/android-app.ts | 10 ++------ src/project-management/ios-app.ts | 10 ++------ .../project-management/android-app.spec.ts | 4 +++- test/unit/project-management/ios-app.spec.ts | 4 +++- 6 files changed, 41 insertions(+), 25 deletions(-) diff --git a/src/index.d.ts b/src/index.d.ts index f02bc45c03..f997298815 100755 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -243,7 +243,7 @@ declare namespace admin.auth { callbackURL?: string; enableRequestSigning?: boolean; } - + interface OIDCAuthProviderConfig extends admin.auth.AuthProviderConfig { clientId: string; issuer: string; @@ -761,10 +761,21 @@ declare namespace admin.projectManagement { resourceName?: string; } - interface AndroidAppMetadata { + class AppMetadata { + public appId: string; + public displayName: string; + public platform: AppPlatform; + } + + enum AppPlatform { + PLATFORM_UNKNOWN = 'PLATFORM_UNKNOWN', + IOS = 'IOS', + ANDROID = 'ANDROID', + } + + interface AndroidAppMetadata extends AppMetadata { + platform: AppPlatform.ANDROID; resourceName: string; - appId: string; - displayName: string | null; projectId: string; packageName: string; } @@ -780,10 +791,9 @@ declare namespace admin.projectManagement { getConfig(): Promise; } - interface IosAppMetadata { + interface IosAppMetadata extends AppMetadata { + platform: AppPlatform.IOS; resourceName: string; - appId: string; - displayName: string; projectId: string; bundleId: string; } diff --git a/src/project-management/AppMetadata.ts b/src/project-management/AppMetadata.ts index 2769aa0be0..9ace2dc8fd 100644 --- a/src/project-management/AppMetadata.ts +++ b/src/project-management/AppMetadata.ts @@ -25,3 +25,17 @@ export enum AppPlatform { IOS = 'IOS', ANDROID = 'ANDROID', } + +export interface AndroidAppMetadata extends AppMetadata { + readonly platform: AppPlatform.ANDROID; + readonly resourceName: string; + readonly projectId: string; + readonly packageName: string; +} + +export interface IosAppMetadata extends AppMetadata { + readonly platform: AppPlatform.IOS; + readonly resourceName: string; + readonly projectId: string; + readonly bundleId: string; +} diff --git a/src/project-management/android-app.ts b/src/project-management/android-app.ts index 0a9eeb0771..521b3e9037 100644 --- a/src/project-management/android-app.ts +++ b/src/project-management/android-app.ts @@ -17,6 +17,7 @@ import { FirebaseProjectManagementError } from '../utils/error'; import * as validator from '../utils/validator'; import { ProjectManagementRequestHandler, assertServerResponse } from './project-management-api-request'; +import { AndroidAppMetadata, AppPlatform } from './AppMetadata'; export class AndroidApp { private readonly resourceName: string; @@ -49,6 +50,7 @@ export class AndroidApp { }); const metadata: AndroidAppMetadata = { + platform: AppPlatform.ANDROID, resourceName: responseData.name, appId: responseData.appId, displayName: responseData.displayName || null, @@ -127,14 +129,6 @@ export class AndroidApp { } } -export interface AndroidAppMetadata { - readonly resourceName: string; - readonly appId: string; - readonly displayName: string | null; - readonly projectId: string; - readonly packageName: string; -} - export class ShaCertificate { public readonly certType: ('sha1' | 'sha256'); diff --git a/src/project-management/ios-app.ts b/src/project-management/ios-app.ts index 35af3a3a17..95c50e065d 100644 --- a/src/project-management/ios-app.ts +++ b/src/project-management/ios-app.ts @@ -17,6 +17,7 @@ import { FirebaseProjectManagementError } from '../utils/error'; import * as validator from '../utils/validator'; import { ProjectManagementRequestHandler, assertServerResponse } from './project-management-api-request'; +import { IosAppMetadata, AppPlatform } from './AppMetadata'; export class IosApp { private readonly resourceName: string; @@ -49,6 +50,7 @@ export class IosApp { }); const metadata: IosAppMetadata = { + platform: AppPlatform.IOS, resourceName: responseData.name, appId: responseData.appId, displayName: responseData.displayName || null, @@ -85,11 +87,3 @@ export class IosApp { }); } } - -export interface IosAppMetadata { - readonly resourceName: string; - readonly appId: string; - readonly displayName: string; - readonly projectId: string; - readonly bundleId: string; -} diff --git a/test/unit/project-management/android-app.spec.ts b/test/unit/project-management/android-app.spec.ts index b6022ffcbc..794f996fb7 100644 --- a/test/unit/project-management/android-app.spec.ts +++ b/test/unit/project-management/android-app.spec.ts @@ -20,11 +20,12 @@ import * as chai from 'chai'; import * as _ from 'lodash'; import * as sinon from 'sinon'; import { FirebaseApp } from '../../../src/firebase-app'; -import { AndroidApp, AndroidAppMetadata, ShaCertificate } from '../../../src/project-management/android-app'; +import { AndroidApp, ShaCertificate } from '../../../src/project-management/android-app'; import { ProjectManagementRequestHandler } from '../../../src/project-management/project-management-api-request'; import { deepCopy } from '../../../src/utils/deep-copy'; import { FirebaseProjectManagementError } from '../../../src/utils/error'; import * as mocks from '../../resources/mocks'; +import { AndroidAppMetadata, AppPlatform } from '../../../src/project-management/AppMetadata'; const expect = chai.expect; @@ -89,6 +90,7 @@ describe('AndroidApp', () => { }; const VALID_ANDROID_APP_METADATA: AndroidAppMetadata = { + platform: AppPlatform.ANDROID, resourceName: VALID_ANDROID_APP_METADATA_API_RESPONSE.name, appId: APP_ID, displayName: VALID_ANDROID_APP_METADATA_API_RESPONSE.displayName, diff --git a/test/unit/project-management/ios-app.spec.ts b/test/unit/project-management/ios-app.spec.ts index 2aaa343dd9..48a7de09dc 100644 --- a/test/unit/project-management/ios-app.spec.ts +++ b/test/unit/project-management/ios-app.spec.ts @@ -20,11 +20,12 @@ import * as chai from 'chai'; import * as _ from 'lodash'; import * as sinon from 'sinon'; import { FirebaseApp } from '../../../src/firebase-app'; -import { IosApp, IosAppMetadata } from '../../../src/project-management/ios-app'; +import { IosApp } from '../../../src/project-management/ios-app'; import { ProjectManagementRequestHandler } from '../../../src/project-management/project-management-api-request'; import { deepCopy } from '../../../src/utils/deep-copy'; import { FirebaseProjectManagementError } from '../../../src/utils/error'; import * as mocks from '../../resources/mocks'; +import { IosAppMetadata, AppPlatform } from '../../../src/project-management/AppMetadata'; const expect = chai.expect; @@ -88,6 +89,7 @@ describe('IosApp', () => { }; const VALID_IOS_APP_METADATA: IosAppMetadata = { + platform: AppPlatform.IOS, resourceName: VALID_IOS_APP_METADATA_API_RESPONSE.name, appId: APP_ID, displayName: VALID_IOS_APP_METADATA_API_RESPONSE.displayName, From 78944dc2a63562ce53a60f40e254ce19cc652b4c Mon Sep 17 00:00:00 2001 From: Tri Cao Date: Thu, 23 May 2019 08:40:08 -0400 Subject: [PATCH 05/13] Change AppMetadata to interface and displayName to be optional --- src/index.d.ts | 8 ++++---- src/project-management/AppMetadata.ts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/index.d.ts b/src/index.d.ts index f997298815..68f37980ce 100755 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -761,10 +761,10 @@ declare namespace admin.projectManagement { resourceName?: string; } - class AppMetadata { - public appId: string; - public displayName: string; - public platform: AppPlatform; + interface AppMetadata { + appId: string; + displayName?: string; + platform: AppPlatform; } enum AppPlatform { diff --git a/src/project-management/AppMetadata.ts b/src/project-management/AppMetadata.ts index 9ace2dc8fd..611833c876 100644 --- a/src/project-management/AppMetadata.ts +++ b/src/project-management/AppMetadata.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -export class AppMetadata { - public readonly appId: string; - public readonly displayName: string; - public readonly platform: AppPlatform; +export interface AppMetadata { + readonly appId: string; + readonly displayName: string; + readonly platform: AppPlatform; } export enum AppPlatform { From 21470b5dc0f9f251a53f3e651b8655845d527c25 Mon Sep 17 00:00:00 2001 From: Tri Cao Date: Thu, 23 May 2019 11:35:29 -0400 Subject: [PATCH 06/13] listAppMetadata: implement api request --- .../project-management-api-request.ts | 12 ++++++ .../project-management-api-request.spec.ts | 43 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/project-management/project-management-api-request.ts b/src/project-management/project-management-api-request.ts index 253dedd957..abbdc9d656 100644 --- a/src/project-management/project-management-api-request.ts +++ b/src/project-management/project-management-api-request.ts @@ -139,6 +139,18 @@ export class ProjectManagementRequestHandler { 'v1beta1'); } + /** + * @param {string} parentResourceName Fully-qualified resource name of the project whose iOS apps + * you want to list. + */ + public listAppMetadata(parentResourceName: string): Promise { + return this.invokeRequestHandler( + 'GET', + `${parentResourceName}:searchApps?page_size=${LIST_APPS_MAX_PAGE_SIZE}`, + /* requestData */ null, + 'v1beta1'); + } + /** * @param {string} parentResourceName Fully-qualified resource name of the project that you want * to create the Android app within. diff --git a/test/unit/project-management/project-management-api-request.spec.ts b/test/unit/project-management/project-management-api-request.spec.ts index 594c0b53dd..d97d857976 100644 --- a/test/unit/project-management/project-management-api-request.spec.ts +++ b/test/unit/project-management/project-management-api-request.spec.ts @@ -27,6 +27,7 @@ import { HttpClient } from '../../../src/utils/api-request'; import * as mocks from '../../resources/mocks'; import * as utils from '../utils'; import { ShaCertificate } from '../../../src/project-management/android-app'; +import { AppPlatform } from '../../../src/project-management/AppMetadata'; chai.should(); chai.use(sinonChai); @@ -41,11 +42,15 @@ describe('ProjectManagementRequestHandler', () => { const PORT = 443; const PROJECT_RESOURCE_NAME: string = 'projects/test-project-id'; const APP_ID: string = 'test-app-id'; + const APP_ID_ANDROID: string = 'test-android-app-id'; + const APP_ID_IOS: string = 'test-ios-app-id'; const ANDROID_APP_RESOURCE_NAME: string = `projects/-/androidApp/${APP_ID}`; const IOS_APP_RESOURCE_NAME: string = `projects/-/iosApp/${APP_ID}`; const PACKAGE_NAME: string = 'test-package-name'; const BUNDLE_ID: string = 'test-bundle-id'; const DISPLAY_NAME: string = 'test-display-name'; + const DISPLAY_NAME_ANDROID: string = 'test-display-name-android'; + const DISPLAY_NAME_IOS: string = 'test-display-name-ios'; const OPERATION_RESOURCE_NAME: string = 'test-operation-resource-name'; const mockAccessToken: string = utils.generateRandomAccessToken(); @@ -183,6 +188,44 @@ describe('ProjectManagementRequestHandler', () => { }); }); + describe('listAppMetadata', () => { + testHttpErrors(() => requestHandler.listAppMetadata(PROJECT_RESOURCE_NAME)); + + it('should succeed', () => { + const expectedResult = { + apps: [ + { + appId: APP_ID_ANDROID, + displayName: DISPLAY_NAME_ANDROID, + platform: AppPlatform.ANDROID, + }, + { + appId: APP_ID_IOS, + displayName: DISPLAY_NAME_IOS, + platform: AppPlatform.IOS, + }], + }; + + const stub = sinon.stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom(expectedResult)); + stubs.push(stub); + + const url = + `https://${HOST}:${PORT}/v1beta1/${PROJECT_RESOURCE_NAME}:searchApps?page_size=100`; + return requestHandler.listAppMetadata(PROJECT_RESOURCE_NAME) + .then((result) => { + expect(result).to.deep.equal(expectedResult); + expect(stub).to.have.been.calledOnce.and.calledWith({ + method: 'GET', + url, + data: null, + headers: expectedHeaders, + timeout: 10000, + }); + }); + }); + }); + describe('createAndroidApp', () => { testHttpErrors(() => requestHandler.createAndroidApp(PROJECT_RESOURCE_NAME, PACKAGE_NAME)); From fcc14dcee575b267c702c28d46dc0ff4fbfc38c2 Mon Sep 17 00:00:00 2001 From: Tri Cao Date: Fri, 24 May 2019 08:41:23 -0400 Subject: [PATCH 07/13] listAppMetadata: implement listAppMetadata in ProjectManagement class (#16) * listAppMetadata: implement listAppMetadata in ProjectManagement class * Clean up code to address comments --- src/project-management/project-management.ts | 54 ++++++-- .../project-management.spec.ts | 122 ++++++++++++++++-- 2 files changed, 154 insertions(+), 22 deletions(-) diff --git a/src/project-management/project-management.ts b/src/project-management/project-management.ts index 0a251c726c..dc1e73d77f 100644 --- a/src/project-management/project-management.ts +++ b/src/project-management/project-management.ts @@ -22,7 +22,7 @@ import * as validator from '../utils/validator'; import { AndroidApp, ShaCertificate } from './android-app'; import { IosApp } from './ios-app'; import { ProjectManagementRequestHandler, assertServerResponse } from './project-management-api-request'; -import { AppMetadata } from './AppMetadata'; +import { AppMetadata, AppPlatform } from './AppMetadata'; /** * Internals of a Project Management instance. @@ -152,8 +152,8 @@ export class ProjectManagement implements FirebaseServiceInterface { * Lists summary of all apps in the project */ public listAppMetadata(): Promise { - throw new FirebaseProjectManagementError( - 'service-unavailable', 'This service is not available'); + return this.requestHandler.listAppMetadata(this.resourceName) + .then((responseData) => this.transformResponseToAppMetadata(responseData)); } /** @@ -164,6 +164,30 @@ export class ProjectManagement implements FirebaseServiceInterface { 'service-unavailable', 'This service is not available'); } + private transformResponseToAppMetadata(responseData: any): AppMetadata[] { + this.assertListAppsResponseData(responseData, 'listAppMetadata()'); + + if (!responseData.apps) { + return []; + } + + return responseData.apps.map((appJson: any) => { + assertServerResponse( + validator.isNonEmptyString(appJson.appId), + responseData, + `"apps[].appId" field must be present in the listAppMetadata() response data.`); + assertServerResponse( + this.validateAppPlatform(appJson.platform), + responseData, + `"apps[].platform" field must be one of [${Object.keys(AppPlatform).join(', ')}].`); + return appJson as AppMetadata; + }); + } + + private validateAppPlatform(platform: any): boolean { + return platform && (AppPlatform[platform] !== undefined); + } + /** * Lists up to 100 Firebase apps for a specified platform, associated with this Firebase project. */ @@ -174,20 +198,12 @@ export class ProjectManagement implements FirebaseServiceInterface { return listPromise .then((responseData: any) => { - assertServerResponse( - validator.isNonNullObject(responseData), - responseData, - `${callerName}\'s responseData must be a non-null object.`); + this.assertListAppsResponseData(responseData, callerName); if (!responseData.apps) { return []; } - assertServerResponse( - validator.isArray(responseData.apps), - responseData, - `"apps" field must be present in the ${callerName} response data.`); - return responseData.apps.map((appJson: any) => { assertServerResponse( validator.isNonEmptyString(appJson.appId), @@ -201,4 +217,18 @@ export class ProjectManagement implements FirebaseServiceInterface { }); }); } + + private assertListAppsResponseData(responseData: any, callerName: string): void { + assertServerResponse( + validator.isNonNullObject(responseData), + responseData, + `${callerName}\'s responseData must be a non-null object.`); + + if (responseData.apps) { + assertServerResponse( + validator.isArray(responseData.apps), + responseData, + `"apps" field must be present in the ${callerName} response data.`); + } + } } diff --git a/test/unit/project-management/project-management.spec.ts b/test/unit/project-management/project-management.spec.ts index 3898d06cde..1c530a0fb3 100644 --- a/test/unit/project-management/project-management.spec.ts +++ b/test/unit/project-management/project-management.spec.ts @@ -26,12 +26,17 @@ import { ProjectManagementRequestHandler } from '../../../src/project-management import { FirebaseProjectManagementError } from '../../../src/utils/error'; import * as mocks from '../../resources/mocks'; import { IosApp } from '../../../src/project-management/ios-app'; +import { AppPlatform, AppMetadata } from '../../../src/project-management/AppMetadata'; const expect = chai.expect; const APP_ID = 'test-app-id'; +const APP_ID_ANDROID: string = 'test-app-id-android-'; +const APP_ID_IOS: string = 'test-app-id-ios'; const PACKAGE_NAME = 'test-package-name'; const BUNDLE_ID = 'test-bundle-id'; +const DISPLAY_NAME_ANDROID: string = 'test-display-name-android'; +const DISPLAY_NAME_IOS: string = 'test-display-name-ios'; const EXPECTED_ERROR = new FirebaseProjectManagementError('internal-error', 'message'); const VALID_SHA_256_HASH = '0123456789abcdefABCDEF01234567890123456701234567890123456789abcd'; @@ -380,19 +385,116 @@ describe('ProjectManagement', () => { return projectManagement.createIosApp(BUNDLE_ID) .should.eventually.deep.equal(createdIosApp); }); + }); - describe('listAppMetadata', () => { - it('should throw service-unavailable error', () => { - expect(() => projectManagement.listAppMetadata()) - .to.throw('This service is not available'); - }); + describe('listAppMetadata', () => { + const VALID_LIST_APP_METADATA_API_RESPONSE = { + apps: [ + { appId: APP_ID_ANDROID, displayName: DISPLAY_NAME_ANDROID, platform: AppPlatform.ANDROID}, + { appId: APP_ID_IOS, displayName: DISPLAY_NAME_IOS, platform: AppPlatform.IOS}, + ], + }; + + it('should propagate API errors', () => { + const stub = sinon + .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') + .returns(Promise.reject(EXPECTED_ERROR)); + stubs.push(stub); + return projectManagement.listAppMetadata() + .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); }); - describe('setDisplayName', () => { - it('should throw service-unavailable error', () => { - expect(() => projectManagement.setDisplayName('new project name')) - .to.throw('This service is not available'); - }); + it('should throw with null API response', () => { + const stub = sinon + .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') + .returns(Promise.resolve(null)); + stubs.push(stub); + return projectManagement.listAppMetadata() + .should.eventually.be.rejected + .and.have.property( + 'message', + 'listAppMetadata()\'s responseData must be a non-null object. Response data: null'); + }); + + it('should return empty array when API response missing "apps" field', () => { + const partialApiResponse = {}; + + const stub = sinon + .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') + .returns(Promise.resolve(partialApiResponse)); + stubs.push(stub); + return projectManagement.listAppMetadata() + .should.eventually.deep.equal([]); + }); + + it('should throw when API response has non-array "apps" field', () => { + const partialApiResponse = { apps: 'none' }; + + const stub = sinon + .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') + .returns(Promise.resolve(partialApiResponse)); + stubs.push(stub); + return projectManagement.listAppMetadata() + .should.eventually.be.rejected + .and.have.property( + 'message', + '"apps" field must be present in the listAppMetadata() response data. Response data: ' + + JSON.stringify(partialApiResponse, null, 2)); + }); + + it('should throw with API response missing "apps[].appId" field', () => { + const partialApiResponse = { + apps: [{}], + }; + + const stub = sinon + .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') + .returns(Promise.resolve(partialApiResponse)); + stubs.push(stub); + return projectManagement.listAppMetadata() + .should.eventually.be.rejected + .and.have.property( + 'message', + '"apps[].appId" field must be present in the listAppMetadata() response data. ' + + `Response data: ${JSON.stringify(partialApiResponse, null, 2)}`); + }); + + it('should throw with API response with invalid "apps[].platform" field', () => { + const invalidPlatformApiResponse = { + apps: [{ + appId: APP_ID, + platform: 'INVALID', + }], + }; + + const stub = sinon + .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') + .returns(Promise.resolve(invalidPlatformApiResponse)); + stubs.push(stub); + return projectManagement.listAppMetadata() + .should.eventually.be.rejected + .and.have.property( + 'message', + '"apps[].platform" field must be one of [PLATFORM_UNKNOWN, IOS, ANDROID]. ' + + `Response data: ${JSON.stringify(invalidPlatformApiResponse, null, 2)}`); + }); + + it('should resolve with list of apps metadata on success', () => { + const validAppMetadata: AppMetadata[] = VALID_LIST_APP_METADATA_API_RESPONSE.apps; + + const stub = sinon + .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') + .returns(Promise.resolve(VALID_LIST_APP_METADATA_API_RESPONSE)); + stubs.push(stub); + return projectManagement.listAppMetadata() + .should.eventually.deep.equal(validAppMetadata); + }); + }); + + describe('setDisplayName', () => { + it('should throw service-unavailable error', () => { + expect(() => projectManagement.setDisplayName('new project name')) + .to.throw('This service is not available'); }); }); }); From 1655c19fc669d9f6c9dc55e75b188652323e9f1e Mon Sep 17 00:00:00 2001 From: Tri Cao Date: Fri, 24 May 2019 15:39:42 -0400 Subject: [PATCH 08/13] Fix: non ios/android apps should be listed as PLATFORM_UNKNOWN (#18) --- src/project-management/project-management.ts | 9 ++---- .../project-management.spec.ts | 31 +++++++++++++++---- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/project-management/project-management.ts b/src/project-management/project-management.ts index dc1e73d77f..87c6666b0c 100644 --- a/src/project-management/project-management.ts +++ b/src/project-management/project-management.ts @@ -177,17 +177,14 @@ export class ProjectManagement implements FirebaseServiceInterface { responseData, `"apps[].appId" field must be present in the listAppMetadata() response data.`); assertServerResponse( - this.validateAppPlatform(appJson.platform), + validator.isNonEmptyString(appJson.platform), responseData, - `"apps[].platform" field must be one of [${Object.keys(AppPlatform).join(', ')}].`); + `"apps[].platform" field must be present in the listAppMetadata() response data.`); + appJson.platform = AppPlatform[appJson.platform] || AppPlatform.PLATFORM_UNKNOWN; return appJson as AppMetadata; }); } - private validateAppPlatform(platform: any): boolean { - return platform && (AppPlatform[platform] !== undefined); - } - /** * Lists up to 100 Firebase apps for a specified platform, associated with this Firebase project. */ diff --git a/test/unit/project-management/project-management.spec.ts b/test/unit/project-management/project-management.spec.ts index 1c530a0fb3..2f3a8ca1bd 100644 --- a/test/unit/project-management/project-management.spec.ts +++ b/test/unit/project-management/project-management.spec.ts @@ -459,24 +459,23 @@ describe('ProjectManagement', () => { + `Response data: ${JSON.stringify(partialApiResponse, null, 2)}`); }); - it('should throw with API response with invalid "apps[].platform" field', () => { - const invalidPlatformApiResponse = { + it('should throw with API response missing "apps[].platform" field', () => { + const missingPlatformApiResponse = { apps: [{ appId: APP_ID, - platform: 'INVALID', }], }; const stub = sinon .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') - .returns(Promise.resolve(invalidPlatformApiResponse)); + .returns(Promise.resolve(missingPlatformApiResponse)); stubs.push(stub); return projectManagement.listAppMetadata() .should.eventually.be.rejected .and.have.property( 'message', - '"apps[].platform" field must be one of [PLATFORM_UNKNOWN, IOS, ANDROID]. ' - + `Response data: ${JSON.stringify(invalidPlatformApiResponse, null, 2)}`); + '"apps[].platform" field must be present in the listAppMetadata() response data. ' + + `Response data: ${JSON.stringify(missingPlatformApiResponse, null, 2)}`); }); it('should resolve with list of apps metadata on success', () => { @@ -489,6 +488,26 @@ describe('ProjectManagement', () => { return projectManagement.listAppMetadata() .should.eventually.deep.equal(validAppMetadata); }); + + it('should resolve with "apps[].platform" to be "PLATFORM_UNKNOWN" for web app', () => { + const webPlatformApiResponse = { + apps: [{ + appId: APP_ID, + platform: 'WEB', + }], + }; + const expectedAppMetadata: AppMetadata[] = [{ + appId: APP_ID, + platform: AppPlatform.PLATFORM_UNKNOWN, + }]; + + const stub = sinon + .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') + .returns(Promise.resolve(webPlatformApiResponse)); + stubs.push(stub); + return projectManagement.listAppMetadata() + .should.eventually.deep.equal(expectedAppMetadata); + }); }); describe('setDisplayName', () => { From 9815634a468085aa6359cc10f4b698ba407a0f7b Mon Sep 17 00:00:00 2001 From: Tri Cao Date: Mon, 27 May 2019 12:53:07 -0400 Subject: [PATCH 09/13] Add integration test for ProjectManagement.listAppMetadata() (#17) * Add integration test * Fix listAppMetadata integration test --- src/index.d.ts | 1 + src/project-management/AppMetadata.ts | 2 +- test/integration/project-management.spec.ts | 20 +++++++++++++++++++ .../project-management.spec.ts | 2 +- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/index.d.ts b/src/index.d.ts index 68f37980ce..5ffa9f99fd 100755 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -811,6 +811,7 @@ declare namespace admin.projectManagement { listAndroidApps(): Promise; listIosApps(): Promise; + listAppMetadata(): Promise; androidApp(appId: string): admin.projectManagement.AndroidApp; iosApp(appId: string): admin.projectManagement.IosApp; shaCertificate(shaHash: string): admin.projectManagement.ShaCertificate; diff --git a/src/project-management/AppMetadata.ts b/src/project-management/AppMetadata.ts index 611833c876..21107d48be 100644 --- a/src/project-management/AppMetadata.ts +++ b/src/project-management/AppMetadata.ts @@ -16,7 +16,7 @@ export interface AppMetadata { readonly appId: string; - readonly displayName: string; + readonly displayName?: string; readonly platform: AppPlatform; } diff --git a/test/integration/project-management.spec.ts b/test/integration/project-management.spec.ts index e470e51153..1caa2ad261 100644 --- a/test/integration/project-management.spec.ts +++ b/test/integration/project-management.spec.ts @@ -75,6 +75,19 @@ describe('admin.projectManagement', () => { }); }); + describe('listAppMetadata()', () => { + it('successfully lists metadata of all apps', () => { + return admin.projectManagement().listAppMetadata() + .then((metadatas) => { + expect(metadatas.length).to.be.at.least(2); + const testAppMetadatas = metadatas.filter((metadata) => + isIntegrationTestAppDisplayName(metadata.displayName) && + (metadata.appId === androidApp.appId || metadata.appId === iosApp.appId)); + expect(testAppMetadatas).to.have.length(2); + }); + }); + }); + describe('androidApp.getMetadata()', () => { it('successfully sets Android app\'s display name', () => { return androidApp.getMetadata().then((appMetadata) => { @@ -241,6 +254,13 @@ function isIntegrationTestApp(appNamespace: string): boolean { return (appNamespace.indexOf(APP_NAMESPACE_PREFIX) > -1); } +/** + * @return {boolean} True if the specified appDisplayName belongs to these integration tests. + */ +function isIntegrationTestAppDisplayName(appDisplayName: string): boolean { + return (appDisplayName.indexOf(APP_DISPLAY_NAME_PREFIX) > -1); +} + /** * @return {string} A randomly generated alphanumeric string, of the specified length. */ diff --git a/test/unit/project-management/project-management.spec.ts b/test/unit/project-management/project-management.spec.ts index 2f3a8ca1bd..56fd2a1c84 100644 --- a/test/unit/project-management/project-management.spec.ts +++ b/test/unit/project-management/project-management.spec.ts @@ -31,7 +31,7 @@ import { AppPlatform, AppMetadata } from '../../../src/project-management/AppMet const expect = chai.expect; const APP_ID = 'test-app-id'; -const APP_ID_ANDROID: string = 'test-app-id-android-'; +const APP_ID_ANDROID: string = 'test-app-id-android'; const APP_ID_IOS: string = 'test-app-id-ios'; const PACKAGE_NAME = 'test-package-name'; const BUNDLE_ID = 'test-bundle-id'; From 4b8c97f35f94434b54457b288a974163b42addb0 Mon Sep 17 00:00:00 2001 From: Tri Cao Date: Mon, 27 May 2019 13:22:20 -0400 Subject: [PATCH 10/13] Implement setDisplayName() for ProjectManagement class (#19) * Implement setDisplayName() for ProjectManagement class * fix integration test to address comment * Enable promise support for integration test --- src/index.d.ts | 1 + src/project-management/project-management.ts | 5 ++--- test/integration/project-management.spec.ts | 22 +++++++++++++++++++ .../project-management.spec.ts | 18 ++++++++++++--- 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/index.d.ts b/src/index.d.ts index 5ffa9f99fd..f1797fe1e5 100755 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -813,6 +813,7 @@ declare namespace admin.projectManagement { listIosApps(): Promise; listAppMetadata(): Promise; androidApp(appId: string): admin.projectManagement.AndroidApp; + setDisplayName(newDisplayName: string): Promise; iosApp(appId: string): admin.projectManagement.IosApp; shaCertificate(shaHash: string): admin.projectManagement.ShaCertificate; createAndroidApp( diff --git a/src/project-management/project-management.ts b/src/project-management/project-management.ts index 87c6666b0c..0094de2f20 100644 --- a/src/project-management/project-management.ts +++ b/src/project-management/project-management.ts @@ -159,9 +159,8 @@ export class ProjectManagement implements FirebaseServiceInterface { /** * Update display name of the project */ - public setDisplayName(displayName: string): Promise { - throw new FirebaseProjectManagementError( - 'service-unavailable', 'This service is not available'); + public setDisplayName(newDisplayName: string): Promise { + return this.requestHandler.setDisplayName(this.resourceName, newDisplayName); } private transformResponseToAppMetadata(responseData: any): AppMetadata[] { diff --git a/test/integration/project-management.spec.ts b/test/integration/project-management.spec.ts index 1caa2ad261..0691cf4f2b 100644 --- a/test/integration/project-management.spec.ts +++ b/test/integration/project-management.spec.ts @@ -16,6 +16,7 @@ import * as _ from 'lodash'; import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; import * as admin from '../../lib/index'; import { projectId } from './setup'; @@ -23,12 +24,17 @@ const APP_NAMESPACE_PREFIX = 'com.adminsdkintegrationtest.a'; const APP_NAMESPACE_SUFFIX_LENGTH = 15; const APP_DISPLAY_NAME_PREFIX = 'Created By Firebase AdminSDK Nodejs Integration Testing '; +const PROJECT_DISPLAY_NAME_PREFIX = 'Nodejs AdminSDK Testing '; const APP_DISPLAY_NAME_SUFFIX_LENGTH = 15; +const PROJECT_DISPLAY_NAME_SUFFIX_LENGTH = 6; const SHA_256_HASH = 'aaaaccccaaaaccccaaaaccccaaaaccccaaaaccccaaaaccccaaaaccccaaaacccc'; const expect = chai.expect; +chai.should(); +chai.use(chaiAsPromised); + describe('admin.projectManagement', () => { let androidApp: admin.projectManagement.AndroidApp; @@ -75,6 +81,15 @@ describe('admin.projectManagement', () => { }); }); + describe('setDisplayName()', () => { + it('successfully set project\'s display name', () => { + const newDisplayName = generateUniqueProjectDisplayName(); + // TODO(caot): verify that project name has been renamed successfully + return admin.projectManagement().setDisplayName(newDisplayName) + .should.eventually.be.fulfilled; + }); + }); + describe('listAppMetadata()', () => { it('successfully lists metadata of all apps', () => { return admin.projectManagement().listAppMetadata() @@ -247,6 +262,13 @@ function generateUniqueAppDisplayName() { return APP_DISPLAY_NAME_PREFIX + generateRandomString(APP_DISPLAY_NAME_SUFFIX_LENGTH); } +/** + * @return {string} string that can be used as a unique project display name. + */ +function generateUniqueProjectDisplayName() { + return PROJECT_DISPLAY_NAME_PREFIX + generateRandomString(PROJECT_DISPLAY_NAME_SUFFIX_LENGTH); +} + /** * @return {boolean} True if the specified appNamespace belongs to these integration tests. */ diff --git a/test/unit/project-management/project-management.spec.ts b/test/unit/project-management/project-management.spec.ts index 56fd2a1c84..2036d4f462 100644 --- a/test/unit/project-management/project-management.spec.ts +++ b/test/unit/project-management/project-management.spec.ts @@ -511,9 +511,21 @@ describe('ProjectManagement', () => { }); describe('setDisplayName', () => { - it('should throw service-unavailable error', () => { - expect(() => projectManagement.setDisplayName('new project name')) - .to.throw('This service is not available'); + it('should propagate API errors', () => { + const stub = sinon + .stub(ProjectManagementRequestHandler.prototype, 'setDisplayName') + .returns(Promise.reject(EXPECTED_ERROR)); + stubs.push(stub); + return projectManagement.setDisplayName(DISPLAY_NAME_ANDROID) + .should.eventually.be.rejected.and.equal(EXPECTED_ERROR); + }); + + it('should resolve on success', () => { + const stub = sinon + .stub(ProjectManagementRequestHandler.prototype, 'setDisplayName') + .returns(Promise.resolve()); + stubs.push(stub); + return projectManagement.setDisplayName(DISPLAY_NAME_ANDROID).should.eventually.be.fulfilled; }); }); }); From fc99af250cfec181cce3f63eaf018856c8377d3d Mon Sep 17 00:00:00 2001 From: Tri Cao Date: Fri, 31 May 2019 12:08:08 -0400 Subject: [PATCH 11/13] Fix listAppMetadata to match the update on AppMetatdata interface --- src/project-management/project-management.ts | 14 ++++- test/integration/project-management.spec.ts | 2 +- .../project-management-api-request.spec.ts | 2 +- .../project-management.spec.ts | 57 ++++++++++++------- 4 files changed, 51 insertions(+), 24 deletions(-) diff --git a/src/project-management/project-management.ts b/src/project-management/project-management.ts index ecc01e6e49..2b11812563 100644 --- a/src/project-management/project-management.ts +++ b/src/project-management/project-management.ts @@ -24,6 +24,11 @@ import { IosApp } from './ios-app'; import { ProjectManagementRequestHandler, assertServerResponse } from './project-management-api-request'; import { AppMetadata, AppPlatform } from './app-metadata'; +const APP_PLATFORM_MAP: {[key: string]: AppPlatform} = { + ANDROID: AppPlatform.ANDROID, + IOS: AppPlatform.IOS, +}; + /** * Internals of a Project Management instance. */ @@ -179,8 +184,13 @@ export class ProjectManagement implements FirebaseServiceInterface { validator.isNonEmptyString(appJson.platform), responseData, `"apps[].platform" field must be present in the listAppMetadata() response data.`); - appJson.platform = AppPlatform[appJson.platform] || AppPlatform.PLATFORM_UNKNOWN; - return appJson as AppMetadata; + return { + appId: appJson.appId, + displayName: appJson.displayName, + platform: APP_PLATFORM_MAP[appJson.platform] || AppPlatform.PLATFORM_UNKNOWN, + projectId: this.projectId, + resourceName: appJson.name, + }; }); } diff --git a/test/integration/project-management.spec.ts b/test/integration/project-management.spec.ts index 0691cf4f2b..0f13120c76 100644 --- a/test/integration/project-management.spec.ts +++ b/test/integration/project-management.spec.ts @@ -280,7 +280,7 @@ function isIntegrationTestApp(appNamespace: string): boolean { * @return {boolean} True if the specified appDisplayName belongs to these integration tests. */ function isIntegrationTestAppDisplayName(appDisplayName: string): boolean { - return (appDisplayName.indexOf(APP_DISPLAY_NAME_PREFIX) > -1); + return appDisplayName && (appDisplayName.indexOf(APP_DISPLAY_NAME_PREFIX) > -1); } /** diff --git a/test/unit/project-management/project-management-api-request.spec.ts b/test/unit/project-management/project-management-api-request.spec.ts index d97d857976..9b9e6ec0c4 100644 --- a/test/unit/project-management/project-management-api-request.spec.ts +++ b/test/unit/project-management/project-management-api-request.spec.ts @@ -27,7 +27,7 @@ import { HttpClient } from '../../../src/utils/api-request'; import * as mocks from '../../resources/mocks'; import * as utils from '../utils'; import { ShaCertificate } from '../../../src/project-management/android-app'; -import { AppPlatform } from '../../../src/project-management/AppMetadata'; +import { AppPlatform } from '../../../src/project-management/app-metadata'; chai.should(); chai.use(sinonChai); diff --git a/test/unit/project-management/project-management.spec.ts b/test/unit/project-management/project-management.spec.ts index 7b6547bdee..7da92a2363 100644 --- a/test/unit/project-management/project-management.spec.ts +++ b/test/unit/project-management/project-management.spec.ts @@ -26,7 +26,7 @@ import { ProjectManagementRequestHandler } from '../../../src/project-management import { FirebaseProjectManagementError } from '../../../src/utils/error'; import * as mocks from '../../resources/mocks'; import { IosApp } from '../../../src/project-management/ios-app'; -import { AppPlatform, AppMetadata } from '../../../src/project-management/AppMetadata'; +import { AppPlatform, AppMetadata } from '../../../src/project-management/app-metadata'; const expect = chai.expect; @@ -38,6 +38,9 @@ const BUNDLE_ID = 'test-bundle-id'; const DISPLAY_NAME_ANDROID: string = 'test-display-name-android'; const DISPLAY_NAME_IOS: string = 'test-display-name-ios'; const EXPECTED_ERROR = new FirebaseProjectManagementError('internal-error', 'message'); +const RESOURCE_NAME = 'projects/test/resources-name'; +const RESOURCE_NAME_ANDROID = 'projects/test/resources-name:android'; +const RESOURCE_NAME_IOS = 'projects/test/resources-name:ios'; const VALID_SHA_256_HASH = '0123456789abcdefABCDEF01234567890123456701234567890123456789abcd'; @@ -385,27 +388,23 @@ describe('ProjectManagement', () => { return projectManagement.createIosApp(BUNDLE_ID) .should.eventually.deep.equal(createdIosApp); }); - - describe('listAppMetadata', () => { - it('should throw service-unavailable error', () => { - expect(() => projectManagement.listAppMetadata()) - .to.throw('This service is not available'); - }); - }); - - describe('setDisplayName', () => { - it('should throw service-unavailable error', () => { - expect(() => projectManagement.setDisplayName('new project name')) - .to.throw('This service is not available'); - }); - }); }); describe('listAppMetadata', () => { const VALID_LIST_APP_METADATA_API_RESPONSE = { apps: [ - { appId: APP_ID_ANDROID, displayName: DISPLAY_NAME_ANDROID, platform: AppPlatform.ANDROID}, - { appId: APP_ID_IOS, displayName: DISPLAY_NAME_IOS, platform: AppPlatform.IOS}, + { + appId: APP_ID_ANDROID, + displayName: DISPLAY_NAME_ANDROID, + platform: 'ANDROID', + name: RESOURCE_NAME_ANDROID, + }, + { + appId: APP_ID_IOS, + displayName: DISPLAY_NAME_IOS, + platform: 'IOS', + name: RESOURCE_NAME_IOS, + }, ], }; @@ -493,14 +492,28 @@ describe('ProjectManagement', () => { }); it('should resolve with list of apps metadata on success', () => { - const validAppMetadata: AppMetadata[] = VALID_LIST_APP_METADATA_API_RESPONSE.apps; - + const expectedAppMetadata: AppMetadata[] = [ + { + appId: VALID_LIST_APP_METADATA_API_RESPONSE.apps[0].appId, + displayName: VALID_LIST_APP_METADATA_API_RESPONSE.apps[0].displayName, + platform: AppPlatform.ANDROID, + projectId: mocks.projectId, + resourceName: RESOURCE_NAME_ANDROID, + }, + { + appId: VALID_LIST_APP_METADATA_API_RESPONSE.apps[1].appId, + displayName: VALID_LIST_APP_METADATA_API_RESPONSE.apps[1].displayName, + platform: AppPlatform.IOS, + projectId: mocks.projectId, + resourceName: RESOURCE_NAME_IOS, + }, + ]; const stub = sinon .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata') .returns(Promise.resolve(VALID_LIST_APP_METADATA_API_RESPONSE)); stubs.push(stub); return projectManagement.listAppMetadata() - .should.eventually.deep.equal(validAppMetadata); + .should.eventually.deep.equal(expectedAppMetadata); }); it('should resolve with "apps[].platform" to be "PLATFORM_UNKNOWN" for web app', () => { @@ -508,11 +521,15 @@ describe('ProjectManagement', () => { apps: [{ appId: APP_ID, platform: 'WEB', + name: RESOURCE_NAME, }], }; const expectedAppMetadata: AppMetadata[] = [{ appId: APP_ID, + displayName: undefined, platform: AppPlatform.PLATFORM_UNKNOWN, + projectId: mocks.projectId, + resourceName: RESOURCE_NAME, }]; const stub = sinon From 7adff027db569e509b1eadd69299ec74cb09892d Mon Sep 17 00:00:00 2001 From: Tri Cao Date: Mon, 3 Jun 2019 16:10:12 -0400 Subject: [PATCH 12/13] Handle nullable displayName and enum string mapping issues --- src/project-management/app-metadata.ts | 18 +++++++++--------- src/project-management/project-management.ts | 14 ++++++-------- .../project-management.spec.ts | 1 - 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/project-management/app-metadata.ts b/src/project-management/app-metadata.ts index 4e7ff71bce..7f55551d53 100644 --- a/src/project-management/app-metadata.ts +++ b/src/project-management/app-metadata.ts @@ -21,19 +21,19 @@ export enum AppPlatform { } export interface AppMetadata { - readonly appId: string; - readonly displayName?: string; - readonly platform: AppPlatform; - readonly projectId: string; - readonly resourceName: string; + appId: string; + displayName?: string; + platform: AppPlatform; + projectId: string; + resourceName: string; } export interface AndroidAppMetadata extends AppMetadata { - readonly platform: AppPlatform.ANDROID; - readonly packageName: string; + platform: AppPlatform.ANDROID; + packageName: string; } export interface IosAppMetadata extends AppMetadata { - readonly platform: AppPlatform.IOS; - readonly bundleId: string; + platform: AppPlatform.IOS; + bundleId: string; } diff --git a/src/project-management/project-management.ts b/src/project-management/project-management.ts index 2b11812563..37840d7dee 100644 --- a/src/project-management/project-management.ts +++ b/src/project-management/project-management.ts @@ -24,11 +24,6 @@ import { IosApp } from './ios-app'; import { ProjectManagementRequestHandler, assertServerResponse } from './project-management-api-request'; import { AppMetadata, AppPlatform } from './app-metadata'; -const APP_PLATFORM_MAP: {[key: string]: AppPlatform} = { - ANDROID: AppPlatform.ANDROID, - IOS: AppPlatform.IOS, -}; - /** * Internals of a Project Management instance. */ @@ -184,13 +179,16 @@ export class ProjectManagement implements FirebaseServiceInterface { validator.isNonEmptyString(appJson.platform), responseData, `"apps[].platform" field must be present in the listAppMetadata() response data.`); - return { + const metadata: AppMetadata = { appId: appJson.appId, - displayName: appJson.displayName, - platform: APP_PLATFORM_MAP[appJson.platform] || AppPlatform.PLATFORM_UNKNOWN, + platform: (AppPlatform as any)[appJson.platform] || AppPlatform.PLATFORM_UNKNOWN, projectId: this.projectId, resourceName: appJson.name, }; + if (appJson.displayName) { + metadata.displayName = appJson.displayName; + } + return metadata; }); } diff --git a/test/unit/project-management/project-management.spec.ts b/test/unit/project-management/project-management.spec.ts index 7da92a2363..c18b174e83 100644 --- a/test/unit/project-management/project-management.spec.ts +++ b/test/unit/project-management/project-management.spec.ts @@ -526,7 +526,6 @@ describe('ProjectManagement', () => { }; const expectedAppMetadata: AppMetadata[] = [{ appId: APP_ID, - displayName: undefined, platform: AppPlatform.PLATFORM_UNKNOWN, projectId: mocks.projectId, resourceName: RESOURCE_NAME, From f325f767921bd6157ba2544952e84526f66a07d5 Mon Sep 17 00:00:00 2001 From: Tri Cao Date: Tue, 4 Jun 2019 08:52:08 -0400 Subject: [PATCH 13/13] Update test for null display name --- .../project-management/project-management.spec.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/unit/project-management/project-management.spec.ts b/test/unit/project-management/project-management.spec.ts index c18b174e83..9a7de91a2d 100644 --- a/test/unit/project-management/project-management.spec.ts +++ b/test/unit/project-management/project-management.spec.ts @@ -405,6 +405,11 @@ describe('ProjectManagement', () => { platform: 'IOS', name: RESOURCE_NAME_IOS, }, + { + appId: APP_ID, + platform: 'WEB', + name: RESOURCE_NAME, + }, ], }; @@ -507,6 +512,12 @@ describe('ProjectManagement', () => { projectId: mocks.projectId, resourceName: RESOURCE_NAME_IOS, }, + { + appId: VALID_LIST_APP_METADATA_API_RESPONSE.apps[2].appId, + platform: AppPlatform.PLATFORM_UNKNOWN, + projectId: mocks.projectId, + resourceName: RESOURCE_NAME, + }, ]; const stub = sinon .stub(ProjectManagementRequestHandler.prototype, 'listAppMetadata')