mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 17:02:49 +03:00
frontend: Schema registry UI improvements
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/9479 GitOrigin-RevId: 306df780cfb9cc39a335abd98706ae2fe4293700
This commit is contained in:
parent
c3527e3c98
commit
db1b6cf424
@ -47,14 +47,17 @@ const Sidebar: React.FC<SidebarProps> = ({ location, metadata }) => {
|
||||
items: [],
|
||||
};
|
||||
|
||||
if (isCloudConsole(globals)) {
|
||||
if (
|
||||
isCloudConsole(globals) &&
|
||||
(globals.userRole === 'admin' || globals.userRole === 'owner')
|
||||
) {
|
||||
sectionsData.graphql = {
|
||||
key: 'graphql',
|
||||
label: 'GraphQL',
|
||||
items: [
|
||||
{
|
||||
key: 'schema-registry',
|
||||
label: 'Schema Registry',
|
||||
label: 'Schema Registry (Beta)',
|
||||
route: '/settings/schema-registry',
|
||||
dataTestVal: 'metadata-schema-registry-link',
|
||||
},
|
||||
|
@ -3,11 +3,26 @@ import { SchemasList } from './SchemasList';
|
||||
import { FeatureRequest } from './FeatureRequest';
|
||||
import globals from '../../../Globals';
|
||||
import { SCHEMA_REGISTRY_FEATURE_NAME } from '../constants';
|
||||
import { Badge } from '../../../new-components/Badge';
|
||||
import { SCHEMA_REGISTRY_REF_URL } from '../constants';
|
||||
|
||||
const Header: React.VFC = () => {
|
||||
return (
|
||||
<div className="flex w-full">
|
||||
<h1 className="text-xl font-semibold">GraphQL Schema Registry</h1>
|
||||
<div className="flex flex-col w-full">
|
||||
<div className="flex w-full mb-sm">
|
||||
<h1 className="text-xl font-semibold">GraphQL Schema Registry</h1>
|
||||
<Badge className="mx-2" color="blue">
|
||||
BETA
|
||||
</Badge>
|
||||
</div>
|
||||
<a
|
||||
className="text-muted w-auto"
|
||||
href={SCHEMA_REGISTRY_REF_URL}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
What is Schema Registry?
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,12 +1,25 @@
|
||||
import React from 'react';
|
||||
import { ChangeLevel } from '../types';
|
||||
import { FaExclamationTriangle } from 'react-icons/fa';
|
||||
import { IconTooltip } from '../../../new-components/Tooltip';
|
||||
|
||||
export const CountLabel: React.VFC<{
|
||||
count: number;
|
||||
count?: number;
|
||||
type: ChangeLevel;
|
||||
}> = props => {
|
||||
const { count, type } = props;
|
||||
|
||||
if (count === undefined) {
|
||||
return (
|
||||
<IconTooltip
|
||||
icon={<FaExclamationTriangle className="pr-2 text-xl text-secondary" />}
|
||||
message={
|
||||
'Could not compute changes with respect to previous schema for this role. A previous schema for this role might not exist or one of the GraphQL schemas could be erroneous.'
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
let textColor = 'text-green-500';
|
||||
let backgroundColor = 'bg-green-100';
|
||||
|
||||
|
@ -3,6 +3,7 @@ import { Button } from '../../../new-components/Button';
|
||||
import { GraphQLError } from 'graphql';
|
||||
import { hasuraToast } from '../../../new-components/Toasts';
|
||||
import { useSubmitSchemaRegistryFeatureRequest } from '../hooks/useSubmitSchemaRegistryFeatureRequest';
|
||||
import { SCHEMA_REGISTRY_REF_URL } from '../constants';
|
||||
|
||||
export const FeatureRequest = () => {
|
||||
const onSuccess = () => {
|
||||
@ -46,7 +47,11 @@ export const FeatureRequest = () => {
|
||||
schema changes more reliable, prevent breaking changes in your schema
|
||||
and make collaboration across large teams, micro services and roles much
|
||||
more manageable and predictable.{' '}
|
||||
<a href="" target="_blank" rel="noreferrer noopener">
|
||||
<a
|
||||
href={SCHEMA_REGISTRY_REF_URL}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
{' '}
|
||||
Read more.
|
||||
</a>
|
||||
|
@ -1,11 +1,15 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import LZString from 'lz-string';
|
||||
import moment from 'moment';
|
||||
import { useGetSchema } from '../hooks/useGetSchema';
|
||||
import { Tabs } from '../../../new-components/Tabs';
|
||||
import { IconTooltip } from '../../../new-components/Tooltip';
|
||||
import { SchemaRow } from './SchemaRow';
|
||||
import { ChangeSummary } from './ChangeSummary';
|
||||
import { FindIfSubStringExists, schemaTransformFn } from '../utils';
|
||||
import {
|
||||
FindIfSubStringExists,
|
||||
schemaTransformFn,
|
||||
getPublishTime,
|
||||
} from '../utils';
|
||||
import { Link } from 'react-router';
|
||||
import { RoleBasedSchema, Schema } from '../types';
|
||||
import { FaHome, FaAngleRight, FaFileImport, FaSearch } from 'react-icons/fa';
|
||||
@ -62,14 +66,13 @@ const SchemasDetails: React.VFC<{
|
||||
|
||||
// We know for sure only one roleBasedSchema exists
|
||||
const roleBasedSchema = schema.roleBasedSchemas[0];
|
||||
const published = moment(schema.created_at);
|
||||
|
||||
return (
|
||||
<div className="mx-4 mt-4">
|
||||
<Breadcrumbs />
|
||||
<div className="flex mb-sm">
|
||||
<span className="font-bold text-xl text-black mr-4">
|
||||
{roleBasedSchema.role}:{schema.entry_hash.substr(0, 16)}
|
||||
{roleBasedSchema.role}:{schema.entry_hash}
|
||||
</span>
|
||||
</div>
|
||||
<div className="border-neutral-200 bg-white border w-3/5">
|
||||
@ -87,16 +90,22 @@ const SchemasDetails: React.VFC<{
|
||||
<div className="ml-4 mb-2 ">
|
||||
<SchemaRow
|
||||
role={roleBasedSchema.role || ''}
|
||||
changes={roleBasedSchema.changes || []}
|
||||
changes={roleBasedSchema.changes}
|
||||
/>
|
||||
|
||||
<div className="flex mt-4">
|
||||
<div className="flex-col w-1/4">
|
||||
<div className="font-bold text-gray-500">Published</div>
|
||||
<span>{published.fromNow()}</span>
|
||||
<div className="flex-col w-1/2">
|
||||
<div className="flex items-center">
|
||||
<p className="font-bold text-gray-500">Published</p>
|
||||
<IconTooltip message="The time at which this GraphQL schema was generated" />
|
||||
</div>
|
||||
<span>{getPublishTime(schema.created_at)}</span>
|
||||
</div>
|
||||
<div className="flex-col w-3/4">
|
||||
<div className="font-bold text-gray-500">Hash</div>
|
||||
<div className="flex-col w-1/2">
|
||||
<div className="flex items-center">
|
||||
<p className="font-bold text-gray-500">Schema Hash</p>
|
||||
<IconTooltip message="Hash of the GraphQL Schema SDL. Hash for two identical schema is identical." />
|
||||
</div>
|
||||
<span className="font-bold bg-gray-100 px-1 rounded text-sm">
|
||||
{roleBasedSchema.hash}
|
||||
</span>
|
||||
@ -118,7 +127,12 @@ const SchemasDetails: React.VFC<{
|
||||
{
|
||||
value: 'changes',
|
||||
label: 'Changes',
|
||||
content: <ChangesView changes={roleBasedSchema.changes} />,
|
||||
content: (
|
||||
<ChangesView
|
||||
changes={roleBasedSchema.changes}
|
||||
role={roleBasedSchema.role}
|
||||
/>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
@ -149,8 +163,9 @@ export const SchemaView: React.VFC<{ schema: string }> = props => {
|
||||
|
||||
export const ChangesView: React.VFC<{
|
||||
changes: RoleBasedSchema['changes'];
|
||||
role: string;
|
||||
}> = props => {
|
||||
const { changes } = props;
|
||||
const { changes, role } = props;
|
||||
|
||||
const [searchText, setSearchText] = React.useState('');
|
||||
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
@ -165,11 +180,27 @@ export const ChangesView: React.VFC<{
|
||||
}, [searchText, changes]);
|
||||
|
||||
if (!changes) {
|
||||
return <span>Could not compute what changed in this GraphQL Schema</span>;
|
||||
return (
|
||||
<div className="p-4">
|
||||
<span className="text-muted">
|
||||
Could not compute changes in this GraphQL schema with respect to the
|
||||
previous schema for role <b>{role}</b>. This typically happens if
|
||||
there is no previous schema for role <b>{role}</b> or if your GraphQL
|
||||
schema is erroneous.
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!changes.length) {
|
||||
return <span className="ml-4">No changes!</span>;
|
||||
return (
|
||||
<div className="p-4">
|
||||
<span className="text-muted">
|
||||
No changes in this GraphQL schema with respect to the previous schema
|
||||
for role <b>{role}</b>.
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const breakingChanges =
|
||||
@ -182,6 +213,12 @@ export const ChangesView: React.VFC<{
|
||||
|
||||
return (
|
||||
<div className="flex-col m-8">
|
||||
<div className="mb-8">
|
||||
<p className="text-muted">
|
||||
These changes are with respect to the previous schema for role{' '}
|
||||
<b>{role}</b>.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex-col">
|
||||
<div className="font-bold text-md mb-8 text-gray-500">
|
||||
Change Summary
|
||||
|
@ -1,36 +1,45 @@
|
||||
import React from 'react';
|
||||
import { CountLabel } from './CountLabel';
|
||||
import { CapitalizeFirstLetter } from '../utils';
|
||||
import { SchemaChange } from '../types';
|
||||
import { FaChevronRight } from 'react-icons/fa';
|
||||
|
||||
export const SchemaRow: React.VFC<{
|
||||
role: string;
|
||||
changes: SchemaChange[];
|
||||
changes?: SchemaChange[];
|
||||
onClick?: VoidFunction;
|
||||
}> = props => {
|
||||
const { role, changes, onClick } = props;
|
||||
|
||||
const countBreakingChanges = changes?.filter(
|
||||
c => c.criticality.level === 'BREAKING'
|
||||
).length;
|
||||
)?.length;
|
||||
const countDangerousChanges = changes?.filter(
|
||||
c => c.criticality.level === 'DANGEROUS'
|
||||
).length;
|
||||
)?.length;
|
||||
const countSafeChanges = changes?.filter(
|
||||
c => c.criticality.level === 'NON_BREAKING'
|
||||
).length;
|
||||
)?.length;
|
||||
return (
|
||||
<div className="w-full flex my-2">
|
||||
<div className="flex text-base w-[72%] justify-start">
|
||||
<span className="text-sm font-bold bg-gray-100 p-1 rounded">
|
||||
{CapitalizeFirstLetter(role)}
|
||||
{role}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex text-base w-[28%] justify-between">
|
||||
<CountLabel count={countBreakingChanges} type="BREAKING" />
|
||||
<CountLabel count={countDangerousChanges} type="DANGEROUS" />
|
||||
<CountLabel count={countSafeChanges} type="NON_BREAKING" />
|
||||
{changes ? (
|
||||
<>
|
||||
<CountLabel count={countBreakingChanges || 0} type="BREAKING" />
|
||||
<CountLabel count={countDangerousChanges || 0} type="DANGEROUS" />
|
||||
<CountLabel count={countSafeChanges || 0} type="NON_BREAKING" />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<CountLabel count={countBreakingChanges} type="BREAKING" />
|
||||
<CountLabel count={countDangerousChanges} type="DANGEROUS" />
|
||||
<CountLabel count={countSafeChanges} type="NON_BREAKING" />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{onClick && (
|
||||
<div
|
||||
|
@ -1,11 +1,12 @@
|
||||
import React from 'react';
|
||||
import { SchemaRow } from './SchemaRow';
|
||||
import { useGetSchemaList } from '../hooks/useGetSchemaList';
|
||||
import { Button } from '../../../new-components/Button';
|
||||
import { Schema, RoleBasedSchema } from '../types';
|
||||
import moment from 'moment';
|
||||
import { browserHistory } from 'react-router';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import _push from '../../../components/Services/Data/push';
|
||||
import globals from '../../../Globals';
|
||||
import { schemaListTransformFn } from '../utils';
|
||||
import { schemaListTransformFn, getPublishTime } from '../utils';
|
||||
|
||||
export const SchemasList = () => {
|
||||
const projectID = globals.hasuraCloudProjectId || '';
|
||||
@ -21,13 +22,25 @@ export const SchemasList = () => {
|
||||
case 'success': {
|
||||
const schemaList = schemaListTransformFn(fetchSchemaResponse.response);
|
||||
|
||||
return <Tabularised schemas={schemaList} />;
|
||||
return (
|
||||
<Tabularised
|
||||
schemas={schemaList}
|
||||
loadMore={fetchSchemaResponse.loadMore}
|
||||
isLoadingMore={fetchSchemaResponse.isLoadingMore}
|
||||
shouldLoadMore={fetchSchemaResponse.shouldLoadMore}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const Tabularised: React.VFC<{ schemas: Schema[] }> = props => {
|
||||
const { schemas } = props;
|
||||
export const Tabularised: React.VFC<{
|
||||
schemas: Schema[];
|
||||
loadMore: VoidFunction;
|
||||
isLoadingMore: boolean;
|
||||
shouldLoadMore: boolean;
|
||||
}> = props => {
|
||||
const { schemas, loadMore, isLoadingMore, shouldLoadMore } = props;
|
||||
return (
|
||||
<div className="overflow-x-auto rounded-sm border-neutral-200 bg-gray-100 border w-3/5">
|
||||
<div className="w-full flex bg-gray-100 px-4 py-2">
|
||||
@ -43,16 +56,32 @@ export const Tabularised: React.VFC<{ schemas: Schema[] }> = props => {
|
||||
</div>
|
||||
<div className="flex flex-col w-full">
|
||||
{schemas.length ? (
|
||||
schemas.map(schema => {
|
||||
return (
|
||||
<SchemaCard
|
||||
createdAt={schema.created_at}
|
||||
schemaId={schema.id}
|
||||
hash={schema.hash}
|
||||
roleBasedSchemas={schema.roleBasedSchemas}
|
||||
/>
|
||||
);
|
||||
})
|
||||
<div className="mb-md">
|
||||
{schemas.map(schema => {
|
||||
return (
|
||||
<SchemaCard
|
||||
createdAt={schema.created_at}
|
||||
schemaId={schema.id}
|
||||
hash={schema.entry_hash}
|
||||
roleBasedSchemas={schema.roleBasedSchemas}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{shouldLoadMore && (
|
||||
<div className="flex w-full justify-center items-center">
|
||||
<Button
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
loadMore();
|
||||
}}
|
||||
isLoading={isLoadingMore}
|
||||
disabled={isLoadingMore}
|
||||
>
|
||||
Load More
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="white border-t border-neutral-200">
|
||||
<div className="p-xs" data-test="label-no-domain-found">
|
||||
@ -73,17 +102,17 @@ const SchemaCard: React.VFC<{
|
||||
}> = props => {
|
||||
const { createdAt, hash, roleBasedSchemas } = props;
|
||||
|
||||
const published = moment(createdAt);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
return (
|
||||
<div className="w-full flex-col px-4 py-2 mb-2 bg-white">
|
||||
<div className="flex flex-col w-1/2">
|
||||
<div className="flex flex-col w-full">
|
||||
<div className="flex mt-4">
|
||||
<div className="flex-col w-1/4">
|
||||
<div className="flex-col w-1/2">
|
||||
<div className="font-bold text-gray-500">Published</div>
|
||||
<span>{published.fromNow()}</span>
|
||||
<span>{getPublishTime(createdAt)}</span>
|
||||
</div>
|
||||
<div className="flex-col w-3/4">
|
||||
<div className="flex-col w-1/2">
|
||||
<div className="font-bold text-gray-500">Hash</div>
|
||||
<span className="font-bold bg-gray-100 px-1 rounded text-sm">
|
||||
{hash}
|
||||
@ -93,16 +122,17 @@ const SchemaCard: React.VFC<{
|
||||
</div>
|
||||
|
||||
<div className="flex-col w-full mt-8">
|
||||
<div className="font-bold text-gray-500">Roles</div>
|
||||
{roleBasedSchemas.length ? (
|
||||
roleBasedSchemas.map((roleBasedSchema, index) => {
|
||||
return (
|
||||
<div className="flex-col w-full">
|
||||
<SchemaRow
|
||||
role={roleBasedSchema.role || ''}
|
||||
changes={roleBasedSchema.changes || []}
|
||||
changes={roleBasedSchema.changes}
|
||||
onClick={() => {
|
||||
browserHistory.push(
|
||||
`${globals.urlPrefix}/settings/schema-registry/${roleBasedSchema.id}`
|
||||
dispatch(
|
||||
_push(`/settings/schema-registry/${roleBasedSchema.id}`)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
@ -7,3 +7,8 @@ export const FETCH_REGISTRY_SCHEMA_QUERY_NAME =
|
||||
export const SCHEMA_REGISTRY_REFRESH_TIME = 5 * 60 * 1000;
|
||||
|
||||
export const SCHEMA_REGISTRY_FEATURE_NAME = 'GraphQLSchemaRegistry';
|
||||
|
||||
export const SCHEMA_REGISTRY_REF_URL =
|
||||
'https://github.com/hasura/graphql-engine/issues/9574';
|
||||
|
||||
export const SCHEMA_LIST_FETCH_BATCH_SIZE = 10;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { schemaRegsitryControlPlaneClient } from '../utils';
|
||||
import { FETCH_REGSITRY_SCHEMA_QUERY } from '../queries';
|
||||
import { FETCH_REGISTRY_SCHEMA_QUERY } from '../queries';
|
||||
import { GetRegistrySchemaResponseWithError } from '../types';
|
||||
import {
|
||||
FETCH_REGISTRY_SCHEMA_QUERY_NAME,
|
||||
@ -26,7 +26,7 @@ export const useGetSchema = (schemaId: string): FetchSchemaResponse => {
|
||||
return schemaRegsitryControlPlaneClient.query<
|
||||
GetRegistrySchemaResponseWithError,
|
||||
{ schemaId: string }
|
||||
>(FETCH_REGSITRY_SCHEMA_QUERY, {
|
||||
>(FETCH_REGISTRY_SCHEMA_QUERY, {
|
||||
schemaId: schemaId,
|
||||
});
|
||||
};
|
||||
|
@ -1,11 +1,12 @@
|
||||
import * as React from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { FETCH_REGSITRY_SCHEMAS_QUERY } from '../queries';
|
||||
import { FETCH_REGISTRY_SCHEMAS_QUERY } from '../queries';
|
||||
import { schemaRegsitryControlPlaneClient } from '../utils';
|
||||
import { GetSchemaListResponseWithError } from '../types';
|
||||
import {
|
||||
FETCH_REGISTRY_SCHEMAS_QUERY_NAME,
|
||||
SCHEMA_REGISTRY_REFRESH_TIME,
|
||||
SCHEMA_LIST_FETCH_BATCH_SIZE,
|
||||
} from '../constants';
|
||||
|
||||
type FetchSchemaResponse =
|
||||
@ -18,25 +19,58 @@ type FetchSchemaResponse =
|
||||
}
|
||||
| {
|
||||
kind: 'success';
|
||||
response: NonNullable<GetSchemaListResponseWithError['data']>;
|
||||
response: NonNullable<
|
||||
GetSchemaListResponseWithError['data']
|
||||
>['schema_registry_dumps'];
|
||||
loadMore: VoidFunction;
|
||||
isLoadingMore: boolean;
|
||||
shouldLoadMore: boolean;
|
||||
};
|
||||
|
||||
export const useGetSchemaList = (projectId: string): FetchSchemaResponse => {
|
||||
const fetchRegistrySchemasQueryFn = (projectId: string) => {
|
||||
return schemaRegsitryControlPlaneClient.query<
|
||||
GetSchemaListResponseWithError,
|
||||
{ projectId: string }
|
||||
>(FETCH_REGSITRY_SCHEMAS_QUERY, {
|
||||
projectId: projectId,
|
||||
});
|
||||
};
|
||||
const [dumps, setDumps] = React.useState<
|
||||
NonNullable<GetSchemaListResponseWithError['data']>['schema_registry_dumps']
|
||||
>([]);
|
||||
const [loadingMore, setLoadingMore] = React.useState(false);
|
||||
const [shouldLoadMore, setShouldLoadMore] = React.useState(true);
|
||||
|
||||
const { data, error, isLoading } = useQuery({
|
||||
let cursor = 'now()';
|
||||
if (dumps && dumps.length > 0) {
|
||||
cursor = dumps[dumps.length - 1].change_recorded_at;
|
||||
}
|
||||
|
||||
const fetchRegistrySchemasQueryFn = React.useCallback(
|
||||
(projectId: string) => {
|
||||
return schemaRegsitryControlPlaneClient.query<
|
||||
GetSchemaListResponseWithError,
|
||||
{ projectId: string; cursor: string; limit: number }
|
||||
>(FETCH_REGISTRY_SCHEMAS_QUERY, {
|
||||
projectId: projectId,
|
||||
cursor: cursor,
|
||||
limit: SCHEMA_LIST_FETCH_BATCH_SIZE,
|
||||
});
|
||||
},
|
||||
[cursor]
|
||||
);
|
||||
|
||||
const { data, error, isLoading, refetch } = useQuery({
|
||||
queryKey: FETCH_REGISTRY_SCHEMAS_QUERY_NAME,
|
||||
queryFn: () => fetchRegistrySchemasQueryFn(projectId),
|
||||
refetchOnMount: 'always',
|
||||
refetchOnWindowFocus: true,
|
||||
staleTime: SCHEMA_REGISTRY_REFRESH_TIME,
|
||||
onSettled: () => {
|
||||
setLoadingMore(false);
|
||||
},
|
||||
onSuccess: response => {
|
||||
if (response && response.data && response.data.schema_registry_dumps) {
|
||||
const dumps = response.data.schema_registry_dumps;
|
||||
setDumps(d => [...(d || []), ...dumps]);
|
||||
if (dumps.length < SCHEMA_LIST_FETCH_BATCH_SIZE) {
|
||||
setShouldLoadMore(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (isLoading) {
|
||||
@ -54,6 +88,12 @@ export const useGetSchemaList = (projectId: string): FetchSchemaResponse => {
|
||||
|
||||
return {
|
||||
kind: 'success',
|
||||
response: data.data,
|
||||
response: dumps,
|
||||
loadMore: () => {
|
||||
setLoadingMore(true);
|
||||
refetch();
|
||||
},
|
||||
isLoadingMore: loadingMore,
|
||||
shouldLoadMore,
|
||||
};
|
||||
};
|
||||
|
@ -1,15 +1,17 @@
|
||||
import { parse as gql } from 'graphql';
|
||||
|
||||
export const FETCH_REGSITRY_SCHEMAS_QUERY = gql(`
|
||||
query fetchRegistrySchemas($projectId: uuid!) {
|
||||
export const FETCH_REGISTRY_SCHEMAS_QUERY = gql(`
|
||||
query fetchRegistrySchemas($projectId: uuid!, $cursor: timestamptz!, $limit: Int!) {
|
||||
schema_registry_dumps(
|
||||
where: {_and: [{project_id: {_eq: $projectId}, hasura_schema_role: {_eq: "admin"}}]},
|
||||
where: {_and: [{project_id: {_eq: $projectId}, hasura_schema_role: {_eq: "admin"}}, {change_recorded_at: {_lt: $cursor}}]},
|
||||
order_by: {change_recorded_at: desc}
|
||||
limit: $limit
|
||||
) {
|
||||
change_recorded_at
|
||||
schema_hash
|
||||
entry_hash
|
||||
id
|
||||
metadata_resource_version
|
||||
sibling_schemas {
|
||||
id
|
||||
entry_hash
|
||||
@ -30,7 +32,7 @@ query fetchRegistrySchemas($projectId: uuid!) {
|
||||
|
||||
`);
|
||||
|
||||
export const FETCH_REGSITRY_SCHEMA_QUERY = gql(`
|
||||
export const FETCH_REGISTRY_SCHEMA_QUERY = gql(`
|
||||
query fetchRegistrySchema ($schemaId: uuid!) {
|
||||
schema_registry_dumps (
|
||||
where: {
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
SiblingSchema,
|
||||
GetRegistrySchemaResponseWithError,
|
||||
} from './types';
|
||||
import moment from 'moment';
|
||||
|
||||
export const CapitalizeFirstLetter = (str: string) => {
|
||||
return str[0].toUpperCase() + str.slice(1);
|
||||
@ -29,19 +30,24 @@ export const schemaRegsitryControlPlaneClient = createControlPlaneClient(
|
||||
);
|
||||
|
||||
export const schemaListTransformFn = (
|
||||
fetchedData: NonNullable<GetSchemaListResponseWithError['data']>
|
||||
dumps: NonNullable<
|
||||
GetSchemaListResponseWithError['data']
|
||||
>['schema_registry_dumps']
|
||||
) => {
|
||||
const dumps = fetchedData.schema_registry_dumps || [];
|
||||
|
||||
const schemaList: Schema[] = [];
|
||||
|
||||
dumps.forEach((dump: SchemaRegistryDumpWithSiblingSchema) => {
|
||||
const roleBasedSchemas: RoleBasedSchema[] = [];
|
||||
|
||||
dump.sibling_schemas.forEach((childSchema: SiblingSchema) => {
|
||||
const changes: SchemaChange[] = [
|
||||
...(childSchema.diff_with_previous_schema?.[0]?.schema_diff_data || []),
|
||||
];
|
||||
const prevSchemaDiff = childSchema.diff_with_previous_schema;
|
||||
let changes: SchemaChange[] | undefined = [];
|
||||
|
||||
if (prevSchemaDiff?.length > 0) {
|
||||
changes = [...(prevSchemaDiff[0]?.schema_diff_data || [])];
|
||||
} else {
|
||||
changes = undefined;
|
||||
}
|
||||
|
||||
const roleBasedSchema: RoleBasedSchema = {
|
||||
raw: childSchema.schema_sdl,
|
||||
@ -75,9 +81,14 @@ export const schemaTransformFn = (
|
||||
|
||||
const roleBasedSchemas: RoleBasedSchema[] = [];
|
||||
|
||||
const changes = [
|
||||
...(data.diff_with_previous_schema?.[0]?.schema_diff_data || []),
|
||||
];
|
||||
const prevSchemaDiff = data.diff_with_previous_schema;
|
||||
let changes: SchemaChange[] | undefined = [];
|
||||
|
||||
if (prevSchemaDiff?.length > 0) {
|
||||
changes = [...(prevSchemaDiff[0]?.schema_diff_data || [])];
|
||||
} else {
|
||||
changes = undefined;
|
||||
}
|
||||
|
||||
const roleBasedSchema: RoleBasedSchema = {
|
||||
id: data.id,
|
||||
@ -100,3 +111,8 @@ export const schemaTransformFn = (
|
||||
|
||||
return schema;
|
||||
};
|
||||
|
||||
export const getPublishTime = (isoStringTs: string) => {
|
||||
const published = moment(isoStringTs);
|
||||
return published.format('DD/MM/YYYY HH:mm:ss');
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user