Skip to content

Commit a84d3f6

Browse files
authored
Firebase ML Kit Delete Model API implementation (#327)
* implement delete model
1 parent 65f64c0 commit a84d3f6

File tree

2 files changed

+108
-54
lines changed

2 files changed

+108
-54
lines changed

firebase_admin/mlkit.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ def get_model(model_id, app=None):
4949
return Model(mlkit_service.get_model(model_id))
5050

5151

52+
def delete_model(model_id, app=None):
53+
mlkit_service = _get_mlkit_service(app)
54+
mlkit_service.delete_model(model_id)
55+
56+
5257
class Model(object):
5358
"""A Firebase ML Kit Model object."""
5459
def __init__(self, data):
@@ -67,6 +72,13 @@ def __ne__(self, other):
6772
#TODO(ifielker): define the Model properties etc
6873

6974

75+
def _validate_model_id(model_id):
76+
if not isinstance(model_id, six.string_types):
77+
raise TypeError('Model ID must be a string.')
78+
if not re.match(r'^[A-Za-z0-9_-]{1,60}$', model_id):
79+
raise ValueError('Model ID format is invalid.')
80+
81+
7082
class _MLKitService(object):
7183
"""Firebase MLKit service."""
7284

@@ -84,11 +96,15 @@ def __init__(self, app):
8496
base_url=self._project_url)
8597

8698
def get_model(self, model_id):
87-
if not isinstance(model_id, six.string_types):
88-
raise TypeError('Model ID must be a string.')
89-
if not re.match(r'^[A-Za-z0-9_-]{1,60}$', model_id):
90-
raise ValueError('Model ID format is invalid.')
99+
_validate_model_id(model_id)
91100
try:
92101
return self._client.body('get', url='models/{0}'.format(model_id))
93102
except requests.exceptions.RequestException as error:
94103
raise _utils.handle_platform_error_from_requests(error)
104+
105+
def delete_model(self, model_id):
106+
_validate_model_id(model_id)
107+
try:
108+
self._client.body('delete', url='models/{0}'.format(model_id))
109+
except requests.exceptions.RequestException as error:
110+
raise _utils.handle_platform_error_from_requests(error)

tests/test_mlkit.py

Lines changed: 88 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333
'displayName': DISPLAY_NAME_1
3434
}
3535
MODEL_1 = mlkit.Model(MODEL_JSON_1)
36-
_DEFAULT_RESPONSE = json.dumps(MODEL_JSON_1)
36+
_DEFAULT_GET_RESPONSE = json.dumps(MODEL_JSON_1)
37+
_EMPTY_RESPONSE = json.dumps({})
3738

3839
ERROR_CODE = 404
3940
ERROR_MSG = 'The resource was not found'
@@ -47,6 +48,37 @@
4748
}
4849
_ERROR_RESPONSE = json.dumps(ERROR_JSON)
4950

51+
invalid_model_id_args = [
52+
('', ValueError, 'Model ID format is invalid.'),
53+
('&_*#@:/?', ValueError, 'Model ID format is invalid.'),
54+
(None, TypeError, 'Model ID must be a string.'),
55+
(12345, TypeError, 'Model ID must be a string.'),
56+
]
57+
58+
def check_error(err, err_type, msg):
59+
assert isinstance(err, err_type)
60+
assert str(err) == msg
61+
62+
63+
def check_firebase_error(err, code, status, msg):
64+
assert isinstance(err, exceptions.FirebaseError)
65+
assert err.code == code
66+
assert err.http_response is not None
67+
assert err.http_response.status_code == status
68+
assert str(err) == msg
69+
70+
71+
def instrument_mlkit_service(app=None, status=200, payload=None):
72+
if not app:
73+
app = firebase_admin.get_app()
74+
mlkit_service = mlkit._get_mlkit_service(app)
75+
recorder = []
76+
mlkit_service._client.session.mount(
77+
'https://mlkit.googleapis.com',
78+
testutils.MockAdapter(payload, status, recorder)
79+
)
80+
return recorder
81+
5082

5183
class TestGetModel(object):
5284
"""Tests mlkit.get_model."""
@@ -60,75 +92,81 @@ def teardown_class(cls):
6092
testutils.cleanup_apps()
6193

6294
@staticmethod
63-
def check_error(err, err_type, msg):
64-
assert isinstance(err, err_type)
65-
assert str(err) == msg
66-
67-
@staticmethod
68-
def check_firebase_error(err, code, status, msg):
69-
assert isinstance(err, exceptions.FirebaseError)
70-
assert err.code == code
71-
assert err.http_response is not None
72-
assert err.http_response.status_code == status
73-
assert str(err) == msg
74-
75-
def _get_url(self, project_id, model_id):
95+
def _url(project_id, model_id):
7696
return BASE_URL + 'projects/{0}/models/{1}'.format(project_id, model_id)
7797

