Skip to content

Commit bee7372

Browse files
DGS-22816 Add support for contextPrefix on list contexts API (#4003)
1 parent 014378f commit bee7372

File tree

3 files changed

+222
-3
lines changed

3 files changed

+222
-3
lines changed

client/src/main/java/io/confluent/kafka/schemaregistry/client/rest/RestService.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,7 +1542,27 @@ public List<String> getAllContexts()
15421542

15431543
public List<String> getAllContexts(Map<String, String> requestProperties)
15441544
throws IOException, RestClientException {
1545+
return getAllContexts(0, -1, null, requestProperties);
1546+
}
1547+
1548+
public List<String> getAllContexts(int offset, int limit, String contextPrefix)
1549+
throws IOException, RestClientException {
1550+
return getAllContexts(offset, limit, contextPrefix, DEFAULT_REQUEST_PROPERTIES);
1551+
}
1552+
1553+
public List<String> getAllContexts(int offset, int limit, String contextPrefix,
1554+
Map<String, String> requestProperties)
1555+
throws IOException, RestClientException {
15451556
UriBuilder builder = UriBuilder.fromPath("/contexts");
1557+
if (offset > 0) {
1558+
builder.queryParam("offset", offset);
1559+
}
1560+
if (limit >= 0) {
1561+
builder.queryParam("limit", limit);
1562+
}
1563+
if (contextPrefix != null && !contextPrefix.isEmpty()) {
1564+
builder.queryParam("contextPrefix", contextPrefix);
1565+
}
15461566
String path = builder.build().toString();
15471567
List<String> response = httpRequest(path, "GET", null, requestProperties,
15481568
ALL_CONTEXTS_RESPONSE_TYPE);

core/src/main/java/io/confluent/kafka/schemaregistry/rest/resources/ContextsResource.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ public ContextsResource(SchemaRegistry schemaRegistry) {
7777
@GET
7878
@DocumentedName("getAllContexts")
7979
@Operation(summary = "List contexts",
80-
description = "Retrieves a list of contexts.",
80+
description = "Retrieves a list of contexts. "
81+
+ "Optionally filter by context prefix.",
8182
responses = {
8283
@ApiResponse(responseCode = "200", description = "The contexts.", content = @Content(
8384
array = @ArraySchema(schema = @Schema(example = ".")))),
@@ -91,10 +92,22 @@ public List<String> listContexts(
9192
@Parameter(description = "Pagination offset for results")
9293
@DefaultValue("0") @QueryParam("offset") int offset,
9394
@Parameter(description = "Pagination size for results. Ignored if negative")
94-
@DefaultValue("-1") @QueryParam("limit") int limit) {
95+
@DefaultValue("-1") @QueryParam("limit") int limit,
96+
@Parameter(description = "Filter contexts by prefix. "
97+
+ "Only contexts starting with this prefix will be returned")
98+
@QueryParam("contextPrefix") String contextPrefix) {
9599
try {
96100
limit = schemaRegistry.normalizeContextLimit(limit);
97101
List<String> contexts = schemaRegistry.listContexts();
102+
103+
// Filter by context prefix if provided
104+
if (contextPrefix != null && !contextPrefix.isEmpty()) {
105+
contexts = contexts.stream()
106+
.filter(context -> context.startsWith(contextPrefix))
107+
.collect(Collectors.toList());
108+
}
109+
110+
// Apply pagination after filtering
98111
return contexts.stream()
99112
.skip(offset)
100113
.limit(limit)

core/src/test/java/io/confluent/kafka/schemaregistry/rest/RestApiContextTest.java

Lines changed: 187 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,5 +460,191 @@ static void registerAndVerifySchema(RestService restService, String schemaString
460460
"Registered schema should be found"
461461
);
462462
}
463-
}
464463

464+
@Test
465+
public void testContextPrefixFilter() throws Exception {
466+
// Register schemas in multiple contexts to create test data
467+
String subject1 = ":.prod:testSubject1";
468+
String subject2 = ":.prod-eu:testSubject2";
469+
String subject3 = ":.staging:testSubject3";
470+
String subject4 = ":.dev:testSubject4";
471+
String subject5 = "testSubject5"; // default context
472+
473+
List<String> schemas = TestUtils.getRandomCanonicalAvroString(5);
474+
475+
// Register schemas in different contexts
476+
restApp.restClient.registerSchema(schemas.get(0), subject1);
477+
restApp.restClient.registerSchema(schemas.get(1), subject2);
478+
restApp.restClient.registerSchema(schemas.get(2), subject3);
479+
restApp.restClient.registerSchema(schemas.get(3), subject4);
480+
restApp.restClient.registerSchema(schemas.get(4), subject5);
481+
482+
// Test 1: Get all contexts without filter
483+
List<String> allContexts = restApp.restClient.getAllContexts();
484+
assertEquals(
485+
ImmutableList.of(DEFAULT_CONTEXT, ".dev", ".prod", ".prod-eu", ".staging"),
486+
allContexts,
487+
"Getting all contexts should return all registered contexts"
488+
);
489+
490+
// Test 2: Filter by prefix ".prod" - should match both ".prod" and ".prod-eu"
491+
List<String> prodContexts = restApp.restClient.getAllContexts(0, -1, ".prod");
492+
assertEquals(
493+
ImmutableList.of(".prod", ".prod-eu"),
494+
prodContexts,
495+
"Filtering by '.prod' should return contexts starting with '.prod'"
496+
);
497+
498+
// Test 3: Filter by exact match ".prod-eu"
499+
List<String> prodEuContexts = restApp.restClient.getAllContexts(0, -1, ".prod-eu");
500+
assertEquals(
501+
Collections.singletonList(".prod-eu"),
502+
prodEuContexts,
503+
"Filtering by '.prod-eu' should return only '.prod-eu' context"
504+
);
505+
506+
// Test 4: Filter by "." - should return all contexts (all start with ".")
507+
List<String> dotPrefixContexts = restApp.restClient.getAllContexts(0, -1, ".");
508+
assertEquals(
509+
allContexts,
510+
dotPrefixContexts,
511+
"Filtering by '.' should return all contexts as all contexts start with '.'"
512+
);
513+
514+
// Test 5: Filter by ".s" - should return ".staging"
515+
List<String> stagingContexts = restApp.restClient.getAllContexts(0, -1, ".s");
516+
assertEquals(
517+
Collections.singletonList(".staging"),
518+
stagingContexts,
519+
"Filtering by '.s' should return '.staging' context"
520+
);
521+
522+
// Test 6: Filter by non-matching prefix
523+
List<String> noMatch = restApp.restClient.getAllContexts(0, -1, ".nonexistent");
524+
assertEquals(
525+
Collections.emptyList(),
526+
noMatch,
527+
"Filtering by non-matching prefix should return empty list"
528+
);
529+
530+
// Test 7: Filter with empty string - should return all contexts
531+
List<String> emptyFilter = restApp.restClient.getAllContexts(0, -1, "");
532+
assertEquals(
533+
allContexts,
534+
emptyFilter,
535+
"Filtering with empty string should return all contexts"
536+
);
537+
}
538+
539+
@Test
540+
public void testContextPrefixFilterWithPagination() throws Exception {
541+
// Register schemas in multiple contexts
542+
String subject1 = ":.alpha:test";
543+
String subject2 = ":.beta:test";
544+
String subject3 = ":.gamma:test";
545+
String subject4 = ":.delta:test";
546+
547+
List<String> schemas = TestUtils.getRandomCanonicalAvroString(4);
548+
549+
restApp.restClient.registerSchema(schemas.get(0), subject1);
550+
restApp.restClient.registerSchema(schemas.get(1), subject2);
551+
restApp.restClient.registerSchema(schemas.get(2), subject3);
552+
restApp.restClient.registerSchema(schemas.get(3), subject4);
553+
554+
// Test 1: Get all contexts (should have default + 4 named contexts = 5 total)
555+
List<String> allContexts = restApp.restClient.getAllContexts();
556+
assertEquals(
557+
5,
558+
allContexts.size(),
559+
"Should have 5 total contexts"
560+
);
561+
562+
// Test 2: Filter by "." and paginate - offset 0, limit 2
563+
List<String> page1 = restApp.restClient.getAllContexts(0, 2, ".");
564+
assertEquals(
565+
2,
566+
page1.size(),
567+
"First page should have 2 contexts"
568+
);
569+
assertEquals(
570+
ImmutableList.of(DEFAULT_CONTEXT, ".alpha"),
571+
page1,
572+
"First page should contain default and .alpha"
573+
);
574+
575+
// Test 3: Filter by "." and paginate - offset 2, limit 2
576+
List<String> page2 = restApp.restClient.getAllContexts(2, 2, ".");
577+
assertEquals(
578+
2,
579+
page2.size(),
580+
"Second page should have 2 contexts"
581+
);
582+
assertEquals(
583+
ImmutableList.of(".beta", ".delta"),
584+
page2,
585+
"Second page should contain .beta and .delta"
586+
);
587+
588+
// Test 4: Filter by "." and paginate - offset 4, limit 2
589+
List<String> page3 = restApp.restClient.getAllContexts(4, 2, ".");
590+
assertEquals(
591+
1,
592+
page3.size(),
593+
"Third page should have 1 context"
594+
);
595+
assertEquals(
596+
Collections.singletonList(".gamma"),
597+
page3,
598+
"Third page should contain .gamma"
599+
);
600+
601+
// Test 5: Filter by ".a" (should match .alpha) with pagination
602+
List<String> alphaPage = restApp.restClient.getAllContexts(0, 1, ".a");
603+
assertEquals(
604+
Collections.singletonList(".alpha"),
605+
alphaPage,
606+
"Filtering by '.a' with limit 1 should return .alpha"
607+
);
608+
}
609+
610+
@Test
611+
public void testContextPrefixFilterEdgeCases() throws Exception {
612+
// Create a context
613+
String subject = ":.test:subject";
614+
List<String> schemas = TestUtils.getRandomCanonicalAvroString(1);
615+
restApp.restClient.registerSchema(schemas.get(0), subject);
616+
617+
// Test 1: Null contextPrefix (should return all contexts)
618+
List<String> nullFilter = restApp.restClient.getAllContexts(0, -1, null);
619+
assertEquals(
620+
ImmutableList.of(DEFAULT_CONTEXT, ".test"),
621+
nullFilter,
622+
"Null contextPrefix should return all contexts"
623+
);
624+
625+
// Test 2: Very long non-matching prefix
626+
List<String> longPrefix = restApp.restClient.getAllContexts(
627+
0, -1, ".thisIsAVeryLongPrefixThatWillNeverMatch");
628+
assertEquals(
629+
Collections.emptyList(),
630+
longPrefix,
631+
"Long non-matching prefix should return empty list"
632+
);
633+
634+
// Test 3: Filter by "." - should return all contexts (all start with ".")
635+
List<String> dotPrefixCtx = restApp.restClient.getAllContexts(0, -1, ".");
636+
assertEquals(
637+
ImmutableList.of(DEFAULT_CONTEXT, ".test"),
638+
dotPrefixCtx,
639+
"Prefix '.' should match all contexts as all contexts start with '.'"
640+
);
641+
642+
// Test 4: Case-sensitive matching - ".TEST" should not match ".test"
643+
List<String> upperCaseFilter = restApp.restClient.getAllContexts(0, -1, ".TEST");
644+
assertEquals(
645+
Collections.emptyList(),
646+
upperCaseFilter,
647+
"Context prefix filter should be case-sensitive"
648+
);
649+
}
650+
}

0 commit comments

Comments
 (0)