Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/firebase-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {FirestoreService} from './firestore/firestore';
import {InstanceId} from './instance-id/instance-id';
import {ProjectManagement} from './project-management/project-management';
import {SecurityRules} from './security-rules/security-rules';
import { RemoteConfig } from './remote-config/remote-config';

import {Agent} from 'http';

Expand Down Expand Up @@ -380,6 +381,18 @@ export class FirebaseApp {
});
}

/**
* Returns the RemoteConfig service instance associated with this app.
*
* @return {RemoteConfig} The RemoteConfig service instance of this app.
*/
public remoteConfig(): RemoteConfig {
return this.ensureService_('remoteConfig', () => {
const remoteConfigService: typeof RemoteConfig = require('./remote-config/remote-config').RemoteConfig;
return new remoteConfigService(this);
});
}

/**
* Returns the name of the FirebaseApp instance.
*
Expand Down
13 changes: 13 additions & 0 deletions src/firebase-namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {Firestore} from '@google-cloud/firestore';
import {InstanceId} from './instance-id/instance-id';
import {ProjectManagement} from './project-management/project-management';
import { SecurityRules } from './security-rules/security-rules';
import { RemoteConfig } from './remote-config/remote-config';

import * as validator from './utils/validator';

Expand Down Expand Up @@ -435,6 +436,18 @@ export class FirebaseNamespace {
return Object.assign(fn, {SecurityRules: securityRules});
}

/**
* Gets the `RemoteConfig` service namespace. The returned namespace can be used to get the
* `RemoteConfig` service for the default app or an explicitly specified app.
*/
get remoteConfig(): FirebaseServiceNamespace<RemoteConfig> {
const fn: FirebaseServiceNamespace<RemoteConfig> = (app?: FirebaseApp) => {
return this.ensureApp(app).remoteConfig();
};
const remoteConfig = require('./remote-config/remote-config').RemoteConfig;
return Object.assign(fn, { RemoteConfig: remoteConfig });
}

/**
* Initializes the FirebaseApp instance.
*
Expand Down
181 changes: 181 additions & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,37 @@ declare namespace admin {
*/
function projectManagement(app?: admin.app.App): admin.projectManagement.ProjectManagement;

/**
* Gets the {@link admin.remoteConfig.RemoteConfig `RemoteConfig`} service for the
* default app or a given app.
*
* `admin.remoteConfig()` can be called with no arguments to access the default
* app's {@link admin.remoteConfig.RemoteConfig `RemoteConfig`} service or as
* `admin.remoteConfig(app)` to access the
* {@link admin.remoteConfig.RemoteConfig `RemoteConfig`} service associated with a
* specific app.
*
* @example
* ```javascript
* // Get the `RemoteConfig` service for the default app
* var defaultRemoteConfig = admin.remoteConfig();
* ```
*
* @example
* ```javascript
* // Get the `RemoteConfig` service for a given app
* var otherRemoteConfig = admin.remoteConfig(otherApp);
* ```
*
* @param app Optional app for which to return the `RemoteConfig` service.
* If not provided, the default `RemoteConfig` service is returned.
*
* @return The default `RemoteConfig` service if no
* app is provided, or the `RemoteConfig` service associated with the provided
* app.
*/
function remoteConfig(app?: admin.app.App): admin.remoteConfig.RemoteConfig;

/**
* Gets the {@link admin.securityRules.SecurityRules
* `SecurityRules`} service for the default app or a given app.
Expand Down Expand Up @@ -457,6 +488,7 @@ declare namespace admin.app {
instanceId(): admin.instanceId.InstanceId;
messaging(): admin.messaging.Messaging;
projectManagement(): admin.projectManagement.ProjectManagement;
remoteConfig(): admin.remoteConfig.RemoteConfig;
securityRules(): admin.securityRules.SecurityRules;
storage(): admin.storage.Storage;

Expand Down Expand Up @@ -5611,6 +5643,155 @@ declare namespace admin.projectManagement {
}
}

declare namespace admin.remoteConfig {

/**
* Interface representing a Remote Config template.
*/
interface RemoteConfigTemplate {
/**
* A list of conditions in descending order by priority.
*/
conditions: RemoteConfigCondition[];

/**
* Map of parameter keys to their optional default values and optional conditional values.
*/
parameters: { [key: string]: RemoteConfigParameter };

/**
* ETag of the current Remote Config template (readonly).
*/
readonly etag: string;
}

