diff --git a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/SchemaRegistryService.java b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/SchemaRegistryService.java index f4c9355804..92603ba979 100644 --- a/kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/SchemaRegistryService.java +++ b/kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/SchemaRegistryService.java @@ -381,7 +381,7 @@ public class SchemaRegistryService { final var builder = UriComponentsBuilder .fromHttpUrl(schemaRegistry.getUri() + path); builder.queryParams(queryParams); - return builder.buildAndExpand(uriVariables.toArray()).toUri(); + return builder.build(uriVariables.toArray()); } private Function> errorOnSchemaDeleteFailure(String schemaName) { diff --git a/kafka-ui-api/src/test/java/com/provectus/kafka/ui/SchemaRegistryServiceTests.java b/kafka-ui-api/src/test/java/com/provectus/kafka/ui/SchemaRegistryServiceTests.java index 190831da98..60959be049 100644 --- a/kafka-ui-api/src/test/java/com/provectus/kafka/ui/SchemaRegistryServiceTests.java +++ b/kafka-ui-api/src/test/java/com/provectus/kafka/ui/SchemaRegistryServiceTests.java @@ -274,6 +274,21 @@ class SchemaRegistryServiceTests extends AbstractIntegrationTest { }); } + @Test + void shouldCreateNewSchemaWhenSubjectIncludesNonAsciiCharacters() { + String schema = + "{\"subject\":\"test/test\",\"schemaType\":\"JSON\",\"schema\":" + + "\"{\\\"type\\\": \\\"string\\\"}\"}"; + + webTestClient + .post() + .uri("/api/clusters/{clusterName}/schemas", LOCAL) + .contentType(MediaType.APPLICATION_JSON) + .body(BodyInserters.fromValue(schema)) + .exchange() + .expectStatus().isOk(); + } + private void createNewSubjectAndAssert(String subject) { webTestClient .post() diff --git a/kafka-ui-react-app/src/components/Schemas/Details/__test__/Details.spec.tsx b/kafka-ui-react-app/src/components/Schemas/Details/__test__/Details.spec.tsx index fefcf99801..a09788b162 100644 --- a/kafka-ui-react-app/src/components/Schemas/Details/__test__/Details.spec.tsx +++ b/kafka-ui-react-app/src/components/Schemas/Details/__test__/Details.spec.tsx @@ -6,6 +6,7 @@ import { screen, waitFor } from '@testing-library/dom'; import { schemasInitialState, schemaVersion, + schemaVersionWithNonAsciiChars, } from 'redux/reducers/schemas/__test__/fixtures'; import fetchMock from 'fetch-mock'; import ClusterContext, { @@ -98,6 +99,36 @@ describe('Details', () => { }); }); + describe('fetch success schema with non ascii characters', () => { + describe('has schema versions', () => { + beforeEach(async () => { + const schemasAPILatestMock = fetchMock.getOnce( + schemasAPILatestUrl, + schemaVersionWithNonAsciiChars + ); + const schemasAPIVersionsMock = fetchMock.getOnce( + schemasAPIVersionsUrl, + versionPayload + ); + await act(() => { + renderComponent(); + }); + await waitFor(() => { + expect(schemasAPILatestMock.called()).toBeTruthy(); + }); + await waitFor(() => { + expect(schemasAPIVersionsMock.called()).toBeTruthy(); + }); + }); + + it('renders component with schema info', () => { + expect(screen.getByText('Edit Schema')).toBeInTheDocument(); + expect(screen.queryByRole('progressbar')).not.toBeInTheDocument(); + expect(screen.getByRole('table')).toBeInTheDocument(); + }); + }); + }); + describe('empty schema versions', () => { beforeEach(async () => { const schemasAPILatestMock = fetchMock.getOnce( diff --git a/kafka-ui-react-app/src/components/Schemas/Details/__test__/fixtures.ts b/kafka-ui-react-app/src/components/Schemas/Details/__test__/fixtures.ts index 6443dfd541..174cfd830f 100644 --- a/kafka-ui-react-app/src/components/Schemas/Details/__test__/fixtures.ts +++ b/kafka-ui-react-app/src/components/Schemas/Details/__test__/fixtures.ts @@ -2,12 +2,21 @@ import { SchemaSubject, SchemaType } from 'generated-sources'; import { schemaVersion1, schemaVersion2, + schemaVersionWithNonAsciiChars, } from 'redux/reducers/schemas/__test__/fixtures'; -export const versionPayload = [schemaVersion1, schemaVersion2]; +export const versionPayload = [ + schemaVersion1, + schemaVersion2, + schemaVersionWithNonAsciiChars, +]; export const versionEmptyPayload = []; -export const versions = [schemaVersion1, schemaVersion2]; +export const versions = [ + schemaVersion1, + schemaVersion2, + schemaVersionWithNonAsciiChars, +]; export const jsonSchema: SchemaSubject = { subject: 'test', diff --git a/kafka-ui-react-app/src/components/Schemas/Edit/__tests__/Edit.spec.tsx b/kafka-ui-react-app/src/components/Schemas/Edit/__tests__/Edit.spec.tsx index ae4e74d7a9..b5dcb28387 100644 --- a/kafka-ui-react-app/src/components/Schemas/Edit/__tests__/Edit.spec.tsx +++ b/kafka-ui-react-app/src/components/Schemas/Edit/__tests__/Edit.spec.tsx @@ -5,6 +5,7 @@ import { clusterSchemaEditPath } from 'lib/paths'; import { schemasInitialState, schemaVersion, + schemaVersionWithNonAsciiChars, } from 'redux/reducers/schemas/__test__/fixtures'; import { screen, waitFor } from '@testing-library/dom'; import ClusterContext, { @@ -70,4 +71,21 @@ describe('Edit', () => { }); }); }); + + describe('fetch success schema with non ascii characters', () => { + describe('has schema versions', () => { + it('renders component with schema info', async () => { + const schemasAPILatestMock = fetchMock.getOnce( + schemasAPILatestUrl, + schemaVersionWithNonAsciiChars + ); + await act(() => { + renderComponent(); + }); + await waitFor(() => expect(schemasAPILatestMock.called()).toBeTruthy()); + expect(screen.getByText('Submit')).toBeInTheDocument(); + expect(screen.queryByRole('progressbar')).not.toBeInTheDocument(); + }); + }); + }); }); diff --git a/kafka-ui-react-app/src/components/Schemas/List/__test__/fixtures.ts b/kafka-ui-react-app/src/components/Schemas/List/__test__/fixtures.ts index 65bdd99741..992882d332 100644 --- a/kafka-ui-react-app/src/components/Schemas/List/__test__/fixtures.ts +++ b/kafka-ui-react-app/src/components/Schemas/List/__test__/fixtures.ts @@ -1,9 +1,14 @@ import { schemaVersion1, schemaVersion2, + schemaVersionWithNonAsciiChars, } from 'redux/reducers/schemas/__test__/fixtures'; -const schemas = [schemaVersion1, schemaVersion2]; +const schemas = [ + schemaVersion1, + schemaVersion2, + schemaVersionWithNonAsciiChars, +]; export const schemasPayload = { pageCount: 1, diff --git a/kafka-ui-react-app/src/lib/__test__/paths.spec.ts b/kafka-ui-react-app/src/lib/__test__/paths.spec.ts index 3bfbcef7a5..4fb4a3360c 100644 --- a/kafka-ui-react-app/src/lib/__test__/paths.spec.ts +++ b/kafka-ui-react-app/src/lib/__test__/paths.spec.ts @@ -5,6 +5,8 @@ import { RouteParams } from 'lib/paths'; const clusterName = 'test-cluster-name'; const groupId = 'test-group-id'; const schemaId = 'test-schema-id'; +const schemaIdWithNonAsciiChars = 'test/test'; +const schemaIdWithNonAsciiCharsEncoded = 'test%2Ftest'; const topicId = 'test-topic-id'; const brokerId = 'test-Broker-id'; const connectName = 'test-connect-name'; @@ -112,6 +114,13 @@ describe('Paths', () => { expect(paths.clusterSchemaPath()).toEqual( paths.clusterSchemaPath(RouteParams.clusterName, RouteParams.subject) ); + expect( + paths.clusterSchemaPath(clusterName, schemaIdWithNonAsciiChars) + ).toEqual( + `${paths.clusterSchemasPath( + clusterName + )}/${schemaIdWithNonAsciiCharsEncoded}` + ); }); it('clusterSchemaEditPath', () => { expect(paths.clusterSchemaEditPath(clusterName, schemaId)).toEqual( @@ -120,6 +129,11 @@ describe('Paths', () => { expect(paths.clusterSchemaEditPath()).toEqual( paths.clusterSchemaEditPath(RouteParams.clusterName, RouteParams.subject) ); + expect( + paths.clusterSchemaEditPath(clusterName, schemaIdWithNonAsciiChars) + ).toEqual( + `${paths.clusterSchemaPath(clusterName, schemaIdWithNonAsciiChars)}/edit` + ); }); it('clusterSchemaComparePath', () => { expect(paths.clusterSchemaComparePath(clusterName, schemaId)).toEqual( diff --git a/kafka-ui-react-app/src/lib/paths.ts b/kafka-ui-react-app/src/lib/paths.ts index 3392b88795..67dea37580 100644 --- a/kafka-ui-react-app/src/lib/paths.ts +++ b/kafka-ui-react-app/src/lib/paths.ts @@ -94,11 +94,19 @@ export const clusterSchemaNewPath = ( export const clusterSchemaPath = ( clusterName: ClusterName = RouteParams.clusterName, subject: SchemaName = RouteParams.subject -) => `${clusterSchemasPath(clusterName)}/${subject}`; +) => { + let subjectName = subject; + if (subject !== ':subject') subjectName = encodeURIComponent(subject); + return `${clusterSchemasPath(clusterName)}/${subjectName}`; +}; export const clusterSchemaEditPath = ( clusterName: ClusterName = RouteParams.clusterName, subject: SchemaName = RouteParams.subject -) => `${clusterSchemasPath(clusterName)}/${subject}/edit`; +) => { + let subjectName = subject; + if (subject !== ':subject') subjectName = encodeURIComponent(subject); + return `${clusterSchemasPath(clusterName)}/${subjectName}/edit`; +}; export const clusterSchemaComparePath = ( clusterName: ClusterName = RouteParams.clusterName, subject: SchemaName = RouteParams.subject diff --git a/kafka-ui-react-app/src/redux/reducers/schemas/__test__/fixtures.ts b/kafka-ui-react-app/src/redux/reducers/schemas/__test__/fixtures.ts index f6b924592e..5cab12584b 100644 --- a/kafka-ui-react-app/src/redux/reducers/schemas/__test__/fixtures.ts +++ b/kafka-ui-react-app/src/redux/reducers/schemas/__test__/fixtures.ts @@ -29,6 +29,14 @@ export const schemaVersion2: SchemaSubject = { compatibilityLevel: 'FORWARD_TRANSITIVE', schemaType: SchemaType.JSON, }; +export const schemaVersionWithNonAsciiChars: SchemaSubject = { + subject: 'test/test', + version: '1', + id: 29, + schema: '13', + compatibilityLevel: 'FORWARD_TRANSITIVE', + schemaType: SchemaType.JSON, +}; export { schemaVersion1 as schemaVersion };