Skip to content

Commit 9b468a7

Browse files
committed
add more
1 parent 710d390 commit 9b468a7

File tree

5 files changed

+167
-7
lines changed

5 files changed

+167
-7
lines changed

src/confluent_kafka/schema_registry/_async/schema_registry_client.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,6 +1307,30 @@ async def set_config(
13071307
'config/{}'.format(_urlencode(subject_name)), body=config.to_dict()
13081308
)
13091309

1310+
async def delete_config(self, subject_name: Optional[str] = None) -> 'ServerConfig':
1311+
"""
1312+
Delete the specified subject-level compatibility level config and revert to the global default.
1313+
1314+
Args:
1315+
subject_name (str, optional): Subject name. Deletes global config
1316+
if left unset.
1317+
1318+
Returns:
1319+
ServerConfig: The old deleted config
1320+
1321+
Raises:
1322+
SchemaRegistryError: if the request was unsuccessful.
1323+
1324+
See Also:
1325+
`DELETE Subject Config API Reference <https://docs.confluent.io/current/schema-registry/develop/api.html#delete--config-(string- subject)>`_
1326+
"""
1327+
if subject_name is not None:
1328+
url = 'config/{}'.format(_urlencode(subject_name))
1329+
else:
1330+
url = 'config'
1331+
result = await self._rest_client.delete(url)
1332+
return ServerConfig.from_dict(result)
1333+
13101334
async def get_config(self, subject_name: Optional[str] = None) -> 'ServerConfig':
13111335
"""
13121336
Get the current config.
@@ -1430,6 +1454,23 @@ async def update_global_mode(self, mode: str, force: bool = False) -> str:
14301454
""" # noqa: E501
14311455
result = await self._rest_client.put('mode?force={}'.format(force), body={'mode': mode})
14321456
return result['mode']
1457+
async def get_contexts(self, offset: int = 0, limit: int = -1) -> List[str]:
1458+
"""
1459+
Retrieves a list of contexts.
1460+
1461+
Args:
1462+
offset (int): Pagination offset for results.
1463+
limit (int): Pagination size for results. Ignored if negative.
1464+
1465+
Returns:
1466+
List[str]: List of contexts.
1467+
1468+
Raises:
1469+
SchemaRegistryError: if the request was unsuccessful.
1470+
""" # noqa: E501
1471+
1472+
result = await self._rest_client.get('contexts', query={'offset': offset, 'limit': limit})
1473+
return result
14331474

14341475
def clear_latest_caches(self):
14351476
self._latest_version_cache.clear()

src/confluent_kafka/schema_registry/_sync/schema_registry_client.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,6 +1307,30 @@ def set_config(
13071307
'config/{}'.format(_urlencode(subject_name)), body=config.to_dict()
13081308
)
13091309

1310+
def delete_config(self, subject_name: Optional[str] = None) -> 'ServerConfig':
1311+
"""
1312+
Delete the specified subject-level compatibility level config and revert to the global default.
1313+
1314+
Args:
1315+
subject_name (str, optional): Subject name. Deletes global config
1316+
if left unset.
1317+
1318+
Returns:
1319+
ServerConfig: The old deleted config
1320+
1321+
Raises:
1322+
SchemaRegistryError: if the request was unsuccessful.
1323+
1324+
See Also:
1325+
`DELETE Subject Config API Reference <https://docs.confluent.io/current/schema-registry/develop/api.html#delete--config-(string- subject)>`_
1326+
"""
1327+
if subject_name is not None:
1328+
url = 'config/{}'.format(_urlencode(subject_name))
1329+
else:
1330+
url = 'config'
1331+
result = self._rest_client.delete(url)
1332+
return ServerConfig.from_dict(result)
1333+
13101334
def get_config(self, subject_name: Optional[str] = None) -> 'ServerConfig':
13111335
"""
13121336
Get the current config.
@@ -1430,6 +1454,23 @@ def update_global_mode(self, mode: str, force: bool = False) -> str:
14301454
""" # noqa: E501
14311455
result = self._rest_client.put('mode?force={}'.format(force), body={'mode': mode})
14321456
return result['mode']
1457+
def get_contexts(self, offset: int = 0, limit: int = -1) -> List[str]:
1458+
"""
1459+
Retrieves a list of contexts.
1460+
1461+
Args:
1462+
offset (int): Pagination offset for results.
1463+
limit (int): Pagination size for results. Ignored if negative.
1464+
1465+
Returns:
1466+
List[str]: List of contexts.
1467+
1468+
Raises:
1469+
SchemaRegistryError: if the request was unsuccessful.
1470+
""" # noqa: E501
1471+
1472+
result = self._rest_client.get('contexts', query={'offset': offset, 'limit': limit})
1473+
return result
14331474

