Skip to content

Allow security-rules to auto-generate typings, separate internal vs external APIs #974

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

Merged
merged 9 commits into from
Aug 6, 2020
2 changes: 1 addition & 1 deletion gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ var paths = {
curatedTypings: [
'src/*.d.ts',
'!src/instance-id.d.ts',
'!src/security-rules.d.ts',
'!src/project-management.d.ts'
],
};
Expand All @@ -70,7 +71,6 @@ const TEMPORARY_TYPING_EXCLUDES = [
'!lib/machine-learning/*.d.ts',
'!lib/messaging/*.d.ts',
'!lib/remote-config/*.d.ts',
'!lib/security-rules/*.d.ts',
'!lib/storage/*.d.ts',
'!lib/utils/*.d.ts'
];
Expand Down
47 changes: 47 additions & 0 deletions src/security-rules/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*!
* Copyright 2020 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.
*/

import { FirebaseApp } from '../firebase-app';
import * as securityRulesApi from './security-rules';
import * as firebaseAdmin from '../index';

export function securityRules(app?: FirebaseApp): securityRulesApi.SecurityRules {
if (typeof(app) === 'undefined') {
app = firebaseAdmin.app();
}
return app.securityRules();
}

/**
* We must define a namespace to make the typings work correctly. Otherwise
* `admin.securityRules()` cannot be called like a function. Temporarily,
* admin.securityRules is used as the namespace name because we cannot barrel
* re-export the contents from security-rules, and we want it to
* match the namespacing in the re-export inside src/index.d.ts
*/
/* eslint-disable @typescript-eslint/no-namespace */
export namespace admin.securityRules {
// See https://github.com/microsoft/TypeScript/issues/4336
/* eslint-disable @typescript-eslint/no-unused-vars */
// See https://github.com/typescript-eslint/typescript-eslint/issues/363
export import RulesFile = securityRulesApi.RulesFile;
export import RulesetMetadata = securityRulesApi.RulesetMetadata;
export import RulesetMetadataList = securityRulesApi.RulesetMetadataList;

/* eslint-disable @typescript-eslint/no-empty-interface */
export interface Ruleset extends securityRulesApi.Ruleset {}
export interface SecurityRules extends securityRulesApi.SecurityRules {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import { HttpRequestConfig, HttpClient, HttpError, AuthorizedHttpClient } from '../utils/api-request';
import { PrefixedFirebaseError } from '../utils/error';
import { FirebaseSecurityRulesError, SecurityRulesErrorCode } from './security-rules-utils';
import { FirebaseSecurityRulesError, SecurityRulesErrorCode } from './security-rules-internal';
import * as utils from '../utils/index';
import * as validator from '../utils/validator';
import { FirebaseApp } from '../firebase-app';
Expand Down
79 changes: 56 additions & 23 deletions src/security-rules/security-rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,47 @@ import { FirebaseApp } from '../firebase-app';
import * as validator from '../utils/validator';
import {
SecurityRulesApiClient, RulesetResponse, RulesetContent, ListRulesetsResponse,
} from './security-rules-api-client';
import { FirebaseSecurityRulesError } from './security-rules-utils';
} from './security-rules-api-client-internal';
import { FirebaseSecurityRulesError } from './security-rules-internal';

/**
* A source file containing some Firebase security rules.
* A source file containing some Firebase security rules. The content includes raw
* source code including text formatting, indentation and comments. Use the
* [`securityRules.createRulesFileFromSource()`](admin.securityRules.SecurityRules#createRulesFileFromSource)
* method to create new instances of this type.
*/
export interface RulesFile {
readonly name: string;
readonly content: string;
}

/**
* Additional metadata associated with a Ruleset.
* Required metadata associated with a ruleset.
*/
export interface RulesetMetadata {
/**
* Name of the `Ruleset` as a short string. This can be directly passed into APIs
* like [`securityRules.getRuleset()`](admin.securityRules.SecurityRules#getRuleset)
* and [`securityRules.deleteRuleset()`](admin.securityRules.SecurityRules#deleteRuleset).
*/
readonly name: string;
/**
* Creation time of the `Ruleset` as a UTC timestamp string.
*/
readonly createTime: string;
}

/**
* A page of ruleset metadata.
*/
export interface RulesetMetadataList {
/**
* A batch of ruleset metadata.
*/
readonly rulesets: RulesetMetadata[];
/**
* The next page token if available. This is needed to retrieve the next batch.
*/
readonly nextPageToken?: string;
}

Expand Down Expand Up @@ -97,7 +114,10 @@ export class Ruleset implements RulesetMetadata {
}

/**
* SecurityRules service bound to the provided app.
* The Firebase `SecurityRules` service interface.
*
* Do not call this constructor directly. Instead, use
* [`admin.securityRules()`](admin.securityRules#securityRules).
*/
export class SecurityRules implements FirebaseServiceInterface {

Expand Down Expand Up @@ -235,12 +255,21 @@ export class SecurityRules implements FirebaseServiceInterface {
}

/**
* Creates a `RulesFile` with the given name and source. Throws if any of the arguments are invalid. This is a
* local operation, and does not involve any network API calls.
* Creates a {@link admin.securityRules.RulesFile `RuleFile`} with the given name
* and source. Throws an error if any of the arguments are invalid. This is a local
* operation, and does not involve any network API calls.
*
* @param {string} name Name to assign to the rules file.
* @param {string|Buffer} source Contents of the rules file.
* @returns {RulesFile} A new rules file instance.
* @example
* ```javascript
* const source = '// Some rules source';
* const rulesFile = admin.securityRules().createRulesFileFromSource(
* 'firestore.rules', source);
* ```
*
* @param name Name to assign to the rules file. This is usually a short file name that
* helps identify the file in a ruleset.
* @param source Contents of the rules file.
* @return A new rules file instance.
*/
public createRulesFileFromSource(name: string, source: string | Buffer): RulesFile {
if (!validator.isNonEmptyString(name)) {
Expand All @@ -265,10 +294,11 @@ export class SecurityRules implements FirebaseServiceInterface {
}

/**
* Creates a new `Ruleset` from the given `RulesFile`.
* Creates a new {@link admin.securityRules.Ruleset `Ruleset`} from the given
* {@link admin.securityRules.RulesFile `RuleFile`}.
*
* @param {RulesFile} file Rules file to include in the new Ruleset.
* @returns {Promise<Ruleset>} A promise that fulfills with the newly created Ruleset.
* @param file Rules file to include in the new `Ruleset`.
* @returns A promise that fulfills with the newly created `Ruleset`.
*/
public createRuleset(file: RulesFile): Promise<Ruleset> {
const ruleset: RulesetContent = {
Expand All @@ -284,24 +314,27 @@ export class SecurityRules implements FirebaseServiceInterface {
}

/**
* Deletes the Ruleset identified by the given name. The input name should be the short name string without
* the project ID prefix. For example, to delete the `projects/project-id/rulesets/my-ruleset`, pass the
* short name "my-ruleset". Rejects with a `not-found` error if the specified Ruleset cannot be found.
* Deletes the {@link admin.securityRules.Ruleset `Ruleset`} identified by the given
* name. The input name should be the short name string without the project ID
* prefix. For example, to delete the `projects/project-id/rulesets/my-ruleset`,
* pass the short name "my-ruleset". Rejects with a `not-found` error if the
* specified `Ruleset` cannot be found.
*
* @param {string} name Name of the Ruleset to delete.
* @returns {Promise<Ruleset>} A promise that fulfills when the Ruleset is deleted.
* @param name Name of the `Ruleset` to delete.
* @return A promise that fulfills when the `Ruleset` is deleted.
*/
public deleteRuleset(name: string): Promise<void> {
return this.client.deleteRuleset(name);
}

/**
* Retrieves a page of rulesets.
* Retrieves a page of ruleset metadata.
*
* @param {number=} pageSize The page size, 100 if undefined. This is also the maximum allowed limit.
* @param {string=} nextPageToken The next page token. If not specified, returns rulesets starting
* without any offset.
* @returns {Promise<RulesetMetadataList>} A promise that fulfills a page of rulesets.
* @param pageSize The page size, 100 if undefined. This is also the maximum allowed
* limit.
* @param nextPageToken The next page token. If not specified, returns rulesets
* starting without any offset.
* @return A promise that fulfills with a page of rulesets.
*/
public listRulesetMetadata(pageSize = 100, nextPageToken?: string): Promise<RulesetMetadataList> {
return this.client.listRulesets(pageSize, nextPageToken)
Expand Down
4 changes: 2 additions & 2 deletions test/unit/security-rules/security-rules-api-client.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
import * as _ from 'lodash';
import * as chai from 'chai';
import * as sinon from 'sinon';
import { SecurityRulesApiClient, RulesetContent } from '../../../src/security-rules/security-rules-api-client';
import { FirebaseSecurityRulesError } from '../../../src/security-rules/security-rules-utils';
import { SecurityRulesApiClient, RulesetContent } from '../../../src/security-rules/security-rules-api-client-internal';
import { FirebaseSecurityRulesError } from '../../../src/security-rules/security-rules-internal';
import { HttpClient } from '../../../src/utils/api-request';
import * as utils from '../utils';
import * as mocks from '../../resources/mocks';
Expand Down
4 changes: 2 additions & 2 deletions test/unit/security-rules/security-rules.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import * as sinon from 'sinon';
import { SecurityRules } from '../../../src/security-rules/security-rules';
import { FirebaseApp } from '../../../src/firebase-app';
import * as mocks from '../../resources/mocks';
import { SecurityRulesApiClient, RulesetContent } from '../../../src/security-rules/security-rules-api-client';
import { FirebaseSecurityRulesError } from '../../../src/security-rules/security-rules-utils';
import { SecurityRulesApiClient, RulesetContent } from '../../../src/security-rules/security-rules-api-client-internal';
import { FirebaseSecurityRulesError } from '../../../src/security-rules/security-rules-internal';
import { deepCopy } from '../../../src/utils/deep-copy';

const expect = chai.expect;
Expand Down