78-
def _instrument_mlkit_service(self, app=None, status=200, payload=_DEFAULT_RESPONSE):
79-
if not app:
80-
app = firebase_admin.get_app()
81-
mlkit_service = mlkit._get_mlkit_service(app)
82-
recorder = []
83-
mlkit_service._client.session.mount(
84-
'https://mlkit.googleapis.com',
85-
testutils.MockAdapter(payload, status, recorder)
86-
)
87-
return mlkit_service, recorder
88-
8998
def test_get_model(self):
90-
_, recorder = self._instrument_mlkit_service()
99+
recorder = instrument_mlkit_service(status=200, payload=_DEFAULT_GET_RESPONSE)
91100
model = mlkit.get_model(MODEL_ID_1)
92101
assert len(recorder) == 1
93102
assert recorder[0].method == 'GET'
94-
assert recorder[0].url == self._get_url(PROJECT_ID, MODEL_ID_1)
103+
assert recorder[0].url == TestGetModel._url(PROJECT_ID, MODEL_ID_1)
95104
assert model == MODEL_1
96105
assert model._data['name'] == MODEL_NAME_1
97106
assert model._data['displayName'] == DISPLAY_NAME_1
98107

99-
def test_get_model_validation_errors(self):
100-
#Empty model-id
101-
with pytest.raises(ValueError) as err:
102-
mlkit.get_model('')
103-
self.check_error(err.value, ValueError, 'Model ID format is invalid.')
104-
105-
#None model-id
106-
with pytest.raises(TypeError) as err:
107-
mlkit.get_model(None)
108-
self.check_error(err.value, TypeError, 'Model ID must be a string.')
109-
110-
#Wrong type
111-
with pytest.raises(TypeError) as err:
112-
mlkit.get_model(12345)
113-
self.check_error(err.value, TypeError, 'Model ID must be a string.')
114-
115-
#Invalid characters
116-
with pytest.raises(ValueError) as err:
117-
mlkit.get_model('&_*#@:/?')
118-
self.check_error(err.value, ValueError, 'Model ID format is invalid.')
108+
@pytest.mark.parametrize('model_id, exc_type, error_message', invalid_model_id_args)
109+
def test_get_model_validation_errors(self, model_id, exc_type, error_message):
110+
with pytest.raises(exc_type) as err:
111+
mlkit.get_model(model_id)
112+
check_error(err.value, exc_type, error_message)
119113

120114
def test_get_model_error(self):
121-
_, recorder = self._instrument_mlkit_service(status=404, payload=_ERROR_RESPONSE)
115+
recorder = instrument_mlkit_service(status=404, payload=_ERROR_RESPONSE)
122116
with pytest.raises(exceptions.NotFoundError) as err:
123117
mlkit.get_model(MODEL_ID_1)
124-
self.check_firebase_error(err.value, ERROR_STATUS, ERROR_CODE, ERROR_MSG)
118+
check_firebase_error(err.value, ERROR_STATUS, ERROR_CODE, ERROR_MSG)
125119
assert len(recorder) == 1
126120
assert recorder[0].method == 'GET'
127-
assert recorder[0].url == self._get_url(PROJECT_ID, MODEL_ID_1)
121+
assert recorder[0].url == self._url(PROJECT_ID, MODEL_ID_1)
128122

129123
def test_no_project_id(self):
130124
def evaluate():
131125
app = firebase_admin.initialize_app(testutils.MockCredential(), name='no_project_id')
132126
with pytest.raises(ValueError):
133127
mlkit.get_model(MODEL_ID_1, app)
134128
testutils.run_without_project_id(evaluate)
129+
130+
class TestDeleteModel(object):
131+
"""Tests mlkit.delete_model."""
132+
@classmethod
133+
def setup_class(cls):
134+
cred = testutils.MockCredential()
135+
firebase_admin.initialize_app(cred, {'projectId': PROJECT_ID})
136+
137+
@classmethod
138+
def teardown_class(cls):
139+
testutils.cleanup_apps()
140+
141+
@staticmethod
142+
def _url(project_id, model_id):
143+
return BASE_URL + 'projects/{0}/models/{1}'.format(project_id, model_id)
144+
145+
def test_delete_model(self):
146+
recorder = instrument_mlkit_service(status=200, payload=_EMPTY_RESPONSE)
147+
mlkit.delete_model(MODEL_ID_1) # no response for delete
148+
assert len(recorder) == 1
149+
assert recorder[0].method == 'DELETE'
150+
assert recorder[0].url == TestDeleteModel._url(PROJECT_ID, MODEL_ID_1)
151+
152+
@pytest.mark.parametrize('model_id, exc_type, error_message', invalid_model_id_args)
153+
def test_delete_model_validation_errors(self, model_id, exc_type, error_message):
154+
with pytest.raises(exc_type) as err:
155+
mlkit.delete_model(model_id)
156+
check_error(err.value, exc_type, error_message)
157+
158+
def test_delete_model_error(self):
159+
recorder = instrument_mlkit_service(status=404, payload=_ERROR_RESPONSE)
160+
with pytest.raises(exceptions.NotFoundError) as err:
161+
mlkit.delete_model(MODEL_ID_1)
162+
check_firebase_error(err.value, ERROR_STATUS, ERROR_CODE, ERROR_MSG)
163+
assert len(recorder) == 1
164+
assert recorder[0].method == 'DELETE'
165+
assert recorder[0].url == self._url(PROJECT_ID, MODEL_ID_1)
166+
167+
def test_no_project_id(self):
168+
def evaluate():
169+
app = firebase_admin.initialize_app(testutils.MockCredential(), name='no_project_id')
170+
with pytest.raises(ValueError):
171+
mlkit.delete_model(MODEL_ID_1, app)
172+
testutils.run_without_project_id(evaluate)

0 commit comments

Comments
 (0)