14341475
def clear_latest_caches(self):
14351476
self._latest_version_cache.clear()

tests/schema_registry/_async/test_api_client.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,24 @@ async def test_set_compatibility_invalid(mock_schema_registry):
426426
e.value.error_code = 42203
427427

428428

429+
async def test_delete_config(mock_schema_registry):
430+
conf = {'url': TEST_URL}
431+
sr = AsyncSchemaRegistryClient(conf)
432+
433+
result = await sr.delete_config()
434+
assert result.compatibility == 'FULL'
435+
436+
437+
async def test_delete_config_subject_not_found(mock_schema_registry):
438+
conf = {'url': TEST_URL}
439+
sr = AsyncSchemaRegistryClient(conf)
440+
441+
with pytest.raises(SchemaRegistryError, match="Subject not found") as e:
442+
await sr.delete_config("notfound")
443+
assert e.value.http_status_code == 404
444+
assert e.value.error_code == 40401
445+
446+
429447
async def test_get_compatibility_subject_not_found(mock_schema_registry):
430448
conf = {'url': TEST_URL}
431449
sr = AsyncSchemaRegistryClient(conf)
@@ -436,6 +454,14 @@ async def test_get_compatibility_subject_not_found(mock_schema_registry):
436454
assert e.value.error_code == 40401
437455

438456

457+
async def test_get_contexts(mock_schema_registry):
458+
conf = {'url': TEST_URL}
459+
sr = AsyncSchemaRegistryClient(conf)
460+
461+
result = await sr.get_contexts()
462+
assert result == ['context1', 'context2']
463+
464+
439465
async def test_schema_equivilence(load_avsc):
440466
schema_str1 = load_avsc('basic_schema.avsc')
441467
schema_str2 = load_avsc('basic_schema.avsc')

tests/schema_registry/_sync/test_api_client.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,24 @@ def test_set_compatibility_invalid(mock_schema_registry):
426426
e.value.error_code = 42203
427427

428428

429+
def test_delete_config(mock_schema_registry):
430+
conf = {'url': TEST_URL}
431+
sr = SchemaRegistryClient(conf)
432+
433+
result = sr.delete_config()
434+
assert result.compatibility == 'FULL'
435+
436+
437+
def test_delete_config_subject_not_found(mock_schema_registry):
438+
conf = {'url': TEST_URL}
439+
sr = SchemaRegistryClient(conf)
440+
441+
with pytest.raises(SchemaRegistryError, match="Subject not found") as e:
442+
sr.delete_config("notfound")
443+
assert e.value.http_status_code == 404
444+
assert e.value.error_code == 40401
445+
446+
429447
def test_get_compatibility_subject_not_found(mock_schema_registry):
430448
conf = {'url': TEST_URL}
431449
sr = SchemaRegistryClient(conf)
@@ -436,6 +454,14 @@ def test_get_compatibility_subject_not_found(mock_schema_registry):
436454
assert e.value.error_code == 40401
437455

438456

457+
def test_get_contexts(mock_schema_registry):
458+
conf = {'url': TEST_URL}
459+
sr = SchemaRegistryClient(conf)
460+
461+
result = sr.get_contexts()
462+
assert result == ['context1', 'context2']
463+
464+
439465
def test_schema_equivilence(load_avsc):
440466
schema_str1 = load_avsc('basic_schema.avsc')
441467
schema_str2 = load_avsc('basic_schema.avsc')

tests/schema_registry/conftest.py

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,12 @@
9797
+--------+-------------------------------------------------+-------+------------------------------+
9898
| DELETE | /subjects/notfound/versions/422 | 42202 | Invalid version |
9999
+--------+-------------------------------------------------+-------+------------------------------+
100-
| GET | /config/notconfig | 40401 | Subject not found |
100+
| GET | /config/notfound | 40401 | Subject not found |
101101
+--------+-------------------------------------------------+-------+------------------------------+
102102
| PUT | /config** | 42203 | Invalid compatibility level |
103103
+--------+-------------------------------------------------+-------+------------------------------+
104+
| DELETE | /config/notfound | 40401 | Subject not found |
105+
+--------+-------------------------------------------------+-------+------------------------------+
104106
| POST | /compatibility/subjects/notfound/versions/[0-9] | 40401 | Subject not found |
105107
+--------+-------------------------------------------------+-------+------------------------------+
106108
| POST | /compatibility/subjects/invalid/versions/[0-9] | 42201 | Invalid Schema |
@@ -138,8 +140,11 @@ def mock_schema_registry():
138140
respx_mock.post(COMPATIBILITY_SUBJECTS_ALL_VERSIONS_RE).mock(
139141
side_effect=post_compatibility_subjects_all_versions_callback)
140142

