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
14 changes: 14 additions & 0 deletions src/security-rules/security-rules-api-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,20 @@ export class SecurityRulesApiClient {
return this.sendRequest<RulesetResponse>(request);
}

public deleteRuleset(name: string): Promise<void> {
return Promise.resolve()
.then(() => {
return this.getRulesetName(name);
})
.then((rulesetName) => {
const request: HttpRequestConfig = {
method: 'DELETE',
url: `${this.url}/${rulesetName}`,
};
return this.sendRequest<void>(request);
});
}

public getRelease(name: string): Promise<Release> {
return this.getResource<Release>(`releases/${name}`);
}
Expand Down
12 changes: 12 additions & 0 deletions src/security-rules/security-rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,18 @@ 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.
*
* @param {string} name Name of the Ruleset to delete.
* @returns {Promise<Ruleset>} A promise that fulfills when the Ruleset is deleted.
*/
public deleteRuleset(name: string): Promise<void> {
return this.client.deleteRuleset(name);
}

private getRulesetForRelease(releaseName: string): Promise<Ruleset> {
return this.client.getRelease(releaseName)
.then((release) => {
Expand Down
72 changes: 72 additions & 0 deletions test/unit/security-rules/security-rules-api-client.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,4 +304,76 @@ describe('SecurityRulesApiClient', () => {
.should.eventually.be.rejected.and.deep.equal(expected);
});
});

describe('deleteRuleset', () => {
const INVALID_NAMES: any[] = [null, undefined, '', 1, true, {}, []];
INVALID_NAMES.forEach((invalidName) => {
it(`should reject when called with: ${JSON.stringify(invalidName)}`, () => {
return apiClient.deleteRuleset(invalidName)
.should.eventually.be.rejected.and.have.property(
'message', 'Ruleset name must be a non-empty string.');
});
});

it(`should reject when called with prefixed name`, () => {
return apiClient.deleteRuleset('projects/foo/rulesets/bar')
.should.eventually.be.rejected.and.have.property(
'message', 'Ruleset name must not contain any "/" characters.');
});

it('should resolve on success', () => {
const stub = sinon
.stub(HttpClient.prototype, 'send')
.resolves(utils.responseFrom({}));
stubs.push(stub);
return apiClient.deleteRuleset(RULESET_NAME)
.then(() => {
expect(stub).to.have.been.calledOnce.and.calledWith({
method: 'DELETE',
url: 'https://firebaserules.googleapis.com/v1/projects/test-project/rulesets/ruleset-id',
});
});
});

it('should throw when a full platform error response is received', () => {
const stub = sinon
.stub(HttpClient.prototype, 'send')
.rejects(utils.errorFrom(ERROR_RESPONSE, 404));
stubs.push(stub);
const expected = new FirebaseSecurityRulesError('not-found', 'Requested entity not found');
return apiClient.deleteRuleset(RULESET_NAME)
.should.eventually.be.rejected.and.deep.equal(expected);
});

it('should throw unknown-error when error code is not present', () => {
const stub = sinon
.stub(HttpClient.prototype, 'send')
.rejects(utils.errorFrom({}, 404));
stubs.push(stub);
const expected = new FirebaseSecurityRulesError('unknown-error', 'Unknown server error: {}');
return apiClient.deleteRuleset(RULESET_NAME)
.should.eventually.be.rejected.and.deep.equal(expected);
});

it('should throw unknown-error for non-json response', () => {
const stub = sinon
.stub(HttpClient.prototype, 'send')
.rejects(utils.errorFrom('not json', 404));
stubs.push(stub);
const expected = new FirebaseSecurityRulesError(
'unknown-error', 'Unexpected response with status: 404 and body: not json');
return apiClient.deleteRuleset(RULESET_NAME)
.should.eventually.be.rejected.and.deep.equal(expected);
});

it('should throw when rejected with a FirebaseAppError', () => {
const expected = new FirebaseAppError('network-error', 'socket hang up');
const stub = sinon
.stub(HttpClient.prototype, 'send')
.rejects(expected);
stubs.push(stub);
return apiClient.deleteRuleset(RULESET_NAME)
.should.eventually.be.rejected.and.deep.equal(expected);
});
});
});
20 changes: 20 additions & 0 deletions test/unit/security-rules/security-rules.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,4 +342,24 @@ describe('SecurityRules', () => {
});
});
});

describe('deleteRuleset', () => {
it('should propagate API errors', () => {
const stub = sinon
.stub(SecurityRulesApiClient.prototype, 'deleteRuleset')
.rejects(EXPECTED_ERROR);
stubs.push(stub);
return securityRules.deleteRuleset('foo')
.should.eventually.be.rejected.and.deep.equal(EXPECTED_ERROR);
});

it('should resolve on success', () => {
const stub = sinon
.stub(SecurityRulesApiClient.prototype, 'deleteRuleset')
.resolves({});
stubs.push(stub);

return securityRules.deleteRuleset('foo');
});
});
});