Skip to content

Commit 3db0ebe

Browse files
authored
Added DeleteModel functionality and tests (#782)
* Added DeleteModel functionality and tests
1 parent 3f2ec51 commit 3db0ebe

5 files changed

Lines changed: 126 additions & 1 deletion

File tree

src/machine-learning/machine-learning-api-client.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,18 @@ export class MachineLearningApiClient {
8383
});
8484
}
8585

86+
public deleteModel(modelId: string): Promise<void> {
87+
return this.getUrl()
88+
.then((url) => {
89+
const modelName = this.getModelName(modelId);
90+
const request: HttpRequestConfig = {
91+
method: 'DELETE',
92+
url: `${url}/${modelName}`,
93+
};
94+
return this.sendRequest<void>(request);
95+
});
96+
}
97+
8698
/**
8799
* Gets the specified resource from the ML API. Resource names must be the short names without project
88100
* ID prefix (e.g. `models/123456789`).

src/machine-learning/machine-learning.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ export class MachineLearning implements FirebaseServiceInterface {
168168
* @param {string} modelId The id of the model to delete.
169169
*/
170170
public deleteModel(modelId: string): Promise<void> {
171-
throw new Error('NotImplemented');
171+
return this.client.deleteModel(modelId);
172172
}
173173
}
174174

test/integration/machine-learning.spec.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,19 @@ describe('admin.machineLearning', () => {
3131
'code', 'machine-learning/invalid-argument');
3232
});
3333
});
34+
35+
describe('deleteModel()', () => {
36+
it('rejects with not-found when the Model does not exist', () => {
37+
const nonExistingName = '00000000';
38+
return admin.machineLearning().deleteModel(nonExistingName)
39+
.should.eventually.be.rejected.and.have.property(
40+
'code', 'machine-learning/not-found');
41+
});
42+
43+
it('rejects with invalid-argument when the Model ID is invalid', () => {
44+
return admin.machineLearning().deleteModel('invalid-model-id')
45+
.should.eventually.be.rejected.and.have.property(
46+
'code', 'machine-learning/invalid-argument');
47+
});
48+
});
3449
});

test/unit/machine-learning/machine-learning-api-client.spec.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,82 @@ describe('MachineLearningApiClient', () => {
156156
.should.eventually.be.rejected.and.deep.equal(expected);
157157
});
158158
});
159+
160+
describe('deleteModel', () => {
161+
const INVALID_NAMES: any[] = [null, undefined, '', 1, true, {}, []];
162+
INVALID_NAMES.forEach((invalidName) => {
163+
it(`should reject when called with: ${JSON.stringify(invalidName)}`, () => {
164+
return apiClient.deleteModel(invalidName)
165+
.should.eventually.be.rejected.and.have.property(
166+
'message', 'Model ID must be a non-empty string.');
167+
});
168+
});
169+
170+
it(`should reject when called with prefixed name`, () => {
171+
return apiClient.deleteModel('projects/foo/rulesets/bar')
172+
.should.eventually.be.rejected.and.have.property(
173+
'message', 'Model ID must not contain any "/" characters.');
174+
});
175+
176+
it(`should reject when project id is not available`, () => {
177+
return clientWithoutProjectId.deleteModel(MODEL_ID)
178+
.should.eventually.be.rejectedWith(noProjectId);
179+
});
180+
181+
it('should resolve on success', () => {
182+
const stub = sinon
183+
.stub(HttpClient.prototype, 'send')
184+
.resolves(utils.responseFrom({}));
185+
stubs.push(stub);
186+
return apiClient.deleteModel(MODEL_ID)
187+
.then(() => {
188+
expect(stub).to.have.been.calledOnce.and.calledWith({
189+
method: 'DELETE',
190+
url: 'https://mlkit.googleapis.com/v1beta1/projects/test-project/models/1234567',
191+
headers: EXPECTED_HEADERS,
192+
});
193+
});
194+
});
195+
196+
it('should reject when a full platform error response is received', () => {
197+
const stub = sinon
198+
.stub(HttpClient.prototype, 'send')
199+
.rejects(utils.errorFrom(ERROR_RESPONSE, 404));
200+
stubs.push(stub);
201+
const expected = new FirebaseMachineLearningError('not-found', 'Requested entity not found');
202+
return apiClient.deleteModel(MODEL_ID)
203+
.should.eventually.be.rejected.and.deep.equal(expected);
204+
});
205+
206+
it('should reject with unknown-error when error code is not present', () => {
207+
const stub = sinon
208+
.stub(HttpClient.prototype, 'send')
209+
.rejects(utils.errorFrom({}, 404));
210+
stubs.push(stub);
211+
const expected = new FirebaseMachineLearningError('unknown-error', 'Unknown server error: {}');
212+
return apiClient.deleteModel(MODEL_ID)
213+
.should.eventually.be.rejected.and.deep.equal(expected);
214+
});
215+
216+
it('should reject with unknown-error for non-json response', () => {
217+
const stub = sinon
218+
.stub(HttpClient.prototype, 'send')
219+
.rejects(utils.errorFrom('not json', 404));
220+
stubs.push(stub);
221+
const expected = new FirebaseMachineLearningError(
222+
'unknown-error', 'Unexpected response with status: 404 and body: not json');
223+
return apiClient.deleteModel(MODEL_ID)
224+
.should.eventually.be.rejected.and.deep.equal(expected);
225+
});
226+
227+
it('should reject when failed with a FirebaseAppError', () => {
228+
const expected = new FirebaseAppError('network-error', 'socket hang up');
229+
const stub = sinon
230+
.stub(HttpClient.prototype, 'send')
231+
.rejects(expected);
232+
stubs.push(stub);
233+
return apiClient.deleteModel(MODEL_ID)
234+
.should.eventually.be.rejected.and.deep.equal(expected);
235+
});
236+
});
159237
});

test/unit/machine-learning/machine-learning.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,4 +241,24 @@ describe('MachineLearning', () => {
241241
});
242242
});
243243
});
244+
245+
describe('deleteModel', () => {
246+
it('should propagate API errors', () => {
247+
const stub = sinon
248+
.stub(MachineLearningApiClient.prototype, 'deleteModel')
249+
.rejects(EXPECTED_ERROR);
250+
stubs.push(stub);
251+
return machineLearning.deleteModel('1234567')
252+
.should.eventually.be.rejected.and.deep.equal(EXPECTED_ERROR);
253+
});
254+
255+
it('should resolve on success', () => {
256+
const stub = sinon
257+
.stub(MachineLearningApiClient.prototype, 'deleteModel')
258+
.resolves({});
259+
stubs.push(stub);
260+
261+
return machineLearning.deleteModel('1234567');
262+
});
263+
});
244264
});

0 commit comments

Comments
 (0)