141-
respx_mock.get(COMPATIBILITY_RE).mock(side_effect=get_compatibility_callback)
142-
respx_mock.put(COMPATIBILITY_RE).mock(side_effect=put_compatibility_callback)
143+
respx_mock.get(CONFIG_RE).mock(side_effect=get_config_callback)
144+
respx_mock.put(CONFIG_RE).mock(side_effect=put_config_callback)
145+
respx_mock.delete(CONFIG_RE).mock(side_effect=delete_config_callback)
146+
147+
respx_mock.get(CONTEXTS_RE).mock(side_effect=get_contexts_callback)
143148

144149
respx_mock.get(MODE_GLOBAL_RE).mock(side_effect=get_global_mode_callback)
145150
respx_mock.put(MODE_GLOBAL_RE).mock(side_effect=put_global_mode_callback)
@@ -175,13 +180,16 @@ def mock_schema_registry():
175180
SUBJECTS_VERSIONS_SCHEMA_RE = re.compile(r"/subjects/(.*)/versions/(.*)/schema(\?.*)?$")
176181
SUBJECTS_VERSIONS_REFERENCED_BY_RE = re.compile(r"/subjects/(.*)/versions/(.*)/referencedby(\?.*)?$")
177182

178-
COMPATIBILITY_RE = re.compile("/config/?(.*)$")
183+
CONFIG_RE = re.compile("/config/?(.*)$")
184+
179185
COMPATIBILITY_SUBJECTS_VERSIONS_RE = re.compile("/compatibility/subjects/(.*)/versions/?(.*)$")
180186
COMPATIBILITY_SUBJECTS_ALL_VERSIONS_RE = re.compile("/compatibility/subjects/(.*)/versions")
181187

182188
MODE_GLOBAL_RE = re.compile(r"/mode(\?.*)?$")
183189
MODE_RE = re.compile("/mode/(.*)$")
184190

191+
CONTEXTS_RE = re.compile(r"/contexts(\?.*)?$")
192+
185193
# constants
186194
SCHEMA_ID = 47
187195
VERSION = 3
@@ -222,10 +230,10 @@ def _load_avsc(name) -> str:
222230
return fd.read()
223231

224232

225-
def get_compatibility_callback(request, route):
233+
def get_config_callback(request, route):
226234
COUNTER['GET'][request.url.path] += 1
227235

228-
path_match = re.match(COMPATIBILITY_RE, request.url.path)
236+
path_match = re.match(CONFIG_RE, request.url.path)
229237
subject = path_match.group(1)
230238

231239
if subject == "notfound":
@@ -235,7 +243,7 @@ def get_compatibility_callback(request, route):
235243
return Response(200, json={'compatibility': 'FULL'})
236244

237245

238-
def put_compatibility_callback(request, route):
246+
def put_config_callback(request, route):
239247
COUNTER['PUT'][request.url.path] += 1
240248

241249
body = json.loads(request.content.decode('utf-8'))
@@ -248,6 +256,24 @@ def put_compatibility_callback(request, route):
248256
return Response(200, json=body)
249257

250258

259+
def delete_config_callback(request, route):
260+
COUNTER['DELETE'][request.url.path] += 1
261+
262+
path_match = re.match(CONFIG_RE, request.url.path)
263+
subject = path_match.group(1)
264+
265+
if subject == "notfound":
266+
return Response(404, json={'error_code': 40401,
267+
'message': "Subject not found"})
268+
269+
return Response(200, json={'compatibility': 'FULL'})
270+
271+
272+
def get_contexts_callback(request, route):
273+
COUNTER['GET'][request.url.path] += 1
274+
return Response(200, json=['context1', 'context2'])
275+
276+
251277
def delete_subject_callback(request, route):
252278
COUNTER['DELETE'][request.url.path] += 1
253279

0 commit comments

Comments
 (0)