Compare commits

..

10 Commits

Author SHA1 Message Date
Szilárd Dóró
cf347341d4 Merge pull request #1620 from nhost/changeset-release/main
chore: update versions
2023-02-16 09:54:38 +01:00
github-actions[bot]
d6c670a78b chore: update versions 2023-02-16 08:32:50 +00:00
Szilárd Dóró
197e406209 Merge pull request #1614 from nhost/fix/deployments-are-undefined
fix(dashboard): don't break the UI when deployments are unavailable
2023-02-16 09:30:53 +01:00
Szilárd Dóró
5fc78964dc fix(dashboard): fix deployments on the deployments page 2023-02-15 18:32:55 +01:00
Szilárd Dóró
5880f0cd17 chore(hasura-auth-js): bump msw version 2023-02-15 15:14:15 +01:00
Szilárd Dóró
b2b17d71aa fix(dashboard): fix build error 2023-02-15 14:16:54 +01:00
Szilárd Dóró
0785a41070 fix lockfile 2023-02-15 13:52:52 +01:00
Szilárd Dóró
c6c8569088 chore(dashboard): extended deployment tests 2023-02-15 13:51:56 +01:00
Szilárd Dóró
c38f60740e chore(dashboard): added deployment list tests 2023-02-15 13:45:32 +01:00
Szilárd Dóró
a37a430b9f fix(dashboard): don't break UI when deployments are unavailable 2023-02-15 11:42:28 +01:00
31 changed files with 999 additions and 353 deletions

View File

@@ -1,5 +1,13 @@
# @nhost/dashboard # @nhost/dashboard
## 0.11.10
### Patch Changes
- a37a430b: fix(dashboard): don't break UI when deployments are unavailable
- @nhost/react-apollo@4.13.4
- @nhost/nextjs@1.13.4
## 0.11.9 ## 0.11.9
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "@nhost/dashboard", "name": "@nhost/dashboard",
"version": "0.11.9", "version": "0.11.10",
"private": true, "private": true,
"scripts": { "scripts": {
"preinstall": "npx only-allow pnpm", "preinstall": "npx only-allow pnpm",
@@ -129,7 +129,7 @@
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"jsdom": "^21.0.0", "jsdom": "^21.0.0",
"lint-staged": ">=13", "lint-staged": ">=13",
"msw": "^0.49.0", "msw": "^1.0.1",
"msw-storybook-addon": "^1.6.3", "msw-storybook-addon": "^1.6.3",
"postcss": "^8.4.19", "postcss": "^8.4.19",
"prettier": "^2.7.1", "prettier": "^2.7.1",

View File

@@ -111,9 +111,9 @@ export default function AppDeployments(props: AppDeploymentsProps) {
throw error; throw error;
} }
const { deployments } = deploymentPageData || {}; const { deployments } = deploymentPageData || { deployments: [] };
const { deployments: scheduledOrPendingDeployments } = const { deployments: scheduledOrPendingDeployments } =
scheduledOrPendingDeploymentsData || {}; scheduledOrPendingDeploymentsData || { deployments: [] };
const latestDeployment = latestDeploymentData?.deployments[0]; const latestDeployment = latestDeploymentData?.deployments[0];
const latestLiveDeployment = latestLiveDeploymentData?.deployments[0]; const latestLiveDeployment = latestLiveDeploymentData?.deployments[0];
@@ -135,7 +135,7 @@ export default function AppDeployments(props: AppDeploymentsProps) {
deployment={deployment} deployment={deployment}
isLive={liveDeploymentId === deployment.id} isLive={liveDeploymentId === deployment.id}
showRedeploy={latestDeployment.id === deployment.id} showRedeploy={latestDeployment.id === deployment.id}
disableRedeploy={scheduledOrPendingDeployments.length > 0} disableRedeploy={scheduledOrPendingDeployments?.length > 0}
/> />
{index !== deployments.length - 1 && <Divider component="li" />} {index !== deployments.length - 1 && <Divider component="li" />}
@@ -143,7 +143,7 @@ export default function AppDeployments(props: AppDeploymentsProps) {
))} ))}
</List> </List>
<div className="mt-8 flex w-full justify-center"> <div className="mt-8 flex w-full justify-center">
<div className="grid grid-flow-col gap-2 items-center"> <div className="grid grid-flow-col items-center gap-2">
<NextPrevPageLink <NextPrevPageLink
direction="prev" direction="prev"
prevAllowed={page !== 1} prevAllowed={page !== 1}

