Skip to content

Commit e88ba99

Browse files
committed
chore(config): add merge params for create api
1 parent 8a8843b commit e88ba99

2 files changed

Lines changed: 88 additions & 7 deletions

File tree

rootfs/api/tests/test_config.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,78 @@ def test_config(self, mock_requests):
141141
self.assertEqual(response.status_code, 405, response.data)
142142
return config5
143143

144+
def test_config_merge_false(self, mock_requests):
145+
"""
146+
Test that config can be created with merge='false' parameter
147+
which replaces the entire config instead of merging
148+
"""
149+
app_id = self.create_app()
150+
151+
# set initial config values
152+
url = f"/v2/apps/{app_id}/config"
153+
value1 = {"name": "DEBUG", "value": "true", "group": "global"}
154+
value2 = {"name": "DATABASE_URL", "value": "postgres://localhost/db", "group": "global"}
155+
body = {'values': [value1, value2]}
156+
response = self.client.post(url, body)
157+
self.assertEqual(response.status_code, 201, response.data)
158+
self.assertEqual(len(response.data['values']), 2)
159+
160+
# replace entire config with merge='false'
161+
value3 = {"name": "NEW_CONFIG", "value": "replacement_value", "group": "global"}
162+
body = {'values': [value3]}
163+
response = self.client.post(f"{url}?merge=false", body)
164+
self.assertEqual(response.status_code, 201, response.data)
165+
166+
# verify only the new config exists
167+
response = self.client.get(url)
168+
self.assertEqual(response.status_code, 200, response.data)
169+
self.assertEqual(len(response.data['values']), 1)
170+
self.assertEqual(response.data['values'][0]['name'], "NEW_CONFIG")
171+
self.assertEqual(response.data['values'][0]['value'], "replacement_value")
172+
173+
def test_config_merge_false_with_empty_values(self, mock_requests):
174+
"""
175+
Test that config can be created with merge='false' and empty values
176+
"""
177+
app_id = self.create_app()
178+
179+
# set initial config values
180+
url = f"/v2/apps/{app_id}/config"
181+
value1 = {"name": "DEBUG", "value": "true", "group": "global"}
182+
body = {'values': [value1]}
183+
response = self.client.post(url, body)
184+
self.assertEqual(response.status_code, 201, response.data)
185+
self.assertEqual(len(response.data['values']), 1)
186+
187+
# replace with empty config using merge='false'
188+
body = {'values': []}
189+
response = self.client.post(f"{url}?merge=false", body)
190+
self.assertEqual(response.status_code, 201, response.data)
191+
192+
# verify config is empty
193+
response = self.client.get(url)
194+
self.assertEqual(response.status_code, 200, response.data)
195+
self.assertEqual(len(response.data['values']), 0)
196+
197+
def test_config_merge_false_case_insensitive(self, mock_requests):
198+
"""
199+
Test that merge parameter is case insensitive
200+
"""
201+
app_id = self.create_app()
202+
203+
url = f"/v2/apps/{app_id}/config"
204+
value1 = {"name": "DEBUG", "value": "true", "group": "global"}
205+
body = {'values': [value1]}
206+
207+
# test with uppercase FALSE
208+
response = self.client.post(f"{url}?merge=FALSE", body)
209+
self.assertEqual(response.status_code, 201, response.data)
210+
211+
# verify config was replaced
212+
response = self.client.get(url)
213+
self.assertEqual(response.status_code, 200, response.data)
214+
self.assertEqual(len(response.data['values']), 1)
215+
144216
def test_registry_set(self, mock_requests):
145217
app_id = self.create_app()
146218
# set an initial config value

rootfs/api/views.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ def get_queryset(self):
260260

261261
def destroy(self, *args, **kwargs):
262262
key = self.get_object().key
263-
response = super(TokenViewSet, self).destroy(self, *args, **kwargs)
263+
response = super().destroy(self, *args, **kwargs)
264264
cache.delete(key)
265265
return response
266266

@@ -294,7 +294,7 @@ def get_object(self, **kwargs):
294294

295295
def create(self, request, **kwargs):
296296
request.data['app'] = self.get_app()
297-
return super(AppFilterViewSet, self).create(request, **kwargs)
297+
return super().create(request, **kwargs)
298298

299299

300300
class ReleasableViewSet(AppFilterViewSet):
@@ -327,7 +327,7 @@ def list(self, request, *args, **kwargs):
327327
which are owned by the user as well as any apps they have been given permission to
328328
interact with.
329329
"""
330-
queryset = super(AppViewSet, self).get_queryset(**kwargs) | \
330+
queryset = super().get_queryset(**kwargs) | \
331331
get_objects_for_user(
332332
self.request.user, f'api.{models.app.VIEW_APP_PERMISSION.codename}')
333333
instance = self.filter_queryset(queryset)
@@ -394,7 +394,7 @@ def post_save(self, build):
394394
if is_loopback(image):
395395
raise DryccException("image must not use the loopback address")
396396
build.create_release(self.request.user)
397-
super(BuildViewSet, self).post_save(build)
397+
super().post_save(build)
398398

399399

400400
class LimitSpecViewSet(BaseDryccViewSet):
@@ -438,6 +438,15 @@ class ConfigViewSet(ReleasableViewSet):
438438
model = models.config.Config
439439
serializer_class = serializers.ConfigSerializer
440440

441+
def create(self, request, **kwargs):
442+
if self.request.query_params.get('merge', 'true').lower() == 'true':
443+
return super().create(request, **kwargs)
444+
values = self.get_serializer().validate_values(request.data.get('values'))
445+
config = self.model(app=self.get_app(), owner=self.request.user, values=values)
446+
config.save(ignore_update_fields=["values"])
447+
self.post_save(config)
448+
return Response(status=status.HTTP_201_CREATED)
449+
441450
def delete(self, request, **kwargs):
442451
values_refs = self.get_serializer().validate_values_refs(request.data.get('values_refs'))
443452
if not values_refs or not values_refs.values():
@@ -691,7 +700,7 @@ def get_object(self, **kwargs):
691700

692701
def get_queryset(self, **kwargs):
693702
ptypes = self.request.query_params.get('ptypes', '').strip()
694-
queryset = super(ReleaseViewSet, self).get_queryset(**kwargs)
703+
queryset = super().get_queryset(**kwargs)
695704
if ptypes:
696705
queryset = queryset.filter(Q(
697706
deployed_ptypes__contains=[
@@ -829,7 +838,7 @@ def create(self, request, *args, **kwargs):
829838
return Response(message, status=status.HTTP_403_FORBIDDEN)
830839
request.data['app'] = app
831840
request.data['owner'] = self.user
832-
super(BuildHookViewSet, self).create(request, *args, **kwargs)
841+
super().create(request, *args, **kwargs)
833842
# return the application databag
834843
response = {
835844
'release': {
@@ -1067,7 +1076,7 @@ def get_object(self):
10671076
def retrieve(self, request, *args, **kwargs):
10681077
resource = self.get_object()
10691078
resource.retrieve(request)
1070-
response = super(AppSingleResourceViewSet, self).retrieve(request, *args, **kwargs) # noqa
1079+
response = super().retrieve(request, *args, **kwargs) # noqa
10711080
response.data["message"] = resource.message
10721081
return response
10731082

0 commit comments

Comments
 (0)