diff --git a/frontend/libs/console/legacy-ce/src/lib/features/ConnectDBRedesign/components/ConnectPostgresWidget/parts/DynamicDBRouting/ConnectPostgresModal.tsx b/frontend/libs/console/legacy-ce/src/lib/features/ConnectDBRedesign/components/ConnectPostgresWidget/parts/DynamicDBRouting/ConnectPostgresModal.tsx new file mode 100644 index 00000000000..850df125c96 --- /dev/null +++ b/frontend/libs/console/legacy-ce/src/lib/features/ConnectDBRedesign/components/ConnectPostgresWidget/parts/DynamicDBRouting/ConnectPostgresModal.tsx @@ -0,0 +1,130 @@ +import React from 'react'; +import z from 'zod'; + +import { Collapsible } from '../../../../../../new-components/Collapsible'; +import { Dialog } from '../../../../../../new-components/Dialog'; +import { schema } from '../../schema'; + +import { + InputField, + useConsoleForm, +} from '../../../../../../new-components/Form'; +import { areSSLSettingsEnabled } from '../../utils/helpers'; +import { DatabaseUrl } from '../DatabaseUrl'; +import { IsolationLevel } from '../IsolationLevel'; +import { PoolSettings } from '../PoolSettings'; +import { SslSettings } from '../SslSettings'; +import { UsePreparedStatements } from '../UsePreparedStatements'; + +interface ConnectPostgresModalProps { + alreadyUseNames?: string[]; + defaultValues?: z.infer; + onClose: () => void; + onSubmit: (values: z.infer) => void; +} + +export const ConnectPostgresModal = (props: ConnectPostgresModalProps) => { + const { onClose, onSubmit, defaultValues, alreadyUseNames } = props; + + const { + Form, + methods: { setError, formState, trigger, getValues }, + } = useConsoleForm({ + schema, + options: { + defaultValues: defaultValues ?? { + configuration: { + connectionInfo: { + databaseUrl: { + connectionType: 'databaseUrl', + }, + }, + }, + }, + }, + }); + + return ( +
{ + if (alreadyUseNames?.includes(values.name)) { + setError('name', { + type: 'manual', + message: 'This name is already in use', + }); + } else { + onSubmit(values); + } + }} + > + + <> +
+ + +
+ +
+ +
+ + Advanced Settings +
+ } + > + + + + {areSSLSettingsEnabled() && ( + + SSL Certificates Settings + + (Certificates will be loaded from{' '} + + environment variables + + ) + +
+ } + > + + + )} + + + + {}} + /> + +
+
+ ); +}; diff --git a/frontend/libs/console/legacy-ce/src/lib/features/ConnectDBRedesign/components/ConnectPostgresWidget/parts/DynamicDBRouting/DynamicDBRouting.stories.tsx b/frontend/libs/console/legacy-ce/src/lib/features/ConnectDBRedesign/components/ConnectPostgresWidget/parts/DynamicDBRouting/DynamicDBRouting.stories.tsx new file mode 100644 index 00000000000..5bfa9a99431 --- /dev/null +++ b/frontend/libs/console/legacy-ce/src/lib/features/ConnectDBRedesign/components/ConnectPostgresWidget/parts/DynamicDBRouting/DynamicDBRouting.stories.tsx @@ -0,0 +1,48 @@ +import { expect } from '@storybook/jest'; +import { ComponentStory, ComponentMeta } from '@storybook/react'; +import { userEvent, waitFor, within } from '@storybook/testing-library'; +import { handlers } from '../../../../../../mocks/metadata.mock'; +import { ReactQueryDecorator } from '../../../../../../storybook/decorators/react-query'; +import { DynamicDBRouting } from './DynamicDBRouting'; + +export default { + component: DynamicDBRouting, + decorators: [ReactQueryDecorator()], + parameters: { + msw: handlers({ delay: 500 }), + }, +} as ComponentMeta; + +export const Default: ComponentStory = () => ( + +); + +Default.play = async ({ args, canvasElement }) => { + const canvas = within(canvasElement); + + await waitFor(() => { + expect(canvas.getByLabelText('Database Tenancy')).toBeInTheDocument(); + }); + + // click on Database Tenancy + const radioTenancy = canvas.getByLabelText('Database Tenancy'); + userEvent.click(radioTenancy); + + // click on "Add Connection" + const buttonAddConnection = canvas.getByText('Add Connection'); + userEvent.click(buttonAddConnection); + + // write "test" in the input text with testid "name" + const inputName = canvas.getByTestId('name'); + userEvent.type(inputName, 'test'); + + // write "test" in the input text with testid "configuration.connectionInfo.databaseUrl.url" + const inputDatabaseUrl = canvas.getByTestId( + 'configuration.connectionInfo.databaseUrl.url' + ); + userEvent.type(inputDatabaseUrl, 'test'); + + // click on submit + const buttonSubmit = canvas.getByText('Submit'); + userEvent.click(buttonSubmit); +}; diff --git a/frontend/libs/console/legacy-ce/src/lib/features/ConnectDBRedesign/components/ConnectPostgresWidget/parts/DynamicDBRouting/DynamicDBRouting.tsx b/frontend/libs/console/legacy-ce/src/lib/features/ConnectDBRedesign/components/ConnectPostgresWidget/parts/DynamicDBRouting/DynamicDBRouting.tsx new file mode 100644 index 00000000000..ba9e23dc407 --- /dev/null +++ b/frontend/libs/console/legacy-ce/src/lib/features/ConnectDBRedesign/components/ConnectPostgresWidget/parts/DynamicDBRouting/DynamicDBRouting.tsx @@ -0,0 +1,126 @@ +import React from 'react'; +import z from 'zod'; + +import { SimpleForm } from '../../../../../../new-components/Form'; +import { ConnectPostgresModal } from './ConnectPostgresModal'; +import { DynamicDBRoutingForm } from './DynamicDBRoutingForm'; +import { generatePostgresRequestPayload } from '../../utils/generateRequests'; +import { adaptPostgresConnectionInfo } from '../../utils/adaptResponse'; +import { useDynamicDbRouting } from './hooks/useDynamicDbRouting'; +import { ConnectionSet } from '../../../../../../metadata/types'; +import { PostgresConfiguration } from '../../../../../hasura-metadata-types'; + +const schema = z.object({ + connection_template: z.string().optional(), +}); + +interface DynamicDBRoutingProps { + sourceName: string; +} + +export const DynamicDBRouting = (props: DynamicDBRoutingProps) => { + const { + connectionTemplate, + connectionSet, + addConnection, + removeConnection, + updateConnection, + updateConnectionTemplate, + isLoading, + isMetadaLoading, + } = useDynamicDbRouting({ + sourceName: props.sourceName, + }); + + const [isModalOpen, setIsModalOpen] = React.useState(false); + const [editingConnectionSetMember, setEditingConnectionSetMember] = + React.useState(); + + const connectionSetMembers = connectionSet.map(connection => { + const { name, connection_info } = connection; + return { + name, + configuration: { + connectionInfo: adaptPostgresConnectionInfo( + connection_info as PostgresConfiguration['connection_info'] + ), + }, + }; + }); + + if (isMetadaLoading) { + return null; + } + + console.log('template', connectionTemplate); + + return ( + <> + {isModalOpen && ( + connection.name + )} + onSubmit={values => { + const payload = { + name: values.name, + connection_info: generatePostgresRequestPayload({ + driver: 'postgres', + values, + }).details.configuration.connection_info, + } as ConnectionSet; + addConnection(payload); + setIsModalOpen(false); + }} + onClose={() => setIsModalOpen(false)} + /> + )} + {editingConnectionSetMember && ( + connection.name + )} + defaultValues={connectionSetMembers.find( + connection => connection.name === editingConnectionSetMember + )} + onSubmit={values => { + updateConnection(editingConnectionSetMember, { + name: values.name, + connection_info: generatePostgresRequestPayload({ + driver: 'postgres', + values, + }).details.configuration.connection_info, + } as ConnectionSet); + + setEditingConnectionSetMember(undefined); + }} + onClose={() => setEditingConnectionSetMember(undefined)} + /> + )} + { + updateConnectionTemplate(values.connection_template); + }} + schema={schema} + options={{ + defaultValues: { + connection_template: connectionTemplate ?? undefined, + }, + }} + > + setIsModalOpen(true)} + onEditConnection={connectionName => { + setEditingConnectionSetMember(connectionName); + }} + onRemoveConnection={connectionName => { + removeConnection(connectionName); + }} + isLoading={isLoading} + connectionTemplate={connectionTemplate} + /> + + + ); +}; diff --git a/frontend/libs/console/legacy-ce/src/lib/features/ConnectDBRedesign/components/ConnectPostgresWidget/parts/DynamicDBRouting/DynamicDBRoutingForm.tsx b/frontend/libs/console/legacy-ce/src/lib/features/ConnectDBRedesign/components/ConnectPostgresWidget/parts/DynamicDBRouting/DynamicDBRoutingForm.tsx new file mode 100644 index 00000000000..b7933cb38b3 --- /dev/null +++ b/frontend/libs/console/legacy-ce/src/lib/features/ConnectDBRedesign/components/ConnectPostgresWidget/parts/DynamicDBRouting/DynamicDBRoutingForm.tsx @@ -0,0 +1,251 @@ +import React from 'react'; +import { useFormContext } from 'react-hook-form'; +import { FaExclamationCircle, FaPlusCircle } from 'react-icons/fa'; +import z from 'zod'; +import { Badge } from '../../../../../../new-components/Badge'; +import { Button } from '../../../../../../new-components/Button'; +import { CardedTable } from '../../../../../../new-components/CardedTable'; +import { CardRadioGroup } from '../../../../../../new-components/CardRadioGroup'; +import { schema as postgresSchema } from '../../schema'; +import { CodeEditorField } from '../../../../../../new-components/Form'; +import { LearnMoreLink } from '../../../../../../new-components/LearnMoreLink'; +import { IconTooltip } from '../../../../../../new-components/Tooltip'; + +const editorOptions = { + minLines: 33, + maxLines: 33, + showLineNumbers: true, + useSoftTabs: true, + showPrintMargin: false, + showGutter: true, + wrap: true, +}; + +const templates = { + disabled: { + value: 'disabled', + title: 'Disabled', + body: 'Use default Hasura connection routing.', + template: '', + isSelected: (connectionTemplate?: string | null) => !connectionTemplate, + }, + tenancy: { + value: 'tenancy', + title: 'Database Tenancy', + body: 'Tenancy template using a x-hasura-tenant variable to connect to a named database.', + template: `{{ if ($.request.session.x-hasura-tenant-id == "my_tenant_1")}} + {{$.connection_set.my_tenant_1_connection}} +{{ elif ($.request.session.x-hasura-tenant-id == "my_tenant_2")}} + {{$.connection_set.my_tenant_2_connection}} +{{ else }} + {{$.default}} +{{ end }}`, + isSelected: (connectionTemplate?: string | null) => + connectionTemplate?.includes('x-hasura-tenant-id'), + }, + 'no-stale-reads': { + value: 'no-stale-reads', + title: 'Read Replicas - No Stale Reads', + body: 'No stale reads template using the primary for reads on mutations and replicas for all other reads.', + template: `{{ if (($.request.query.operation_type == "query") +|| ($.request.query.operation_type == "subscription")) +&& ($.request.headers.x-query-read-no-stale == "true") }} + {{$.primary}} +{{ else }} + {{$.default}} +{{ end }}`, + + isSelected: (connectionTemplate?: string | null) => + connectionTemplate?.includes('x-query-read-no-stale'), + }, + sharding: { + value: 'sharding', + title: 'Different credentials', + body: 'Route specific queries to specific databases in a distributed database system model.', + template: `{{ if ($.request.session.x-hasura-role == "manager")}} + {{$.connection_set.manager_connection}} +{{ elif ($.request.session.x-hasura-role == "employee")}} + {{$.connection_set.employee_connection}} +{{ else }} + {{$.default}} +{{ end }}`, + isSelected: (connectionTemplate?: string | null) => + connectionTemplate?.includes('x-hasura-role'), + }, + custom: { + value: 'custom', + title: 'Custom Template', + body: 'Write a custom connection template using Kriti templating.', + template: `{{ if ()}} + {{$.}} +{{ elif ()}} + {{$.}} +{{ else }} + {{$.default}} +{{ end }}`, + isSelected: (connectionTemplate?: string | null) => !!connectionTemplate, + }, +}; + +interface DynamicDBRoutingFormProps { + connectionSetMembers: z.infer[]; + onAddConnection: () => void; + onRemoveConnection: (name: string) => void; + onEditConnection: (name: string) => void; + isLoading: boolean; + connectionTemplate?: string | null; +} + +export const DynamicDBRoutingForm = (props: DynamicDBRoutingFormProps) => { + const { + connectionSetMembers, + onAddConnection, + onRemoveConnection, + onEditConnection, + isLoading, + connectionTemplate, + } = props; + const { setValue } = useFormContext(); + const [template, setTemplate] = React.useState( + (Object.entries(templates).find(([_, template]) => + template.isSelected(connectionTemplate) + )?.[0] as keyof typeof templates) || 'disabled' + ); + + return ( +
+
+
+ {template !== 'disabled' && ( +
+ +
+ Dynamic Database Routing Precedence +

+ {' '} + Dynamic database routing takes precedence over read replicas. + You may use both read replica routing and default database + routing in your connection template. +

+
+
+ )} +
+ + + +
+
+ Database connection template to define dynamic connection routing. +
+
+
+ { + setTemplate(value as keyof typeof templates); + setValue( + 'connection_template', + templates[value as keyof typeof templates]?.template + ); + }} + items={Object.values(templates).map(template => ({ + value: template.value, + title: template.title, + body: template.body, + }))} + /> +
+ +
+
+
+
+
+
+ + + +
+
+ Available connections which can be referenced in your dynamic + connection template.{' '} +
+
+ + +
+
+ Default Routing Behavior, + ], + [ + '{{$.primary}}', + The Database Primary, + ], + [ + '{{$.read_replicas}}', + Read Replica Routing, + ], + ...connectionSetMembers.map(connection => [ + `{{$.${connection.name}}}`, + <> + + + , + ]), + ]} + /> +
+
+ +
+
+ ); +}; diff --git a/frontend/libs/console/legacy-ce/src/lib/features/ConnectDBRedesign/components/ConnectPostgresWidget/parts/DynamicDBRouting/hooks/useDynamicDbRouting.ts b/frontend/libs/console/legacy-ce/src/lib/features/ConnectDBRedesign/components/ConnectPostgresWidget/parts/DynamicDBRouting/hooks/useDynamicDbRouting.ts new file mode 100644 index 00000000000..82e1aed8e48 --- /dev/null +++ b/frontend/libs/console/legacy-ce/src/lib/features/ConnectDBRedesign/components/ConnectPostgresWidget/parts/DynamicDBRouting/hooks/useDynamicDbRouting.ts @@ -0,0 +1,134 @@ +import { ConnectionSet } from '../../../../../../../metadata/types'; +import { + useMetadata, + useMetadataMigration, +} from '../../../../../../MetadataAPI'; + +export const useDynamicDbRouting = ({ sourceName }: { sourceName: string }) => { + const { data, isLoading: isMetadaLoading } = useMetadata(); + + const { mutate, isLoading } = useMetadataMigration({}); + + const source = data?.metadata?.sources.find( + source => source.name === sourceName + ); + + const connectionTemplate = + source?.configuration?.connection_template?.template || null; + const connectionSet = source?.configuration?.connection_set || []; + + const addConnection = async ( + connection: ConnectionSet, + options?: Parameters[1] + ): Promise => { + mutate( + { + query: { + ...(data?.resource_version && { + resource_version: data.resource_version, + }), + type: 'pg_update_source', + args: { + ...source, + configuration: { + ...source?.configuration, + connection_set: [...connectionSet, connection], + }, + }, + }, + }, + options + ); + }; + + const removeConnection = async ( + connectionName: string, + options?: Parameters[1] + ): Promise => { + mutate( + { + query: { + ...(data?.resource_version && { + resource_version: data.resource_version, + }), + type: 'pg_update_source', + args: { + ...source, + configuration: { + ...source?.configuration, + connection_set: + connectionSet.length === 1 + ? null + : connectionSet.filter(c => c.name !== connectionName), + }, + }, + }, + }, + options + ); + }; + + const updateConnection = async ( + connectionName: string, + connection: ConnectionSet, + options?: Parameters[1] + ): Promise => { + mutate( + { + query: { + ...(data?.resource_version && { + resource_version: data.resource_version, + }), + type: 'pg_update_source', + args: { + ...source, + configuration: { + ...source?.configuration, + connection_set: connectionSet.map(c => + c.name === connectionName ? connection : c + ), + }, + }, + }, + }, + options + ); + }; + + const updateConnectionTemplate = async ( + connectionTemplate?: string | null, + options?: Parameters[1] + ): Promise => { + mutate( + { + query: { + ...(data?.resource_version && { + resource_version: data.resource_version, + }), + type: 'pg_update_source', + args: { + ...source, + configuration: { + ...source?.configuration, + connection_template: connectionTemplate + ? { template: connectionTemplate } + : null, + }, + }, + }, + }, + options + ); + }; + + return { + connectionTemplate, + connectionSet, + addConnection, + removeConnection, + updateConnection, + updateConnectionTemplate, + isLoading, + isMetadaLoading, + }; +}; diff --git a/frontend/libs/console/legacy-ce/src/lib/features/ConnectDBRedesign/components/ConnectPostgresWidget/parts/DynamicDBRouting/index.ts b/frontend/libs/console/legacy-ce/src/lib/features/ConnectDBRedesign/components/ConnectPostgresWidget/parts/DynamicDBRouting/index.ts new file mode 100644 index 00000000000..e2011ee2733 --- /dev/null +++ b/frontend/libs/console/legacy-ce/src/lib/features/ConnectDBRedesign/components/ConnectPostgresWidget/parts/DynamicDBRouting/index.ts @@ -0,0 +1 @@ +export * from './DynamicDBRouting'; diff --git a/frontend/libs/console/legacy-ce/src/lib/features/Data/index.tsx b/frontend/libs/console/legacy-ce/src/lib/features/Data/index.tsx index 66e4889d163..bd71e2e1fc0 100644 --- a/frontend/libs/console/legacy-ce/src/lib/features/Data/index.tsx +++ b/frontend/libs/console/legacy-ce/src/lib/features/Data/index.tsx @@ -6,3 +6,4 @@ export { useTrackTable } from './TrackTables/hooks/useTrackTable'; export { useMetadataSource } from './TrackTables/hooks/useMetadataSource'; export * from './CustomFieldNames'; export * from '../../utils/getDataRoute'; +export * from './mocks/metadata.mocks'; diff --git a/frontend/libs/console/legacy-ce/src/lib/features/Data/mocks/metadata.mocks.ts b/frontend/libs/console/legacy-ce/src/lib/features/Data/mocks/metadata.mocks.ts new file mode 100644 index 00000000000..b3bbe811cfc --- /dev/null +++ b/frontend/libs/console/legacy-ce/src/lib/features/Data/mocks/metadata.mocks.ts @@ -0,0 +1,61 @@ +import produce from 'immer'; + +import { allowedMetadataTypes } from '../../MetadataAPI'; +import { Metadata } from '../../hasura-metadata-types'; +import { MetadataReducer } from '../../../mocks/actions'; + +export const dataInitialData: Partial = { + sources: [ + { + name: 'default', + kind: 'postgres', + tables: [], + configuration: { + connection_info: { + database_url: { + from_env: 'HASURA_GRAPHQL_DATABASE_URL', + }, + isolation_level: 'read-committed', + pool_settings: { + connection_lifetime: 600, + idle_timeout: 180, + max_connections: 50, + retries: 1, + }, + use_prepared_statements: true, + }, + }, + }, + ], +}; + +export const metadataHandlers: Partial< + Record +> = { + pg_update_source: (state, action) => { + const { name, configuration } = action.args as { + name: string; + configuration: Record; + }; + const existingSource = state.metadata.sources.find(s => s.name === name); + if (!existingSource) { + return { + status: 400, + error: { + path: '$.args.name', + error: `source with name "${name}" does not exist`, + code: 'not-exists', + }, + }; + } + return produce(state, draft => { + const source = draft.metadata.sources.find(s => s.name === name); + if (source?.configuration) { + source.configuration = { + ...source?.configuration, + ...configuration, + }; + } + }); + }, +}; diff --git a/frontend/libs/console/legacy-ce/src/lib/features/hasura-metadata-types/source/configuration.ts b/frontend/libs/console/legacy-ce/src/lib/features/hasura-metadata-types/source/configuration.ts index a523c1e614c..a543360c6ba 100644 --- a/frontend/libs/console/legacy-ce/src/lib/features/hasura-metadata-types/source/configuration.ts +++ b/frontend/libs/console/legacy-ce/src/lib/features/hasura-metadata-types/source/configuration.ts @@ -1,39 +1,54 @@ export type FromEnv = { from_env: string }; type ValidJson = Record; -export interface PostgresConfiguration { - connection_info: { - database_url: - | string - | FromEnv - | { - username: string; - password?: string; - database: string; - host: string; - port: string; - }; - pool_settings?: { - max_connections?: number; - total_max_connections?: number; - idle_timeout?: number; - retries?: number; - pool_timeout?: number; - connection_lifetime?: number; - }; - use_prepared_statements?: boolean; - /** - * The transaction isolation level in which the queries made to the source will be run with (default: read-committed). - */ - isolation_level?: 'read-committed' | 'repeatable-read' | 'serializable'; - ssl_configuration?: { - sslmode: string; - sslrootcert: FromEnv; - sslcert: FromEnv; - sslkey: FromEnv; - sslpassword: FromEnv; - }; +interface PostgresConnectionInfo { + database_url: + | string + | FromEnv + | { + username: string; + password?: string; + database: string; + host: string; + port: string; + }; + pool_settings?: { + max_connections?: number; + total_max_connections?: number; + idle_timeout?: number; + retries?: number; + pool_timeout?: number; + connection_lifetime?: number; }; + use_prepared_statements?: boolean; + /** + * The transaction isolation level in which the queries made to the source will be run with (default: read-committed). + */ + isolation_level?: 'read-committed' | 'repeatable-read' | 'serializable'; + ssl_configuration?: { + sslmode: string; + sslrootcert: FromEnv; + sslcert: FromEnv; + sslkey: FromEnv; + sslpassword: FromEnv; + }; +} +export interface PostgresConfiguration { + connection_info: PostgresConnectionInfo; + /** + * Kriti template to resolve connection info at runtime + */ + connection_template?: { + template: string; + }; + /** + * List of connection sets to use in connection template + */ + connection_set?: { + name: string; + connection_info: PostgresConnectionInfo; + }[]; + /** * Optional list of read replica configuration (supported only in cloud/enterprise versions) */ diff --git a/frontend/libs/console/legacy-ce/src/lib/metadata/types.ts b/frontend/libs/console/legacy-ce/src/lib/metadata/types.ts index 8b128b860fc..b2e5490ce25 100644 --- a/frontend/libs/console/legacy-ce/src/lib/metadata/types.ts +++ b/frontend/libs/console/legacy-ce/src/lib/metadata/types.ts @@ -1157,11 +1157,14 @@ type GraphQLCustomizationMetadata = { }; // Used for Dynamic Connection Routing -type ConnectionSet = { +export type ConnectionSet = { connection_info: SourceConnectionInfo; name: string; }; +export type SourceConnectionTemplate = { + template: string | null; +}; export interface MetadataDataSource { name: string; kind: @@ -1177,6 +1180,7 @@ export interface MetadataDataSource { extensions_schema?: string; // pro-only feature read_replicas?: SourceConnectionInfo[]; + connection_template?: SourceConnectionTemplate; connection_set?: ConnectionSet[]; service_account?: BigQueryServiceAccount; global_select_limit?: number; diff --git a/frontend/libs/console/legacy-ce/src/lib/mocks/actions.ts b/frontend/libs/console/legacy-ce/src/lib/mocks/actions.ts index 7d8d19067da..407413f8d16 100644 --- a/frontend/libs/console/legacy-ce/src/lib/mocks/actions.ts +++ b/frontend/libs/console/legacy-ce/src/lib/mocks/actions.ts @@ -5,6 +5,7 @@ import { metadataHandlers as allowListMetadataHandlers } from '../features/Allow import { metadataHandlers as adhocEventMetadataHandlers } from '../features/AdhocEvents'; import { metadataHandlers as queryCollectionMetadataHandlers } from '../features/QueryCollections'; import { metadataHandlers as openTelemetryMetadataHandlers } from '../features/OpenTelemetry'; +import { metadataHandlers as dataMetadataHandlers } from '../features/Data'; import { TMigration } from '../features/MetadataAPI/hooks/useMetadataMigration'; @@ -32,6 +33,7 @@ const metadataHandlers: Partial> = ...queryCollectionMetadataHandlers, ...adhocEventMetadataHandlers, ...openTelemetryMetadataHandlers, + ...dataMetadataHandlers, }; export const metadataReducer: MetadataReducer = (state, action) => { diff --git a/frontend/libs/console/legacy-ce/src/lib/mocks/metadata.mock.ts b/frontend/libs/console/legacy-ce/src/lib/mocks/metadata.mock.ts index 94a36914cf9..12ed5607cec 100644 --- a/frontend/libs/console/legacy-ce/src/lib/mocks/metadata.mock.ts +++ b/frontend/libs/console/legacy-ce/src/lib/mocks/metadata.mock.ts @@ -5,6 +5,7 @@ import type { Metadata } from '../features/hasura-metadata-types'; import { allowListInitialData } from '../features/AllowLists'; import { queryCollectionInitialData } from '../features/QueryCollections'; import { openTelemetryInitialData } from '../features/OpenTelemetry'; +import { dataInitialData } from '../features/Data'; import { rest } from 'msw'; import { metadataReducer } from './actions'; @@ -18,6 +19,7 @@ export const createDefaultInitialData = (): Metadata => ({ ...allowListInitialData, ...queryCollectionInitialData, ...openTelemetryInitialData, + ...dataInitialData, }, }); diff --git a/frontend/libs/console/legacy-ce/src/lib/new-components/Badge/Badge.tsx b/frontend/libs/console/legacy-ce/src/lib/new-components/Badge/Badge.tsx index 8ea870c8e54..282cd65eca2 100644 --- a/frontend/libs/console/legacy-ce/src/lib/new-components/Badge/Badge.tsx +++ b/frontend/libs/console/legacy-ce/src/lib/new-components/Badge/Badge.tsx @@ -8,7 +8,8 @@ export type BadgeColor = | 'indigo' | 'gray' | 'blue' - | 'purple'; + | 'purple' + | 'light-gray'; interface BadgeProps extends React.ComponentProps<'span'> { /** * The color of the basge @@ -24,6 +25,7 @@ const badgeClassnames: Record = { indigo: 'bg-indigo-100 text-indigo-800', blue: 'bg-blue-100 text-blue-800', purple: 'bg-purple-100 text-purple-800', + 'light-gray': 'bg-gray-100 text-gray-800', }; export const Badge: React.FC> = ({