View File

@@ -1,3 +1,4 @@
import RetryableErrorBoundary from '@/components/common/RetryableErrorBoundary';
import Container from '@/components/layout/Container'; import Container from '@/components/layout/Container';
import { features } from '@/components/overview/features'; import { features } from '@/components/overview/features';
import { frameworks } from '@/components/overview/frameworks'; import { frameworks } from '@/components/overview/frameworks';
@@ -55,7 +56,9 @@ export default function ApplicationLive() {
<div className="grid grid-cols-1 gap-12 pt-3 lg:grid-cols-3"> <div className="grid grid-cols-1 gap-12 pt-3 lg:grid-cols-3">
<div className="order-2 grid grid-flow-row gap-12 lg:order-1 lg:col-span-2"> <div className="order-2 grid grid-flow-row gap-12 lg:order-1 lg:col-span-2">
<OverviewDeployments /> <RetryableErrorBoundary>
<OverviewDeployments />
</RetryableErrorBoundary>
<OverviewDocumentation <OverviewDocumentation
title="Pick your favorite framework and start learning" title="Pick your favorite framework and start learning"

View File

@@ -58,9 +58,10 @@ export default function DeploymentListItem({
return ( return (
<ListItem.Root> <ListItem.Root>
<ListItem.Button <ListItem.Button
className="grid grid-flow-col items-center justify-between gap-2 px-2 py-2" className="grid grid-flow-col items-center justify-between gap-2 rounded-none px-2 py-2"
component={NavLink} component={NavLink}
href={`/${currentWorkspace.slug}/${currentApplication.slug}/deployments/${deployment.id}`} href={`/${currentWorkspace.slug}/${currentApplication.slug}/deployments/${deployment.id}`}
aria-label={commitMessage || 'No commit message'}
> >
<div className="flex cursor-pointer flex-row items-center justify-center space-x-2 self-center"> <div className="flex cursor-pointer flex-row items-center justify-center space-x-2 self-center">
<ListItem.Avatar> <ListItem.Avatar>
@@ -83,10 +84,14 @@ export default function DeploymentListItem({
/> />
</div> </div>
<div className="grid grid-flow-col gap-2 items-center"> <div className="grid grid-flow-col items-center gap-2">
{showRedeploy && ( {showRedeploy && (
<Tooltip <Tooltip
title="Deployments cannot be re-triggered when a deployment is in progress." title={
!disableRedeploy && !loading
? 'Deployments cannot be re-triggered when a deployment is in progress.'
: ''
}
hasDisabledChildren={disableRedeploy || loading} hasDisabledChildren={disableRedeploy || loading}
disableHoverListener={!disableRedeploy} disableHoverListener={!disableRedeploy}
> >
@@ -123,9 +128,10 @@ export default function DeploymentListItem({
); );
}} }}
startIcon={ startIcon={
<ArrowCounterclockwiseIcon className={twMerge('w-4 h-4')} /> <ArrowCounterclockwiseIcon className={twMerge('h-4 w-4')} />
} }
className="rounded-full py-1 px-2 text-xs" className="rounded-full py-1 px-2 text-xs"
aria-label="Redeploy"
> >
Redeploy Redeploy
</Button> </Button>
@@ -133,7 +139,7 @@ export default function DeploymentListItem({
)} )}
{isLive && ( {isLive && (
<div className="w-12 flex justify-end"> <div className="flex w-12 justify-end">
<Chip size="small" color="success" label="Live" /> <Chip size="small" color="success" label="Live" />
</div> </div>
)} )}

View File

@@ -0,0 +1,238 @@
import { UserDataProvider } from '@/context/workspace1-context';
import type { Application } from '@/types/application';
import { ApplicationStatus } from '@/types/application';
import type { Workspace } from '@/types/workspace';
import { render, screen, waitForElementToBeRemoved } from '@/utils/testUtils';
import { graphql, rest } from 'msw';
import { setupServer } from 'msw/node';
import { afterAll, beforeAll, vi } from 'vitest';
import OverviewDeployments from '.';
vi.mock('next/router', () => ({
useRouter: vi.fn().mockReturnValue({
basePath: '',
pathname: '/test-workspace/test-application',
route: '/[workspaceSlug]/[appSlug]',
asPath: '/test-workspace/test-application',
isLocaleDomain: false,
isReady: true,
isPreview: false,
query: {
workspaceSlug: 'test-workspace',
appSlug: 'test-application',
},
push: vi.fn(),
replace: vi.fn(),
reload: vi.fn(),
back: vi.fn(),
prefetch: vi.fn(),
beforePopState: vi.fn(),
events: {
on: vi.fn(),
off: vi.fn(),
emit: vi.fn(),
},
isFallback: false,
}),
}));
const mockApplication: Application = {
id: '1',
name: 'Test Application',
slug: 'test-application',
appStates: [],
hasuraGraphqlAdminSecret: 'nhost-admin-secret',
subdomain: '',
isProvisioned: true,
region: {
awsName: 'us-east-1',
city: 'New York',
countryCode: 'US',
id: '1',
},
createdAt: new Date().toISOString(),
deployments: [],
desiredState: ApplicationStatus.Live,
featureFlags: [],
providersUpdated: true,
githubRepository: { fullName: 'test/git-project' },
};
const mockWorkspace: Workspace = {
id: '1',
name: 'Test Workspace',
slug: 'test-workspace',
members: [],
applications: [mockApplication],
};
const mockGraphqlLink = graphql.link('http://localhost:1337/v1/graphql');
const server = setupServer(
rest.get('http://localhost:1337/v1/graphql', (req, res, ctx) =>
res(ctx.status(200)),
),
mockGraphqlLink.operation(async (req, res, ctx) =>
res(
ctx.data({
deployments: [],
}),
),
),
);
beforeAll(() => {
process.env.NEXT_PUBLIC_NHOST_PLATFORM = 'true';
process.env.NEXT_PUBLIC_ENV = 'production';
server.listen();
});
afterEach(() => server.resetHandlers());
afterAll(() => {
server.close();
vi.restoreAllMocks();
});
test('should render an empty state when GitHub is not connected', () => {
render(
<UserDataProvider
initialWorkspaces={[
{
...mockWorkspace,
applications: [{ ...mockApplication, githubRepository: null }],
},
]}
>
<OverviewDeployments />
</UserDataProvider>,
);
expect(screen.getByText(/no deployments/i)).toBeInTheDocument();
expect(
screen.getByRole('button', { name: /connect to github/i }),
).toBeInTheDocument();
});
test('should render an empty state when GitHub is connected, but there are no deployments', async () => {
render(
<UserDataProvider initialWorkspaces={[mockWorkspace]}>
<OverviewDeployments />
</UserDataProvider>,
);
expect(screen.getByText(/^deployments$/i)).toBeInTheDocument();
expect(screen.getByRole('link', { name: /view all/i })).toBeInTheDocument();
await waitForElementToBeRemoved(() => screen.queryByRole('progressbar'));
expect(screen.getByText(/no deployments/i)).toBeInTheDocument();
expect(screen.getByText(/test\/git-project/i)).toBeInTheDocument();
expect(screen.getByRole('link', { name: /edit/i })).toHaveAttribute(
'href',
'/test-workspace/test-application/settings/git',
);
});
test('should render a list of deployments', async () => {
server.use(
mockGraphqlLink.operation(async (req, res, ctx) => {
const requestPayload = await req.json();
if (requestPayload.operationName === 'ScheduledOrPendingDeploymentsSub') {
return res(ctx.data({ deployments: [] }));
}
return res(
ctx.data({
deployments: [
{
id: '1',
commitSHA: 'abc123',
deploymentStartedAt: '2021-08-01T00:00:00.000Z',
deploymentEndedAt: '2021-08-01T00:05:00.000Z',
deploymentStatus: 'DEPLOYED',
commitUserName: 'test.user',
commitUserAvatarUrl: 'http://images.example.com/avatar.png',
commitMessage: 'Test commit message',
},
],
}),
);
}),
);
render(
<UserDataProvider initialWorkspaces={[mockWorkspace]}>
<OverviewDeployments />
</UserDataProvider>,
);
await waitForElementToBeRemoved(() => screen.queryByRole('progressbar'));
expect(screen.getByText(/test commit message/i)).toBeInTheDocument();
expect(screen.getByLabelText(/avatar/i)).toHaveStyle(
'background-image: url(http://images.example.com/avatar.png)',
);
expect(
screen.getByRole('link', {
name: /test commit message/i,
}),
).toHaveAttribute('href', '/test-workspace/test-application/deployments/1');
expect(screen.getByText(/5m 0s/i)).toBeInTheDocument();
expect(screen.getByText(/live/i)).toBeInTheDocument();
expect(screen.getByRole('button', { name: /redeploy/i })).not.toBeDisabled();
});
test('should disable redeployments if a deployment is already in progress', async () => {
server.use(
mockGraphqlLink.operation(async (req, res, ctx) => {
const requestPayload = await req.json();
if (requestPayload.operationName === 'ScheduledOrPendingDeploymentsSub') {
return res(
ctx.data({
deployments: [
{
id: '2',
commitSHA: 'abc234',
deploymentStartedAt: '2021-08-02T00:00:00.000Z',
deploymentEndedAt: null,
deploymentStatus: 'PENDING',
commitUserName: 'test.user',
commitUserAvatarUrl: 'http://images.example.com/avatar.png',
commitMessage: 'Test commit message',
},
],
}),
);
}
return res(
ctx.data({
deployments: [
{
id: '1',
commitSHA: 'abc123',
deploymentStartedAt: '2021-08-01T00:00:00.000Z',
deploymentEndedAt: '2021-08-01T00:05:00.000Z',
deploymentStatus: 'DEPLOYED',
commitUserName: 'test.user',
commitUserAvatarUrl: 'http://images.example.com/avatar.png',
commitMessage: 'Test commit message',
},
],
}),
);
}),
);
render(
<UserDataProvider initialWorkspaces={[mockWorkspace]}>
<OverviewDeployments />
</UserDataProvider>,
);
await waitForElementToBeRemoved(() => screen.queryByRole('progressbar'));
expect(screen.getByRole('button', { name: /redeploy/i })).toBeDisabled();
});

View File

@@ -6,6 +6,7 @@ import ActivityIndicator from '@/ui/v2/ActivityIndicator';
import Box from '@/ui/v2/Box'; import Box from '@/ui/v2/Box';
import Button from '@/ui/v2/Button'; import Button from '@/ui/v2/Button';
import Divider from '@/ui/v2/Divider'; import Divider from '@/ui/v2/Divider';
import ChevronRightIcon from '@/ui/v2/icons/ChevronRightIcon';
import RocketIcon from '@/ui/v2/icons/RocketIcon'; import RocketIcon from '@/ui/v2/icons/RocketIcon';
import List from '@/ui/v2/List'; import List from '@/ui/v2/List';
import Text from '@/ui/v2/Text'; import Text from '@/ui/v2/Text';
@@ -14,7 +15,6 @@ import {
useGetDeploymentsSubSubscription, useGetDeploymentsSubSubscription,
useScheduledOrPendingDeploymentsSubSubscription, useScheduledOrPendingDeploymentsSubSubscription,
} from '@/utils/__generated__/graphql'; } from '@/utils/__generated__/graphql';
import { ChevronRightIcon } from '@heroicons/react/solid';
import NavLink from 'next/link'; import NavLink from 'next/link';
import { Fragment } from 'react'; import { Fragment } from 'react';
@@ -22,16 +22,16 @@ function OverviewDeploymentsTopBar() {
const { currentWorkspace, currentApplication } = const { currentWorkspace, currentApplication } =
useCurrentWorkspaceAndApplication(); useCurrentWorkspaceAndApplication();
const { githubRepository } = currentApplication; const { githubRepository } = currentApplication || {};
return ( return (
<div className="grid grid-flow-col gap-2 items-center place-content-between pb-4"> <div className="grid grid-flow-col place-content-between items-center gap-2 pb-4">
<Text variant="h3" className="font-medium"> <Text variant="h3" className="font-medium">
Deployments Deployments
</Text> </Text>
<NavLink <NavLink
href={`/${currentWorkspace.slug}/${currentApplication.slug}/deployments`} href={`/${currentWorkspace?.slug}/${currentApplication?.slug}/deployments`}
passHref passHref
> >
<Button variant="borderless" disabled={!githubRepository}> <Button variant="borderless" disabled={!githubRepository}>
@@ -43,20 +43,12 @@ function OverviewDeploymentsTopBar() {
); );
} }
interface OverviewDeploymentsProps { function OverviewDeploymentList() {
projectId: string;
githubRepository: { fullName: string };
}
function OverviewDeployments({
projectId,
githubRepository,
}: OverviewDeploymentsProps) {
const { currentWorkspace, currentApplication } = const { currentWorkspace, currentApplication } =
useCurrentWorkspaceAndApplication(); useCurrentWorkspaceAndApplication();
const { data, loading } = useGetDeploymentsSubSubscription({ const { data, loading } = useGetDeploymentsSubSubscription({
variables: { variables: {
id: projectId, id: currentApplication?.id,
limit: 5, limit: 5,
offset: 0, offset: 0,
}, },
@@ -67,23 +59,23 @@ function OverviewDeployments({
loading: scheduledOrPendingDeploymentsLoading, loading: scheduledOrPendingDeploymentsLoading,
} = useScheduledOrPendingDeploymentsSubSubscription({ } = useScheduledOrPendingDeploymentsSubSubscription({
variables: { variables: {
appId: projectId, appId: currentApplication?.id,
}, },
}); });
if (loading || scheduledOrPendingDeploymentsLoading) { if (loading || scheduledOrPendingDeploymentsLoading) {
return ( return (
<Box className="h-[323px] p-2 border-1 rounded-lg"> <Box className="h-[323px] rounded-lg border-1 p-2">
<ActivityIndicator label="Loading deployments..." /> <ActivityIndicator label="Loading deployments..." />
</Box> </Box>
); );
} }
const { deployments } = data; const { deployments } = data || { deployments: [] };
if (deployments.length === 0) { if (!deployments?.length) {
return ( return (
<Box className="grid grid-flow-row gap-5 items-center justify-items-center rounded-lg py-12 px-48 shadow-sm border-1"> <Box className="grid grid-flow-row items-center justify-items-center gap-5 overflow-hidden rounded-lg border-1 py-12 px-48 shadow-sm">
<RocketIcon <RocketIcon
strokeWidth={1} strokeWidth={1}
className="h-10 w-10" className="h-10 w-10"
@@ -100,16 +92,16 @@ function OverviewDeployments({
</div> </div>
<Box <Box
className="mt-6 flex flex-row place-content-between rounded-lg py-2 px-2 max-w-sm w-full" className="mt-6 flex w-full max-w-sm flex-row place-content-between rounded-lg py-2 px-2"
sx={{ backgroundColor: 'grey.200' }} sx={{ backgroundColor: 'grey.200' }}
> >
<Box <Box
className="grid grid-flow-col gap-1.5 ml-2" className="ml-2 grid grid-flow-col gap-1.5"
sx={{ backgroundColor: 'transparent' }} sx={{ backgroundColor: 'transparent' }}
> >
<GithubIcon className="h-4 w-4 self-center" /> <GithubIcon className="h-4 w-4 self-center" />
<Text variant="body1" className="self-center font-normal"> <Text variant="body1" className="self-center font-normal">
{githubRepository.fullName} {currentApplication?.githubRepository?.fullName}
</Text> </Text>
</Box> </Box>
@@ -128,20 +120,20 @@ function OverviewDeployments({
const liveDeploymentId = getLastLiveDeployment(deployments); const liveDeploymentId = getLastLiveDeployment(deployments);
const { deployments: scheduledOrPendingDeployments } = const { deployments: scheduledOrPendingDeployments } =
scheduledOrPendingDeploymentsData; scheduledOrPendingDeploymentsData || { deployments: [] };
return ( return (
<List <List
className="rounded-x-lg flex flex-col rounded-lg" className="rounded-x-lg flex flex-col overflow-hidden rounded-lg"
sx={{ borderColor: 'grey.300', borderWidth: 1 }} sx={{ borderColor: 'grey.300', borderWidth: 1 }}
> >
{deployments.map((deployment, index) => ( {deployments?.map((deployment, index) => (
<Fragment key={deployment.id}> <Fragment key={deployment.id}>
<DeploymentListItem <DeploymentListItem
deployment={deployment} deployment={deployment}
isLive={deployment.id === liveDeploymentId} isLive={deployment.id === liveDeploymentId}
showRedeploy={index === 0} showRedeploy={index === 0}
disableRedeploy={scheduledOrPendingDeployments.length > 0} disableRedeploy={scheduledOrPendingDeployments?.length > 0}
/> />
{index !== deployments.length - 1 && <Divider component="li" />} {index !== deployments.length - 1 && <Divider component="li" />}
@@ -151,21 +143,18 @@ function OverviewDeployments({
); );
} }
export default function OverviewDeploymentsPage() { export default function OverviewDeployments() {
const { currentApplication } = useCurrentWorkspaceAndApplication(); const { currentApplication } = useCurrentWorkspaceAndApplication();
const { openGitHubModal } = useGitHubModal(); const { openGitHubModal } = useGitHubModal();
const { githubRepository } = currentApplication; const { githubRepository } = currentApplication || {};
// GitHub repo connected. Show deployments // GitHub repo connected. Show deployments
if (githubRepository) { if (githubRepository) {
return ( return (
<div className="flex flex-col"> <div className="flex flex-col">
<OverviewDeploymentsTopBar /> <OverviewDeploymentsTopBar />
<OverviewDeployments <OverviewDeploymentList />
projectId={currentApplication.id}
githubRepository={githubRepository}
/>
</div> </div>
); );
} }
@@ -175,7 +164,7 @@ export default function OverviewDeploymentsPage() {
<div className="flex flex-col"> <div className="flex flex-col">
<OverviewDeploymentsTopBar /> <OverviewDeploymentsTopBar />
<Box className="grid grid-flow-row gap-5 items-center justify-items-center rounded-lg py-12 px-48 shadow-sm border-1"> <Box className="grid grid-flow-row items-center justify-items-center gap-5 rounded-lg border-1 py-12 px-48 shadow-sm">
<RocketIcon strokeWidth={1} className="h-10 w-10" /> <RocketIcon strokeWidth={1} className="h-10 w-10" />
<div className="grid grid-flow-row gap-1"> <div className="grid grid-flow-row gap-1">

View File

@@ -59,6 +59,7 @@ export function Avatar({
<Box <Box
style={Object.assign(style, { backgroundImage: `url(${avatarUrl})` })} style={Object.assign(style, { backgroundImage: `url(${avatarUrl})` })}
className={classes} className={classes}
aria-label="Avatar"
{...rest} {...rest}
/> />
); );

View File

@@ -63,7 +63,9 @@ function ActivityIndicator({
// We are rendering a span instead of null in order to keep the layout // We are rendering a span instead of null in order to keep the layout
// intact in certain cases (e.g. when elements have a "space-between" // intact in certain cases (e.g. when elements have a "space-between"
// position). // position).
return <span />; return (
<span role="progressbar" aria-label="Activity indicator placeholder" />
);
} }
return ( return (

View File

@@ -25,10 +25,25 @@ export const UserDataContext = createContext<UserDataContent>({
setUserContext: () => {}, setUserContext: () => {},
}); });
export function UserDataProvider({ children }: PropsWithChildren<unknown>) { export interface UserDataProviderProps {
/**
* Initial workspaces to be used in the context.
*/
initialWorkspaces?: Workspace[];
/**
* Initial metadata to be used in the context.
*/
initialMetadata?: Record<string, any>;
}
export function UserDataProvider({
children,
initialWorkspaces,
initialMetadata,
}: PropsWithChildren<UserDataProviderProps>) {
const [userContext, setUserContext] = useState({ const [userContext, setUserContext] = useState({
workspaces: [], workspaces: initialWorkspaces || [],
metadata: {}, metadata: initialMetadata || {},
}); });
const value = useMemo( const value = useMemo(

View File

@@ -31,6 +31,7 @@ test('should throw an error if permission object is incorrectly provided', async
action: 'select', action: 'select',
role: 'user', role: 'user',
mode: 'update', mode: 'update',
resourceVersion: 1,
}), }),
).rejects.toThrowError( ).rejects.toThrowError(
new Error( new Error(

View File

@@ -4,7 +4,11 @@ import slugify from 'slugify';
import { LOCAL_BACKEND_URL } from './env'; import { LOCAL_BACKEND_URL } from './env';
import type { DeploymentRowFragment } from './__generated__/graphql'; import type { DeploymentRowFragment } from './__generated__/graphql';
export function getLastLiveDeployment(deployments: DeploymentRowFragment[]) { export function getLastLiveDeployment(deployments?: DeploymentRowFragment[]) {
if (!deployments) {
return '';
}
return ( return (
deployments.find((deployment) => deployment.deploymentStatus === 'DEPLOYED') deployments.find((deployment) => deployment.deploymentStatus === 'DEPLOYED')
?.id || '' ?.id || ''

View File

@@ -5,6 +5,7 @@ import { ManagedUIContext } from '@/context/UIContext';
import { WorkspaceProvider } from '@/context/workspace-context'; import { WorkspaceProvider } from '@/context/workspace-context';
import { UserDataProvider } from '@/context/workspace1-context'; import { UserDataProvider } from '@/context/workspace1-context';
import createTheme from '@/ui/v2/createTheme'; import createTheme from '@/ui/v2/createTheme';
import { createHttpLink } from '@apollo/client';
import { CacheProvider } from '@emotion/react'; import { CacheProvider } from '@emotion/react';
import { ThemeProvider } from '@mui/material/styles'; import { ThemeProvider } from '@mui/material/styles';
import { NhostProvider } from '@nhost/nextjs'; import { NhostProvider } from '@nhost/nextjs';
@@ -33,7 +34,7 @@ const queryClient = new QueryClient({
global.fetch = fetch; global.fetch = fetch;
const mockRouter: NextRouter = { export const mockRouter: NextRouter = {
basePath: '', basePath: '',
pathname: '/', pathname: '/',
route: '/', route: '/',
@@ -65,7 +66,12 @@ function Providers({ children }: PropsWithChildren<{}>) {
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<CacheProvider value={emotionCache}> <CacheProvider value={emotionCache}>
<NhostProvider nhost={nhost}> <NhostProvider nhost={nhost}>
<NhostApolloProvider nhost={nhost}> <NhostApolloProvider
nhost={nhost}
link={createHttpLink({
uri: 'http://localhost:1337/v1/graphql',
})}
>
<WorkspaceProvider> <WorkspaceProvider>
<UserDataProvider> <UserDataProvider>
<ManagedUIContext> <ManagedUIContext>

View File

@@ -1,5 +1,12 @@
# @nhost/apollo # @nhost/apollo
## 4.13.3
### Patch Changes
- c38f6074: chore(react-apollo): allow `link` in configuration options
- @nhost/nhost-js@1.13.3
## 4.13.2 ## 4.13.2
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "@nhost/apollo", "name": "@nhost/apollo",
"version": "4.13.2", "version": "4.13.3",
"description": "Nhost Apollo Client library", "description": "Nhost Apollo Client library",
"license": "MIT", "license": "MIT",
"keywords": [ "keywords": [
@@ -68,6 +68,6 @@
}, },
"devDependencies": { "devDependencies": {
"@nhost/nhost-js": "workspace:*", "@nhost/nhost-js": "workspace:*",
"@apollo/client": "^3.7.1" "@apollo/client": "^3.7.3"
} }
} }

View File

@@ -25,6 +25,7 @@ export type NhostApolloClientOptions = {
connectToDevTools?: boolean connectToDevTools?: boolean
cache?: InMemoryCache cache?: InMemoryCache
onError?: RequestHandler onError?: RequestHandler
link?: ApolloClient<any>['link']
} }
export const createApolloClient = ({ export const createApolloClient = ({
@@ -35,7 +36,8 @@ export const createApolloClient = ({
fetchPolicy, fetchPolicy,
cache = new InMemoryCache(), cache = new InMemoryCache(),
connectToDevTools = isBrowser && process.env.NODE_ENV === 'development', connectToDevTools = isBrowser && process.env.NODE_ENV === 'development',
onError onError,
link: customLink
}: NhostApolloClientOptions): ApolloClient<any> => { }: NhostApolloClientOptions): ApolloClient<any> => {
let backendUrl = graphqlUrl || nhost?.graphql.getUrl() let backendUrl = graphqlUrl || nhost?.graphql.getUrl()
if (!backendUrl) { if (!backendUrl) {
@@ -122,7 +124,11 @@ export const createApolloClient = ({
} }
// add link // add link
apolloClientOptions.link = typeof onError === 'function' ? from([onError, link]) : from([link]) if (customLink) {
apolloClientOptions.link = from([customLink])
} else {
apolloClientOptions.link = typeof onError === 'function' ? from([onError, link]) : from([link])
}
const client = new ApolloClient(apolloClientOptions) const client = new ApolloClient(apolloClientOptions)

View File

@@ -1,5 +1,13 @@
# @nhost/react-apollo # @nhost/react-apollo
## 4.13.4
### Patch Changes
- Updated dependencies [c38f6074]
- @nhost/apollo@4.13.3
- @nhost/react@1.13.4
## 4.13.3 ## 4.13.3
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "@nhost/react-apollo", "name": "@nhost/react-apollo",
"version": "4.13.3", "version": "4.13.4",
"description": "Nhost React Apollo client", "description": "Nhost React Apollo client",
"license": "MIT", "license": "MIT",
"keywords": [ "keywords": [

View File

@@ -1,5 +1,11 @@
# @nhost/react-urql # @nhost/react-urql
## 1.0.4
### Patch Changes
- @nhost/react@1.13.4
## 1.0.3 ## 1.0.3
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "@nhost/react-urql", "name": "@nhost/react-urql",
"version": "1.0.3", "version": "1.0.4",
"description": "Nhost React URQL client", "description": "Nhost React URQL client",
"license": "MIT", "license": "MIT",
"keywords": [ "keywords": [

View File

@@ -1,5 +1,11 @@
# @nhost/hasura-auth-js # @nhost/hasura-auth-js
## 1.12.3
### Patch Changes
- 5880f0cd: chore(hasura-auth-js): bump `msw` version to `1.0.1`
## 1.12.2 ## 1.12.2
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "@nhost/hasura-auth-js", "name": "@nhost/hasura-auth-js",
"version": "1.12.2", "version": "1.12.3",
"description": "Hasura-auth client", "description": "Hasura-auth client",
"license": "MIT", "license": "MIT",
"keywords": [ "keywords": [
@@ -74,7 +74,7 @@
"@types/js-cookie": "^3.0.2", "@types/js-cookie": "^3.0.2",
"cheerio": "1.0.0-rc.12", "cheerio": "1.0.0-rc.12",
"mailhog": "^4.16.0", "mailhog": "^4.16.0",
"msw": "^0.47.4", "msw": "^1.0.1",
"start-server-and-test": "^1.15.2" "start-server-and-test": "^1.15.2"
} }
} }

View File

@@ -1,5 +1,11 @@
# @nhost/nextjs # @nhost/nextjs
## 1.13.4
### Patch Changes
- @nhost/react@1.13.4
## 1.13.3 ## 1.13.3
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "@nhost/nextjs", "name": "@nhost/nextjs",
"version": "1.13.3", "version": "1.13.4",
"description": "Nhost NextJS library", "description": "Nhost NextJS library",
"license": "MIT", "license": "MIT",
"keywords": [ "keywords": [

View File

@@ -1,5 +1,12 @@
# @nhost/nhost-js # @nhost/nhost-js
## 1.13.3
### Patch Changes
- Updated dependencies [5880f0cd]
- @nhost/hasura-auth-js@1.12.3
## 1.13.2 ## 1.13.2
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "@nhost/nhost-js", "name": "@nhost/nhost-js",
"version": "1.13.2", "version": "1.13.3",
"description": "Nhost JavaScript SDK", "description": "Nhost JavaScript SDK",
"license": "MIT", "license": "MIT",
"keywords": [ "keywords": [

View File

@@ -1,5 +1,11 @@
# @nhost/react # @nhost/react
## 1.13.4
### Patch Changes
- @nhost/nhost-js@1.13.3
## 1.13.3 ## 1.13.3
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "@nhost/react", "name": "@nhost/react",
"version": "1.13.3", "version": "1.13.4",
"description": "Nhost React library", "description": "Nhost React library",
"license": "MIT", "license": "MIT",
"keywords": [ "keywords": [

View File

@@ -1,5 +1,11 @@
# @nhost/vue # @nhost/vue
## 1.13.4
### Patch Changes
- @nhost/nhost-js@1.13.3
## 1.13.3 ## 1.13.3
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "@nhost/vue", "name": "@nhost/vue",
"version": "1.13.3", "version": "1.13.4",
"description": "Nhost Vue library", "description": "Nhost Vue library",
"license": "MIT", "license": "MIT",
"keywords": [ "keywords": [

893
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff