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 f4c9355804ea60d02bd039068347de49c9d1af2e..92603ba979e23b622ba4faf90382c32f583f1771 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 190831da98b11f4548bb40f26fbf273cc4a5005c..60959be049245f3fbf4aff32b91fff71a91238b7 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 fefcf99801f78486fce7d1ca4016d7119d6e9300..a09788b162e097e4f3537da4f46494d63b46f762 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 6443dfd541b0991c838b6aa96912cd4d59f96bc9..174cfd830f9f349ddfcebfd4da0a0842fbe3be26 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 ae4e74d7a9eb2c006d52fb995f40765317fdf15a..b5dcb2838734781b587ac1dfb4c8da89079e350e 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 65bdd9974102748c8d6bbf3e9afb27d87696abc3..992882d33251acbe428db555ae01d821b6a2c428 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 3bfbcef7a52e7c5f6d1e2afac481e01e3f344f02..4fb4a3360ced7164f97c1071c7228dfbe8ee82ac 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 3392b88795ff1620f2a19add87819dce656867f6..67dea3758002eb3a80af433f36be97fa99aae08e 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 f6b924592e183347d5fa582204a9e1f44307b77c..5cab12584b70618e2b6864c0dbc2868987e02ea7 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 };