소스 검색

Fix SR subject encoding (#2483) (#2471) (#1995)

* Fix subject encoding frontend (#1995)

* Add tests for schemas with non ascii chars #1995

* Backend: Fix URL encoding while getting schemas (#2471)

Add Test for subject name with non ascii chars
Closes #1995

Co-authored-by: Roman Zabaluev <rzabaluev@provectus.com>

Co-authored-by: Roman Zabaluev <rzabaluev@provectus.com>
Shubham Jain 2 년 전
부모
커밋
01127d8f10

+ 1 - 1
kafka-ui-api/src/main/java/com/provectus/kafka/ui/service/SchemaRegistryService.java

@@ -381,7 +381,7 @@ public class SchemaRegistryService {
     final var builder = UriComponentsBuilder
     final var builder = UriComponentsBuilder
         .fromHttpUrl(schemaRegistry.getUri() + path);
         .fromHttpUrl(schemaRegistry.getUri() + path);
     builder.queryParams(queryParams);
     builder.queryParams(queryParams);
-    return builder.buildAndExpand(uriVariables.toArray()).toUri();
+    return builder.build(uriVariables.toArray());
   }
   }
 
 
   private Function<ClientResponse, Mono<? extends Throwable>> errorOnSchemaDeleteFailure(String schemaName) {
   private Function<ClientResponse, Mono<? extends Throwable>> errorOnSchemaDeleteFailure(String schemaName) {

+ 15 - 0
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) {
   private void createNewSubjectAndAssert(String subject) {
     webTestClient
     webTestClient
         .post()
         .post()

+ 31 - 0
kafka-ui-react-app/src/components/Schemas/Details/__test__/Details.spec.tsx

@@ -6,6 +6,7 @@ import { screen, waitFor } from '@testing-library/dom';
 import {
 import {
   schemasInitialState,
   schemasInitialState,
   schemaVersion,
   schemaVersion,
+  schemaVersionWithNonAsciiChars,
 } from 'redux/reducers/schemas/__test__/fixtures';
 } from 'redux/reducers/schemas/__test__/fixtures';
 import fetchMock from 'fetch-mock';
 import fetchMock from 'fetch-mock';
 import ClusterContext, {
 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', () => {
     describe('empty schema versions', () => {
       beforeEach(async () => {
       beforeEach(async () => {
         const schemasAPILatestMock = fetchMock.getOnce(
         const schemasAPILatestMock = fetchMock.getOnce(

+ 11 - 2
kafka-ui-react-app/src/components/Schemas/Details/__test__/fixtures.ts

@@ -2,12 +2,21 @@ import { SchemaSubject, SchemaType } from 'generated-sources';
 import {
 import {
   schemaVersion1,
   schemaVersion1,
   schemaVersion2,
   schemaVersion2,
+  schemaVersionWithNonAsciiChars,
 } from 'redux/reducers/schemas/__test__/fixtures';
 } from 'redux/reducers/schemas/__test__/fixtures';
 
 
-export const versionPayload = [schemaVersion1, schemaVersion2];
+export const versionPayload = [
+  schemaVersion1,
+  schemaVersion2,
+  schemaVersionWithNonAsciiChars,
+];
 export const versionEmptyPayload = [];
 export const versionEmptyPayload = [];
 
 
-export const versions = [schemaVersion1, schemaVersion2];
+export const versions = [
+  schemaVersion1,
+  schemaVersion2,
+  schemaVersionWithNonAsciiChars,
+];
 
 
 export const jsonSchema: SchemaSubject = {
 export const jsonSchema: SchemaSubject = {
   subject: 'test',
   subject: 'test',

+ 18 - 0
kafka-ui-react-app/src/components/Schemas/Edit/__tests__/Edit.spec.tsx

@@ -5,6 +5,7 @@ import { clusterSchemaEditPath } from 'lib/paths';
 import {
 import {
   schemasInitialState,
   schemasInitialState,
   schemaVersion,
   schemaVersion,
+  schemaVersionWithNonAsciiChars,
 } from 'redux/reducers/schemas/__test__/fixtures';
 } from 'redux/reducers/schemas/__test__/fixtures';
 import { screen, waitFor } from '@testing-library/dom';
 import { screen, waitFor } from '@testing-library/dom';
 import ClusterContext, {
 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();
+      });
+    });
+  });
 });
 });

+ 6 - 1
kafka-ui-react-app/src/components/Schemas/List/__test__/fixtures.ts

@@ -1,9 +1,14 @@
 import {
 import {
   schemaVersion1,
   schemaVersion1,
   schemaVersion2,
   schemaVersion2,
+  schemaVersionWithNonAsciiChars,
 } from 'redux/reducers/schemas/__test__/fixtures';
 } from 'redux/reducers/schemas/__test__/fixtures';
 
 
-const schemas = [schemaVersion1, schemaVersion2];
+const schemas = [
+  schemaVersion1,
+  schemaVersion2,
+  schemaVersionWithNonAsciiChars,
+];
 
 
 export const schemasPayload = {
 export const schemasPayload = {
   pageCount: 1,
   pageCount: 1,

+ 14 - 0
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 clusterName = 'test-cluster-name';
 const groupId = 'test-group-id';
 const groupId = 'test-group-id';
 const schemaId = 'test-schema-id';
 const schemaId = 'test-schema-id';
+const schemaIdWithNonAsciiChars = 'test/test';
+const schemaIdWithNonAsciiCharsEncoded = 'test%2Ftest';
 const topicId = 'test-topic-id';
 const topicId = 'test-topic-id';
 const brokerId = 'test-Broker-id';
 const brokerId = 'test-Broker-id';
 const connectName = 'test-connect-name';
 const connectName = 'test-connect-name';
@@ -112,6 +114,13 @@ describe('Paths', () => {
     expect(paths.clusterSchemaPath()).toEqual(
     expect(paths.clusterSchemaPath()).toEqual(
       paths.clusterSchemaPath(RouteParams.clusterName, RouteParams.subject)
       paths.clusterSchemaPath(RouteParams.clusterName, RouteParams.subject)
     );
     );
+    expect(
+      paths.clusterSchemaPath(clusterName, schemaIdWithNonAsciiChars)
+    ).toEqual(
+      `${paths.clusterSchemasPath(
+        clusterName
+      )}/${schemaIdWithNonAsciiCharsEncoded}`
+    );
   });
   });
   it('clusterSchemaEditPath', () => {
   it('clusterSchemaEditPath', () => {
     expect(paths.clusterSchemaEditPath(clusterName, schemaId)).toEqual(
     expect(paths.clusterSchemaEditPath(clusterName, schemaId)).toEqual(
@@ -120,6 +129,11 @@ describe('Paths', () => {
     expect(paths.clusterSchemaEditPath()).toEqual(
     expect(paths.clusterSchemaEditPath()).toEqual(
       paths.clusterSchemaEditPath(RouteParams.clusterName, RouteParams.subject)
       paths.clusterSchemaEditPath(RouteParams.clusterName, RouteParams.subject)
     );
     );
+    expect(
+      paths.clusterSchemaEditPath(clusterName, schemaIdWithNonAsciiChars)
+    ).toEqual(
+      `${paths.clusterSchemaPath(clusterName, schemaIdWithNonAsciiChars)}/edit`
+    );
   });
   });
   it('clusterSchemaComparePath', () => {
   it('clusterSchemaComparePath', () => {
     expect(paths.clusterSchemaComparePath(clusterName, schemaId)).toEqual(
     expect(paths.clusterSchemaComparePath(clusterName, schemaId)).toEqual(

+ 10 - 2
kafka-ui-react-app/src/lib/paths.ts

@@ -94,11 +94,19 @@ export const clusterSchemaNewPath = (
 export const clusterSchemaPath = (
 export const clusterSchemaPath = (
   clusterName: ClusterName = RouteParams.clusterName,
   clusterName: ClusterName = RouteParams.clusterName,
   subject: SchemaName = RouteParams.subject
   subject: SchemaName = RouteParams.subject
-) => `${clusterSchemasPath(clusterName)}/${subject}`;
+) => {
+  let subjectName = subject;
+  if (subject !== ':subject') subjectName = encodeURIComponent(subject);
+  return `${clusterSchemasPath(clusterName)}/${subjectName}`;
+};
 export const clusterSchemaEditPath = (
 export const clusterSchemaEditPath = (
   clusterName: ClusterName = RouteParams.clusterName,
   clusterName: ClusterName = RouteParams.clusterName,
   subject: SchemaName = RouteParams.subject
   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 = (
 export const clusterSchemaComparePath = (
   clusterName: ClusterName = RouteParams.clusterName,
   clusterName: ClusterName = RouteParams.clusterName,
   subject: SchemaName = RouteParams.subject
   subject: SchemaName = RouteParams.subject

+ 8 - 0
kafka-ui-react-app/src/redux/reducers/schemas/__test__/fixtures.ts

@@ -29,6 +29,14 @@ export const schemaVersion2: SchemaSubject = {
   compatibilityLevel: 'FORWARD_TRANSITIVE',
   compatibilityLevel: 'FORWARD_TRANSITIVE',
   schemaType: SchemaType.JSON,
   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 };
 export { schemaVersion1 as schemaVersion };