diff --git a/src/features/Apiexplorer/Schema/RecursiveContent/RecursiveProperties/__tests__/RecursiveProperties.test.tsx b/src/features/Apiexplorer/Schema/RecursiveContent/RecursiveProperties/__tests__/RecursiveProperties.test.tsx index 46e37534..38d5a1bd 100644 --- a/src/features/Apiexplorer/Schema/RecursiveContent/RecursiveProperties/__tests__/RecursiveProperties.test.tsx +++ b/src/features/Apiexplorer/Schema/RecursiveContent/RecursiveProperties/__tests__/RecursiveProperties.test.tsx @@ -13,9 +13,35 @@ const fakeItem = { }, }, properties: { - recursive_item: { + recursive_item_1: { description: 'This is a recursive item', }, + recursive_item_2: { + description: 'This is recursive item 2', + oneOf: 'This is oneOf key for recursive_item_2', + }, + }, + definitions: { + stream_types: { + description: 'This stream_types description', + type: 'string', + enum: [ + 'balance', + 'candles', + 'cashier_payments', + 'p2p_advert', + 'p2p_advertiser', + 'p2p_order', + 'proposal', + 'proposal_open_contract', + 'ticks', + 'transaction', + 'trading_platform_asset_listing', + 'website_status', + 'p2p_settings', + 'crypto_estimations', + ], + }, }, }; @@ -26,21 +52,43 @@ describe('RecursiveProperties', () => { is_open properties={fakeItem.properties || fakeItem?.items?.properties} value={fakeItem} + jsonSchema={fakeItem} />, ); const recursion_1_description = await screen.findByText(/nested items/i); expect(recursion_1_description).toBeVisible(); - const recursion_2_name = await screen.findByText(/recursive_item/i); + const recursion_2_name = await screen.findByText(/recursive_item_1/i); expect(recursion_2_name).toBeVisible(); const recursion_2_description = await screen.findByText(/This is a recursive item/i); expect(recursion_2_description).toBeVisible(); + + const recursion_3_name = await screen.findByText(/recursive_item_2/i); + expect(recursion_3_name).toBeVisible(); + + const recursion_3_description = await screen.findByText(/This is recursive item 2/i); + expect(recursion_3_description).toBeVisible(); }); it('renders only the description (last item) if there are no nested items anymore', async () => { - render(); + render( + , + ); const item = await screen.findByText(/This is the main item description/i); expect(item).toBeVisible(); }); + + it('renders StreamTypesObject if value contains oneOf meaning its forgetAll api call', async () => { + render( + , + ); + const streamTypesObject = await screen.getByTestId('dt_stream_types_object'); + expect(streamTypesObject).toBeVisible(); + }); }); diff --git a/src/features/Apiexplorer/Schema/RecursiveContent/RecursiveProperties/index.tsx b/src/features/Apiexplorer/Schema/RecursiveContent/RecursiveProperties/index.tsx index 1d2e1b7b..306451db 100644 --- a/src/features/Apiexplorer/Schema/RecursiveContent/RecursiveProperties/index.tsx +++ b/src/features/Apiexplorer/Schema/RecursiveContent/RecursiveProperties/index.tsx @@ -1,18 +1,28 @@ import React from 'react'; import SchemaDescription from '../SchemaDescription'; import SchemaObjectContent from '../SchemaObjectContent'; +import StreamTypesObject from '../StreamTypesObject'; type TRecursiveProperties = { is_open: boolean; properties: any; value: any; + jsonSchema: any; }; -const RecursiveProperties = ({ is_open, properties, value }: TRecursiveProperties) => { +const RecursiveProperties = ({ is_open, properties, value, jsonSchema }: TRecursiveProperties) => { const keys = properties && Object.keys(properties); + if (!is_open) { return null; } + if (value && 'oneOf' in value) { + return ( + + + + ); + } if (!keys) { return ( @@ -28,7 +38,17 @@ const RecursiveProperties = ({ is_open, properties, value }: TRecursivePropertie {index === 0 && value?.items?.description && ( )} - + {key === 'forget_all' && 'oneOf' in value[key] ? ( + + ) : ( + + )} ); })} diff --git a/src/features/Apiexplorer/Schema/RecursiveContent/SchemaBodyHeader/__tests__/SchemaBodyHeader.test.tsx b/src/features/Apiexplorer/Schema/RecursiveContent/SchemaBodyHeader/__tests__/SchemaBodyHeader.test.tsx index a12ace0f..1ffa73e5 100644 --- a/src/features/Apiexplorer/Schema/RecursiveContent/SchemaBodyHeader/__tests__/SchemaBodyHeader.test.tsx +++ b/src/features/Apiexplorer/Schema/RecursiveContent/SchemaBodyHeader/__tests__/SchemaBodyHeader.test.tsx @@ -16,6 +16,7 @@ describe('SchemaBodyHeader', () => { title='test title' is_open_object setIsOpenObject={() => jest.fn()} + is_stream_types={false} />, ); const type = await screen.findByText('number'); @@ -34,6 +35,7 @@ describe('SchemaBodyHeader', () => { title='test title' is_open_object setIsOpenObject={() => jest.fn()} + is_stream_types={false} />, ); const type = await screen.findByText('array'); @@ -52,6 +54,7 @@ describe('SchemaBodyHeader', () => { title='test title' is_open_object setIsOpenObject={() => jest.fn()} + is_stream_types={false} />, ); const type = await screen.findByText('integer'); @@ -70,6 +73,7 @@ describe('SchemaBodyHeader', () => { title='test title' is_open_object setIsOpenObject={() => jest.fn()} + is_stream_types={false} />, ); const type = await screen.findByText('string'); @@ -88,6 +92,7 @@ describe('SchemaBodyHeader', () => { title='test title' is_open_object setIsOpenObject={() => jest.fn()} + is_stream_types={false} />, ); const type = await screen.findByText(/number/i); @@ -106,6 +111,7 @@ describe('SchemaBodyHeader', () => { title='test title' is_open_object setIsOpenObject={() => jest.fn()} + is_stream_types={false} />, ); const type = await screen.findByText(/string/i); @@ -124,6 +130,7 @@ describe('SchemaBodyHeader', () => { title='test title' is_open_object setIsOpenObject={() => jest.fn()} + is_stream_types={false} />, ); const type = await screen.findByText(/array/i); @@ -142,9 +149,33 @@ describe('SchemaBodyHeader', () => { title='test title' is_open_object setIsOpenObject={() => jest.fn()} + is_stream_types={false} />, ); const type = await screen.findByText(/integer/i); expect(type).toBeVisible(); }); + + it('should render the SchemaBodyHeader with oneOf stream_types array if is_stream_types is true', async () => { + render( + jest.fn()} + is_stream_types={true} + />, + ); + const oneOfType = await screen.findByText(/one of/i); + const stream_types = await screen.findByText(/stream_types/i); + const array_type = await screen.findByText(/array/i); + expect(oneOfType).toBeVisible(); + expect(stream_types).toBeVisible(); + expect(array_type).toBeVisible(); + }); }); diff --git a/src/features/Apiexplorer/Schema/RecursiveContent/SchemaBodyHeader/index.tsx b/src/features/Apiexplorer/Schema/RecursiveContent/SchemaBodyHeader/index.tsx index b37ae6bf..6a21e4af 100644 --- a/src/features/Apiexplorer/Schema/RecursiveContent/SchemaBodyHeader/index.tsx +++ b/src/features/Apiexplorer/Schema/RecursiveContent/SchemaBodyHeader/index.tsx @@ -12,6 +12,7 @@ type TSchemaBodyHeader = { setIsOpenObject: (boolean) => void; examples: string[]; enum; + is_stream_types: boolean; }; const SchemaBodyHeader = ({ @@ -24,6 +25,7 @@ const SchemaBodyHeader = ({ title, is_open_object, setIsOpenObject, + is_stream_types, }: TSchemaBodyHeader) => { let typeClassName; switch (type) { @@ -116,6 +118,15 @@ const SchemaBodyHeader = ({ ) : ( <> )} + + {is_stream_types && ( +
+ one of + + array +
+ )} + {pattern && (
{pattern}
diff --git a/src/features/Apiexplorer/Schema/RecursiveContent/SchemaObjectContent/__tests__/SchemaObjectContent.test.tsx b/src/features/Apiexplorer/Schema/RecursiveContent/SchemaObjectContent/__tests__/SchemaObjectContent.test.tsx index d2373021..f3f38e18 100644 --- a/src/features/Apiexplorer/Schema/RecursiveContent/SchemaObjectContent/__tests__/SchemaObjectContent.test.tsx +++ b/src/features/Apiexplorer/Schema/RecursiveContent/SchemaObjectContent/__tests__/SchemaObjectContent.test.tsx @@ -21,6 +21,37 @@ const fake_properties = { }, }; +const stream_types_schema = { + properties: { + test_property: { + description: 'property description', + oneOf: 'this is oneOf key', + }, + }, + definitions: { + stream_types: { + description: 'This stream_types description', + type: 'string', + enum: [ + 'balance', + 'candles', + 'cashier_payments', + 'p2p_advert', + 'p2p_advertiser', + 'p2p_order', + 'proposal', + 'proposal_open_contract', + 'ticks', + 'transaction', + 'trading_platform_asset_listing', + 'website_status', + 'p2p_settings', + 'crypto_estimations', + ], + }, + }, +}; + describe('SchemaObjectContent', () => { it('should be able to open a nested object item', async () => { render(); @@ -96,4 +127,24 @@ describe('SchemaObjectContent', () => { const schema = await screen.findByTitle('JSON'); expect(schema).toBeVisible(); }); + + it('should open StreamTypesObject upon clicking stream_types button', async () => { + render( + , + ); + + const button = await screen.findByRole('button', { name: /stream_types/i }); + expect(button).toBeVisible(); + + await userEvent.click(button); + + const streamTypesObject = await screen.getByTestId('dt_stream_types_object'); + expect(streamTypesObject).toBeVisible(); + }); }); diff --git a/src/features/Apiexplorer/Schema/RecursiveContent/SchemaObjectContent/index.tsx b/src/features/Apiexplorer/Schema/RecursiveContent/SchemaObjectContent/index.tsx index 499e9282..5a3c77dc 100644 --- a/src/features/Apiexplorer/Schema/RecursiveContent/SchemaObjectContent/index.tsx +++ b/src/features/Apiexplorer/Schema/RecursiveContent/SchemaObjectContent/index.tsx @@ -10,9 +10,16 @@ const ReactJson = React.lazy(() => import('react-json-view')); type TSchemaObjectContent = { key_value: string; properties: any; + jsonSchema?: any; + is_stream_types?: boolean; }; -export default function SchemaObjectContent({ key_value, properties }: TSchemaObjectContent) { +export default function SchemaObjectContent({ + key_value, + properties, + jsonSchema, + is_stream_types = false, +}: TSchemaObjectContent) { const [is_open_object, setIsOpenObject] = useState(false); const [is_code_open, setIsCodeOpen] = useState(false); const { @@ -52,6 +59,7 @@ export default function SchemaObjectContent({ key_value, properties }: TSchemaOb title={title} is_open_object={is_open_object} setIsOpenObject={setIsOpenObject} + is_stream_types={is_stream_types} /> {/* Description */} @@ -68,6 +76,7 @@ export default function SchemaObjectContent({ key_value, properties }: TSchemaOb is_open={is_open_object} properties={value.properties || value?.items?.properties} value={value} + jsonSchema={jsonSchema} /> )}
diff --git a/src/features/Apiexplorer/Schema/RecursiveContent/StreamTypesObject/StreamTypesBody.tsx b/src/features/Apiexplorer/Schema/RecursiveContent/StreamTypesObject/StreamTypesBody.tsx new file mode 100644 index 00000000..32b0c98d --- /dev/null +++ b/src/features/Apiexplorer/Schema/RecursiveContent/StreamTypesObject/StreamTypesBody.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { TEnumStreamType } from '@site/src/types'; +import styles from '../../Schema.module.scss'; + +type TStreamTypesBody = { + type: string; + _enum: TEnumStreamType; +}; + +const StreamTypesBody = ({ type, _enum }: TStreamTypesBody) => { + return ( +
+
+ enum + {type} + {_enum.map((enum_name: string, i: number) => ( +
+ {enum_name} +
+ ))} +
+
+ ); +}; + +export default StreamTypesBody; diff --git a/src/features/Apiexplorer/Schema/RecursiveContent/StreamTypesObject/StreamTypesHeader.tsx b/src/features/Apiexplorer/Schema/RecursiveContent/StreamTypesObject/StreamTypesHeader.tsx new file mode 100644 index 00000000..003cb1cb --- /dev/null +++ b/src/features/Apiexplorer/Schema/RecursiveContent/StreamTypesObject/StreamTypesHeader.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import SchemaTitle from '../../SchemaTitle'; +import styles from '../../Schema.module.scss'; + +type TStreamTypesHeader = { + description: string; +}; + +const StreamTypesHeader = ({ description }: TStreamTypesHeader) => { + return ( +
+ stream_types +
+
{description}
+
+
+ ); +}; + +export default StreamTypesHeader; diff --git a/src/features/Apiexplorer/Schema/RecursiveContent/StreamTypesObject/__tests__/StreamTypesObject.test.tsx b/src/features/Apiexplorer/Schema/RecursiveContent/StreamTypesObject/__tests__/StreamTypesObject.test.tsx new file mode 100644 index 00000000..c9feb182 --- /dev/null +++ b/src/features/Apiexplorer/Schema/RecursiveContent/StreamTypesObject/__tests__/StreamTypesObject.test.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import userEvent from '@testing-library/user-event'; +import { screen, render } from '@testing-library/react'; +import { TEnumStreamType } from '@site/src/types'; +import StreamTypesObject from '..'; + +describe('StreamTypesObject', () => { + const _enum: TEnumStreamType = [ + 'balance', + 'candles', + 'cashier_payments', + 'p2p_advert', + 'p2p_advertiser', + 'p2p_order', + 'proposal', + 'proposal_open_contract', + 'ticks', + 'transaction', + 'website_status', + ]; + const json_schema = { + stream_types: { + description: 'This stream_types description', + type: 'string', + enum: _enum, + }, + }; + + it('should render button that opens jsonschema', async () => { + render(); + + const schema_button = await screen.findByText('{}'); + + expect(schema_button).toBeVisible(); + + await userEvent.click(schema_button); + + const schema = await screen.findByTitle('JSON'); + expect(schema).toBeVisible(); + }); + + it('should render the header of the object', () => { + render(); + + const header_title = screen.getAllByText(/stream_types/)[0]; + expect(header_title).toBeInTheDocument(); + + const header_description = screen.getByText(/This stream_types description/i); + expect(header_description).toBeInTheDocument(); + }); + + it('should render the body of StreamTypesObject', () => { + render(); + + const type = screen.getByText(/enum/i); + expect(type).toBeInTheDocument(); + + const enum_type = screen.getByText(/string/i); + expect(enum_type).toBeInTheDocument(); + + json_schema.stream_types.enum.map((item) => { + const enum_name = screen.getByText(item); + expect(enum_name).toBeInTheDocument(); + }); + }); +}); diff --git a/src/features/Apiexplorer/Schema/RecursiveContent/StreamTypesObject/index.tsx b/src/features/Apiexplorer/Schema/RecursiveContent/StreamTypesObject/index.tsx new file mode 100644 index 00000000..f4801b5a --- /dev/null +++ b/src/features/Apiexplorer/Schema/RecursiveContent/StreamTypesObject/index.tsx @@ -0,0 +1,49 @@ +import React, { Suspense } from 'react'; +import StreamTypesHeader from './StreamTypesHeader'; +import StreamTypesBody from './StreamTypesBody'; +import SourceButton from '../../SourceButton/SourceButton'; +import { TEnumStreamType } from '@site/src/types'; +import styles from '../../Schema.module.scss'; + +type TStreamTypesObject = { + definitions: { + stream_types: { + description: string; + type: string; + enum: TEnumStreamType; + }; + }; +}; + +const ReactJson = React.lazy(() => import('react-json-view')); + +const StreamTypesObject = ({ definitions }: TStreamTypesObject) => { + const [is_code_open, setIsCodeOpen] = React.useState(false); + let data = ''; + try { + data = JSON.stringify(definitions.stream_types, null, 2); + } catch (error) { + data = ''; + console.error('There was an issue stringifying JSON data: ', error); + } + return ( +
+ + + {is_code_open ? ( + + Loading...
}> + + + + ) : ( + + )} + + ); +}; + +export default StreamTypesObject; diff --git a/src/features/Apiexplorer/Schema/Schema.module.scss b/src/features/Apiexplorer/Schema/Schema.module.scss index 6c1c1f19..09a27c3b 100644 --- a/src/features/Apiexplorer/Schema/Schema.module.scss +++ b/src/features/Apiexplorer/Schema/Schema.module.scss @@ -34,6 +34,7 @@ display: flex; flex-direction: row; position: relative; + gap: rem(0.8); p { color: var(--ifm-color-white); @@ -91,6 +92,7 @@ cursor: pointer; font-weight: 500; transition: opacity 0.2s ease-in-out; + z-index: 1; } .schemaBody { @@ -301,3 +303,84 @@ .reactJsonView { margin-bottom: rem(2.4); } + +.streamTypesContainer { + margin-top: rem(1); + margin-bottom: rem(2.4); + + &:hover { + > .sourceButtonMain { + opacity: 1; + margin: rem(1); + } + } + + .streamTypesHeader { + padding: rem(1); + border: none; + background: #151717; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + + .streamTypesTitle { + font-size: rem(1.6); + color: var(--ifm-color-white); + font-weight: bold; + } + .streamTypesDescription { + display: flex; + padding: rem(0.5) 0; + gap: rem(2); + justify-content: space-between; + color: var(--ifm-color-white); + font-size: rem(1.4); + } + } + + .streamTypesBody { + background-color: rgba(219, 219, 219, 0.05); + display: flex; + padding: rem(1); + border-bottom-left-radius: 6px; + border-bottom-right-radius: 6px; + + .streamTypesObject { + display: flex; + flex-wrap: wrap; + gap: rem(0.8); + align-items: center; + + .enumLabel { + font-size: rem(1.4); + line-height: rem(1.4); + color: var(--ifm-color-emphasis-200); + } + + .enumType { + font-size: rem(1.4); + &.string { + color: var(--schema-string); + } + } + + .enumLabel, + .enumType { + display: flex; + justify-content: center; + align-items: center; + } + + .schemaEnums { + width: fit-content; + height: fit-content; + font-size: rem(1.4); + line-height: rem(1.4); + color: var(--ifm-color-success-light); + border: none; + padding: rem(0.6) rem(0.8); + border-radius: 4px; + background-color: rgba(0, 255, 104, 0.16); + } + } + } +} diff --git a/src/features/Apiexplorer/Schema/SchemaProperties/index.tsx b/src/features/Apiexplorer/Schema/SchemaProperties/index.tsx index 822ca8de..86e09895 100644 --- a/src/features/Apiexplorer/Schema/SchemaProperties/index.tsx +++ b/src/features/Apiexplorer/Schema/SchemaProperties/index.tsx @@ -32,6 +32,7 @@ const SchemaProperties = ({ jsonSchema }: TJsonSchemaType) => { is_open properties={jsonSchema.properties} value={jsonSchema.properties} + jsonSchema={jsonSchema} /> )} diff --git a/src/types/index.ts b/src/types/index.ts index f2ae5ece..53b5e649 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,4 +1,4 @@ -import { ApiToken } from '@deriv/api-types'; +import { ApiToken, StreamTypes } from '@deriv/api-types'; import { Column } from 'react-table'; export type TTokensArrayType = ApiToken['tokens']; @@ -15,3 +15,5 @@ export type TInfo = { auth_required?: number; auth_scopes?: string[]; }; + +export type TEnumStreamType = Array;