/**
* Interface representing a Remote Config parameter.
* At minimum, a `defaultValue` or a `conditionalValues` entry must be present for the
* parameter to have any effect.
*/
interface RemoteConfigParameter {

/**
* The value to set the parameter to, when none of the named conditions evaluate to `true`.
*/
defaultValue?: RemoteConfigParameterValue;

/**
* A `(condition name, value)` map. The condition name of the highest priority
* (the one listed first in the Remote Config template's conditions list) determines the value of
* this parameter.
*/
conditionalValues?: { [key: string]: RemoteConfigParameterValue };

/**
* A description for this parameter. Should not be over 100 characters and may contain any
* Unicode characters.
*/
description?: string;
}

/**
* Interface representing a Remote Config condition.
* A condition targets a specific group of users. A list of these conditions make up
* part of a Remote Config template.
*/
interface RemoteConfigCondition {

/**
* A non-empty and unique name of this condition.
*/
name: string;

/**
* The logic of this condition.
* See the documentation on
* {@link https://firebase.google.com/docs/remote-config/condition-reference condition expressions}
* for the expected syntax of this field.
*/
expression: string;

/**
* The color associated with this condition for display purposes in the Firebase Console.
* Not specifying this value results in the console picking an arbitrary color to associate
* with the condition.
*/
tagColor?: string;
}

/**
* Interface representing an explicit parameter value.
*/
interface ExplicitParameterValue {
/**
* The `string` value that the parameter is set to.
*/
value: string;
}

/**
* Interface representing an in-app-default value.
*/
interface InAppDefaultValue {
/**
* If `true`, the parameter is omitted from the parameter values returned to a client.
*/
useInAppDefault: boolean;
}

/**
* Type representing a Remote Config parameter value.
* A `RemoteConfigParameterValue` could be either an `ExplicitParameterValue` or
* an `InAppDefaultValue`.
*/
type RemoteConfigParameterValue = ExplicitParameterValue | InAppDefaultValue;

/**
* The Firebase `RemoteConfig` service interface.
*
* Do not call this constructor directly. Instead, use
* [`admin.remoteConfig()`](admin.remoteConfig#remoteConfig).
*/
interface RemoteConfig {
app: admin.app.App;

/**
* Gets the current active version of the {@link admin.remoteConfig.RemoteConfigTemplate
* `RemoteConfigTemplate`} of the project.
*
* @return A promise that fulfills with a `RemoteConfigTemplate`.
*/
getTemplate(): Promise<RemoteConfigTemplate>;

/**
* Validates a {@link admin.remoteConfig.RemoteConfigTemplate `RemoteConfigTemplate`}.
*
* @param template The Remote Config template to be validated.
* @returns A promise that fulfills with the validated `RemoteConfigTemplate`.
*/
validateTemplate(template: RemoteConfigTemplate): Promise<RemoteConfigTemplate>;

/**
* Publishes a Remote Config template.
*
* @param template The Remote Config template to be published.
* @param options Optional options object when publishing a Remote Config template.
*
* @return A Promise that fulfills with the published `RemoteConfigTemplate`.
*/
publishTemplate(template: RemoteConfigTemplate, options?: { force: boolean }): Promise<RemoteConfigTemplate>;

/**
* Creates and returns a new Remote Config template from a JSON string.
*
* @param json The JSON string to populate a Remote Config template.
*
* @return A new template instance.
*/
createTemplateFromJSON(json: string): RemoteConfigTemplate;
}
}

declare namespace admin.securityRules {
/**
* A source file containing some Firebase security rules. The content includes raw
Expand Down
29 changes: 28 additions & 1 deletion test/unit/firebase-app.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {InstanceId} from '../../src/instance-id/instance-id';
import {ProjectManagement} from '../../src/project-management/project-management';
import { SecurityRules } from '../../src/security-rules/security-rules';
import { FirebaseAppError, AppErrorCodes } from '../../src/utils/error';
import { RemoteConfig } from '../../src/remote-config/remote-config';

chai.should();
chai.use(sinonChai);
Expand Down Expand Up @@ -496,7 +497,7 @@ describe('FirebaseApp', () => {
expect(gcsNamespace).not.be.null;
});

it('should return a cached version of Messaging on subsequent calls', () => {
it('should return a cached version of Storage on subsequent calls', () => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed a typo. I can move this to a separate PR if we prefer that :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks 👍

const app = firebaseNamespace.initializeApp(mocks.appOptions, mocks.appName);
const serviceNamespace1: Storage = app.storage();
const serviceNamespace2: Storage = app.storage();
Expand Down Expand Up @@ -608,6 +609,32 @@ describe('FirebaseApp', () => {
});
});

describe('remoteConfig()', () => {
it('should throw if the app has already been deleted', () => {
const app = firebaseNamespace.initializeApp(mocks.appOptions, mocks.appName);

return app.delete().then(() => {
expect(() => {
return app.remoteConfig();
}).to.throw(`Firebase app named "${mocks.appName}" has already been deleted.`);
});
});

it('should return the RemoteConfig client', () => {
const app = firebaseNamespace.initializeApp(mocks.appOptions, mocks.appName);

const remoteConfig: RemoteConfig = app.remoteConfig();
expect(remoteConfig).to.not.be.null;
});

it('should return a cached version of RemoteConfig on subsequent calls', () => {
const app = firebaseNamespace.initializeApp(mocks.appOptions, mocks.appName);
const service1: RemoteConfig = app.remoteConfig();
const service2: RemoteConfig = app.remoteConfig();
expect(service1).to.equal(service2);
});
});

describe('#[service]()', () => {
it('should throw if the app has already been deleted', () => {
firebaseNamespace.INTERNAL.registerService(mocks.serviceName, mockServiceFactory);
Expand Down
32 changes: 32 additions & 0 deletions test/unit/firebase-namespace.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import {
import {InstanceId} from '../../src/instance-id/instance-id';
import {ProjectManagement} from '../../src/project-management/project-management';
import { SecurityRules } from '../../src/security-rules/security-rules';
import { RemoteConfig } from '../../src/remote-config/remote-config';

chai.should();
chai.use(sinonChai);
Expand Down Expand Up @@ -659,4 +660,35 @@ describe('FirebaseNamespace', () => {
.to.be.deep.equal(SecurityRules);
});
});

describe('#remoteConfig()', () => {
it('should throw when called before initializing an app', () => {
expect(() => {
firebaseNamespace.remoteConfig();
}).to.throw(DEFAULT_APP_NOT_FOUND);
});

it('should throw when default app is not initialized', () => {
firebaseNamespace.initializeApp(mocks.appOptions, 'testApp');
expect(() => {
firebaseNamespace.remoteConfig();
}).to.throw(DEFAULT_APP_NOT_FOUND);
});

it('should return a valid namespace when the default app is initialized', () => {
const app: FirebaseApp = firebaseNamespace.initializeApp(mocks.appOptions);
const rc: RemoteConfig = firebaseNamespace.remoteConfig();
expect(rc.app).to.be.deep.equal(app);
});

it('should return a valid namespace when the named app is initialized', () => {
const app: FirebaseApp = firebaseNamespace.initializeApp(mocks.appOptions, 'testApp');
const rc: RemoteConfig = firebaseNamespace.remoteConfig(app);
expect(rc.app).to.be.deep.equal(app);
});

it('should return a reference to RemoteConfig type', () => {
expect(firebaseNamespace.remoteConfig.RemoteConfig).to.be.deep.equal(RemoteConfig);
});
});
});
15 changes: 15 additions & 0 deletions test/unit/firebase.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,21 @@ describe('Firebase', () => {
});
});

describe('#remoteConfig', () => {
it('should throw if the app has not be initialized', () => {
expect(() => {
return firebaseAdmin.remoteConfig();
}).to.throw('The default Firebase app does not exist.');
});

it('should return the remoteConfig service', () => {
firebaseAdmin.initializeApp(mocks.appOptions);
expect(() => {
return firebaseAdmin.remoteConfig();
}).not.to.throw();
});
});

describe('#storage', () => {
it('should throw if the app has not be initialized', () => {
expect(() => {
Expand Down