Feat v1 UI tweaks (#1344)

* fix: drop uncached loader

* feat: upgrade modal

* add beta

* hacky feature flag

* fix: build

* refetch interval

* 5s

* stop flashing on load

* lint

* fix: map

* fix: last redir

* nil check

* small styling and wording things, change default canUpgrade -> true

* switch link to github discussion

---------

Co-authored-by: Alexander Belanger <alexander@hatchet.run>
This commit is contained in:
Gabe Ruttner
2025-03-15 09:23:32 -04:00
committed by GitHub
parent 5c647e247e
commit 3670b94fc4
16 changed files with 341 additions and 194 deletions

View File

@@ -21,6 +21,22 @@ func (t *TenantService) TenantUpdate(ctx echo.Context, request gen.TenantUpdateR
return gen.TenantUpdate400JSONResponse(*apiErrors), nil
}
// check if the tenant version is being changed
if t.config.Runtime.PreventTenantVersionUpgrade &&
request.Body.Version != nil &&
*request.Body.Version == "V1" &&
!tenant.CanUpgradeV1 {
code := uint64(403)
message := "Tenant version upgrade is not enabled for this tenant"
return gen.TenantUpdate403JSONResponse(
gen.APIError{
Code: &code,
Description: message,
},
), nil
}
// construct the database query
updateOpts := &repository.UpdateTenantOpts{}

View File

@@ -0,0 +1,9 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE "Tenant" ADD COLUMN IF NOT EXISTS "canUpgradeV1" BOOLEAN NOT NULL DEFAULT true;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
ALTER TABLE "Tenant" DROP COLUMN IF EXISTS "canUpgradeV1";
-- +goose StatementEnd

View File

@@ -140,9 +140,8 @@ export function useTenant(): TenantContext {
return;
}
setLastRedirected(tenant?.slug);
if (tenant?.version == TenantVersion.V0 && pathname.startsWith('/v1')) {
setLastRedirected(tenant?.slug);
return navigate({
pathname: pathname.replace('/v1', ''),
search: params.toString(),
@@ -150,6 +149,7 @@ export function useTenant(): TenantContext {
}
if (tenant?.version == TenantVersion.V1 && !pathname.startsWith('/v1')) {
setLastRedirected(tenant?.slug);
return navigate({
pathname: '/v1' + pathname,
search: params.toString(),

View File

@@ -1,121 +1,99 @@
import MainNav from '@/components/molecules/nav-bar/nav-bar';
import {
LoaderFunctionArgs,
Outlet,
redirect,
useLoaderData,
} from 'react-router-dom';
import { Outlet } from 'react-router-dom';
import api, { queries } from '@/lib/api';
import queryClient from '@/query-client';
import { useContextFromParent } from '@/lib/outlet';
import { Loading } from '@/components/ui/loading.tsx';
import { useQuery } from '@tanstack/react-query';
import SupportChat from '@/components/molecules/support-chat';
import AnalyticsProvider from '@/components/molecules/analytics-provider';
import { useState } from 'react';
const authMiddleware = async (currentUrl: string) => {
try {
const user = await queryClient.fetchQuery({
queryKey: ['user:get:current'],
queryFn: async () => {
const res = await api.userGetCurrent();
return res.data;
},
});
if (
!user.emailVerified &&
!currentUrl.includes('/onboarding/verify-email')
) {
throw redirect('/onboarding/verify-email');
}
return user;
} catch (error) {
if (error instanceof Response) {
throw error;
} else if (
!currentUrl.includes('/auth/login') &&
!currentUrl.includes('/auth/register')
) {
throw redirect('/auth/login');
}
}
};
const invitesRedirector = async (currentUrl: string) => {
try {
const res = await api.userListTenantInvites();
const invites = res.data.rows || [];
if (invites.length > 0 && !currentUrl.includes('/onboarding/invites')) {
throw redirect('/onboarding/invites');
}
return;
} catch (error) {
if (error instanceof Response) {
throw error;
}
}
};
const membershipsPopulator = async (currentUrl: string) => {
try {
const res = await api.tenantMembershipsList();
const memberships = res.data;
if (memberships.rows?.length === 0 && !currentUrl.includes('/onboarding')) {
throw redirect('/onboarding/create-tenant');
}
return res.data.rows;
} catch (error) {
if (error instanceof Response) {
throw error;
}
}
};
export async function loader({ request }: LoaderFunctionArgs) {
const user = await authMiddleware(request.url);
await invitesRedirector(request.url);
const memberships = await membershipsPopulator(request.url);
return {
user,
memberships,
};
}
import { useState, useEffect } from 'react';
import { useContextFromParent } from '@/lib/outlet';
export default function Authenticated() {
const [hasHasBanner, setHasBanner] = useState(false);
const userQuery = useQuery({
queryKey: ['user:get:current'],
queryFn: async () => {
const res = await api.userGetCurrent();
return res.data;
},
});
const invitesQuery = useQuery({
queryKey: ['user:list-tenant-invites'],
queryFn: async () => {
const res = await api.userListTenantInvites();
return res.data.rows || [];
},
});
const listMembershipsQuery = useQuery({
...queries.user.listTenantMemberships,
});
const { user, memberships } = useLoaderData() as Awaited<
ReturnType<typeof loader>
>;
const ctx = useContextFromParent({
user,
memberships: listMembershipsQuery.data?.rows || memberships,
user: userQuery.data,
memberships: listMembershipsQuery.data?.rows,
});
const [hasHasBanner, setHasBanner] = useState(false);
useEffect(() => {
const currentUrl = window.location.pathname;
if (!user || !memberships) {
if (
userQuery.data &&
!userQuery.data.emailVerified &&
!currentUrl.includes('/onboarding/verify-email')
) {
window.location.href = '/onboarding/verify-email';
return;
}
if (
invitesQuery.data?.length &&
invitesQuery.data.length > 0 &&
!currentUrl.includes('/onboarding/invites')
) {
window.location.href = '/onboarding/invites';
return;
}
if (
listMembershipsQuery.data?.rows?.length === 0 &&
!currentUrl.includes('/onboarding')
) {
window.location.href = '/onboarding/create-tenant';
return;
}
}, [userQuery.data, invitesQuery.data, listMembershipsQuery.data]);
if (
userQuery.isLoading ||
invitesQuery.isLoading ||
listMembershipsQuery.isLoading
) {
return <Loading />;
}
if (userQuery.error) {
const currentUrl = window.location.pathname;
if (
!currentUrl.includes('/auth/login') &&
!currentUrl.includes('/auth/register')
) {
window.location.href = '/auth/login';
return null;
}
}
if (!userQuery.data || !listMembershipsQuery.data?.rows) {
return <Loading />;
}
return (
<AnalyticsProvider user={user}>
<SupportChat user={user}>
<AnalyticsProvider user={userQuery.data}>
<SupportChat user={userQuery.data}>
<div className="flex flex-row flex-1 w-full h-full">
<MainNav user={user} setHasBanner={setHasBanner} />
<MainNav user={userQuery.data} setHasBanner={setHasBanner} />
<div
className={`${hasHasBanner ? 'pt-28' : 'pt-16'} flex-grow overflow-y-auto overflow-x-hidden`}
>

View File

@@ -2,7 +2,7 @@ import { Button } from '@/components/ui/button';
import { Separator } from '@/components/ui/separator';
import { TenantContextType } from '@/lib/outlet';
import { useState } from 'react';
import { useLocation, useNavigate, useOutletContext } from 'react-router-dom';
import { useOutletContext } from 'react-router-dom';
import { useApiError } from '@/lib/hooks';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import api, {
@@ -16,8 +16,15 @@ import { Label } from '@radix-ui/react-label';
import { Spinner } from '@/components/ui/loading';
import { capitalize } from '@/lib/utils';
import { UpdateTenantForm } from './components/update-tenant-form';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { AxiosError } from 'axios';
export default function TenantSettings() {
const { tenant } = useOutletContext<TenantContextType>();
@@ -40,57 +47,128 @@ export default function TenantSettings() {
const TenantVersionSwitcher = () => {
const { tenant } = useOutletContext<TenantContextType>();
const selectedVersion = tenant.version;
const queryClient = useQueryClient();
const navigate = useNavigate();
const { pathname } = useLocation();
const [showUpgradeModal, setShowUpgradeModal] = useState(false);
const [upgradeRestrictedError, setUpgradeRestrictedError] =
useState<boolean>(false);
const { handleApiError } = useApiError({});
const { mutate: updateTenant, isPending } = useMutation({
mutationKey: ['tenant:update'],
mutationFn: async (data: UpdateTenantRequest) => {
setUpgradeRestrictedError(false);
await api.tenantUpdate(tenant.metadata.id, data);
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: queries.user.listTenantMemberships.queryKey,
});
window.location.reload();
},
onError: (error: AxiosError) => {
if (error.response?.status === 403) {
setUpgradeRestrictedError(true);
} else {
setShowUpgradeModal(false);
handleApiError(error);
}
},
onError: handleApiError,
});
const tenantVersions = Object.keys(TenantVersion) as Array<
keyof typeof TenantVersion
>;
// Only show for V0 tenants
if (tenant.version === TenantVersion.V1) {
return null;
}
return (
<div className="flex flex-col gap-y-2">
<h2 className="text-xl font-semibold leading-tight text-foreground">
Tenant Version
</h2>
<RadioGroup
disabled={isPending}
value={selectedVersion}
onValueChange={(value) => {
updateTenant({
version: value as TenantVersion,
});
<>
<div className="flex flex-col gap-y-4">
<h2 className="text-xl font-semibold leading-tight text-foreground">
Tenant Version
</h2>
<p className="text-sm text-muted-foreground">
Upgrade your tenant to v1 to access new features and improvements. v1
is currently in beta.
</p>
<Button
onClick={() => setShowUpgradeModal(true)}
disabled={isPending}
className="w-fit"
>
{isPending ? <Spinner /> : null}
Upgrade to v1 (beta)
</Button>
</div>
if (value === 'V1' && !pathname.includes('v1')) {
navigate('/v1' + pathname);
} else if (value === 'V0' && pathname.includes('v1')) {
navigate(pathname.replace('/v1', ''));
}
}}
>
{tenantVersions.map((version) => (
<div key={version} className="flex items-center space-x-2">
<RadioGroupItem value={version} id={version.toLowerCase()} />
<Label htmlFor={version.toLowerCase()}>{version}</Label>
</div>
))}
</RadioGroup>
</div>
<Dialog open={showUpgradeModal} onOpenChange={setShowUpgradeModal}>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Upgrade to v1 (beta)</DialogTitle>
</DialogHeader>
{!upgradeRestrictedError && (
<div className="space-y-4 py-4">
<p className="text-sm">Upgrading your tenant to v1 will:</p>
<ul className="list-disc list-inside text-sm space-y-2">
<li>Enable new v1 features and improvements</li>
<li>Redirect you to the v1 interface</li>
</ul>
<Alert variant="warn">
<AlertTitle>Warning</AlertTitle>
<AlertDescription>
This upgrade will not automatically migrate your existing
workflows or in-progress runs. To ensure zero downtime during
the upgrade, please follow our migration guide which includes
steps for parallel operation of v0 and v1 environments.
</AlertDescription>
</Alert>
<p className="text-sm">
Please read our{' '}
<a
href="https://github.com/hatchet-dev/hatchet/discussions/1348"
target="_blank"
rel="noopener noreferrer"
className="text-indigo-400 hover:underline"
>
v1 preview announcement
</a>{' '}
before proceeding.
</p>
</div>
)}
{upgradeRestrictedError && (
<Alert variant="warn">
<AlertDescription>
Tenant version upgrade has been restricted for this tenant.
Please contact us to request upgrade referencing tenant id:{' '}
{tenant.metadata.id}
</AlertDescription>
</Alert>
)}
<DialogFooter>
<Button
variant="outline"
onClick={() => setShowUpgradeModal(false)}
disabled={isPending}
>
Cancel
</Button>
<Button
onClick={() => {
updateTenant({
version: TenantVersion.V1,
});
}}
disabled={isPending}
>
{isPending ? <Spinner /> : null}
Confirm Upgrade
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</>
);
};

View File

@@ -2,7 +2,7 @@ import { Button } from '@/components/v1/ui/button';
import { Separator } from '@/components/v1/ui/separator';
import { TenantContextType } from '@/lib/outlet';
import { useState } from 'react';
import { useLocation, useNavigate, useOutletContext } from 'react-router-dom';
import { useOutletContext } from 'react-router-dom';
import { useApiError } from '@/lib/hooks';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import api, {
@@ -16,7 +16,14 @@ import { Label } from '@radix-ui/react-label';
import { Spinner } from '@/components/v1/ui/loading';
import { capitalize } from '@/lib/utils';
import { UpdateTenantForm } from './components/update-tenant-form';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/v1/ui/dialog';
import { Alert, AlertDescription, AlertTitle } from '@/components/v1/ui/alert';
export default function TenantSettings() {
const { tenant } = useOutletContext<TenantContextType>();
@@ -40,10 +47,8 @@ export default function TenantSettings() {
const TenantVersionSwitcher = () => {
const { tenant } = useOutletContext<TenantContextType>();
const selectedVersion = tenant.version;
const queryClient = useQueryClient();
const navigate = useNavigate();
const { pathname } = useLocation();
const [showDowngradeModal, setShowDowngradeModal] = useState(false);
const { handleApiError } = useApiError({});
@@ -56,41 +61,85 @@ const TenantVersionSwitcher = () => {
queryClient.invalidateQueries({
queryKey: queries.user.listTenantMemberships.queryKey,
});
window.location.reload();
},
onError: handleApiError,
});
const tenantVersions = Object.keys(TenantVersion) as Array<
keyof typeof TenantVersion
>;
// Only show for V1 tenants
if (tenant.version === TenantVersion.V0) {
return null;
}
return (
<div className="flex flex-col gap-y-2">
<h2 className="text-xl font-semibold leading-tight text-foreground">
Tenant Version
</h2>
<RadioGroup
disabled={isPending}
value={selectedVersion}
onValueChange={(value) => {
updateTenant({
version: value as TenantVersion,
});
<>
<div className="flex flex-col gap-y-2">
<h2 className="text-xl font-semibold leading-tight text-foreground">
Tenant Version
</h2>
<p className="text-sm text-muted-foreground">
You can downgrade your tenant to v0 if needed. Please help us improve
v1 by reporting any bugs in our{' '}
<a
href="https://github.com/hatchet-dev/hatchet/issues"
target="_blank"
rel="noopener noreferrer"
className="text-indigo-400 hover:underline"
>
Github issues.
</a>
</p>
<Button
onClick={() => setShowDowngradeModal(true)}
disabled={isPending}
variant="destructive"
className="w-fit"
>
{isPending ? <Spinner /> : null}
Downgrade to v0
</Button>
</div>
if (value === 'V1' && !pathname.includes('v1')) {
navigate('/v1' + pathname);
} else if (value === 'V0' && pathname.includes('v1')) {
navigate(pathname.replace('/v1', ''));
}
}}
>
{tenantVersions.map((version) => (
<div key={version} className="flex items-center space-x-2">
<RadioGroupItem value={version} id={version.toLowerCase()} />
<Label htmlFor={version.toLowerCase()}>{version}</Label>
<Dialog open={showDowngradeModal} onOpenChange={setShowDowngradeModal}>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Downgrade to v0</DialogTitle>
</DialogHeader>
<div className="space-y-4 py-4">
<Alert variant="warn">
<AlertTitle>Warning</AlertTitle>
<AlertDescription>
Downgrading to v0 will remove access to v1 features and may
affect your existing workflows. This action should only be taken
if absolutely necessary.
</AlertDescription>
</Alert>
</div>
))}
</RadioGroup>
</div>
<DialogFooter>
<Button
variant="outline"
onClick={() => setShowDowngradeModal(false)}
disabled={isPending}
>
Cancel
</Button>
<Button
variant="destructive"
onClick={() => {
updateTenant({
version: TenantVersion.V0,
});
}}
disabled={isPending}
>
{isPending ? <Spinner /> : null}
Confirm Downgrade
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</>
);
};

View File

@@ -1,6 +1,6 @@
import { DataTable } from '@/components/v1/molecules/data-table/data-table.tsx';
import { columns } from './v1/task-runs-columns';
import { useCallback, useState } from 'react';
import { useCallback, useMemo, useState } from 'react';
import { RowSelectionState, VisibilityState } from '@tanstack/react-table';
import { useQuery } from '@tanstack/react-query';
import invariant from 'tiny-invariant';
@@ -69,7 +69,7 @@ export function TaskRunsTable({
createdAfter: createdAfterProp,
initColumnVisibility = {},
filterVisibility = {},
refetchInterval = 100000,
refetchInterval = 5000,
showMetrics = false,
showCounts = true,
disableTaskRunPagination = false,
@@ -106,6 +106,7 @@ export function TaskRunsTable({
selectedRuns,
numPages,
isLoading: isTaskRunsLoading,
isFetching: isTaskRunsFetching,
refetch: refetchTaskRuns,
getRowId,
} = useTaskRuns({
@@ -120,6 +121,7 @@ export function TaskRunsTable({
metrics,
tenantMetrics,
isLoading: isMetricsLoading,
isFetching: isMetricsFetching,
refetch: refetchMetrics,
} = useMetrics({
workflow,
@@ -153,7 +155,12 @@ export function TaskRunsTable({
const hasTaskFiltersSelected = Object.values(v1TaskFilters).some(
(filter) => !!filter,
);
const isLoading = isTaskRunsLoading || isMetricsLoading;
const hasLoaded = useMemo(() => {
return !isTaskRunsLoading && !isMetricsLoading;
}, [isTaskRunsLoading, isMetricsLoading]);
const isFetching = !hasLoaded && (isTaskRunsFetching || isMetricsFetching);
return (
<>
@@ -197,7 +204,7 @@ export function TaskRunsTable({
code={JSON.stringify(tenantMetrics || '{}', null, 2)}
/>
)}
{isMetricsLoading && <Skeleton className="w-full h-36" />}
{isMetricsLoading && 'Loading...'}
</DialogContent>
</Dialog>
)}
@@ -319,7 +326,7 @@ export function TaskRunsTable({
)}
<DataTable
emptyState={<>No workflow runs found with the given filters.</>}
isLoading={isLoading}
isLoading={isFetching}
columns={columns(cf.setAdditionalMetadata, onTaskRunIdClick)}
columnVisibility={columnVisibility}
setColumnVisibility={setColumnVisibility}

View File

@@ -38,11 +38,8 @@ export const useMetrics = ({
const tenantMetrics = tenantMetricsQuery.data?.queues || {};
return {
isLoading:
metricsQuery.isLoading ||
metricsQuery.isFetching ||
tenantMetricsQuery.isLoading ||
tenantMetricsQuery.isFetching,
isLoading: metricsQuery.isLoading || tenantMetricsQuery.isLoading,
isFetching: metricsQuery.isFetching || tenantMetricsQuery.isFetching,
tenantMetrics,
metrics,
refetch: () => {

View File

@@ -10,8 +10,8 @@ export const usePagination = () => {
const pagination = useMemo(
() => ({
pageIndex: Number(searchParams.get(pageSizeParamName)) || 0,
pageSize: Number(searchParams.get(pageIndexParamName)) || 50,
pageIndex: Number(searchParams.get(pageIndexParamName)) || 0,
pageSize: Number(searchParams.get(pageSizeParamName)) || 50,
}),
[searchParams],
);

View File

@@ -94,6 +94,7 @@ export const useTaskRuns = ({
refetch: listTasksQuery.refetch,
isLoading: listTasksQuery.isLoading,
isError: listTasksQuery.isError,
isFetching: listTasksQuery.isFetching,
getRowId,
};
};

View File

@@ -52,7 +52,6 @@ export const routes: RouteObject[] = [
lazy: async () =>
import('./pages/onboarding/verify-email').then((res) => {
return {
loader: res.loader,
Component: res.default,
};
}),
@@ -62,7 +61,6 @@ export const routes: RouteObject[] = [
lazy: async () =>
import('./pages/authenticated').then((res) => {
return {
loader: res.loader,
Component: res.default,
};
}),
@@ -100,7 +98,6 @@ export const routes: RouteObject[] = [
lazy: async () =>
import('./pages/onboarding/invites').then((res) => {
return {
loader: res.loader,
Component: res.default,
};
}),
@@ -343,7 +340,6 @@ export const routes: RouteObject[] = [
lazy: async () =>
import('./pages/authenticated').then((res) => {
return {
loader: res.loader,
Component: res.default,
};
}),

View File

@@ -181,6 +181,9 @@ type ConfigFileRuntime struct {
QueueStepRunBuffer buffer.ConfigFileBuffer `mapstructure:"queueStepRunBuffer" json:"queueStepRunBuffer,omitempty"`
Monitoring ConfigFileMonitoring `mapstructure:"monitoring" json:"monitoring,omitempty"`
// PreventTenantVersionUpgrade controls whether the server prevents tenant version upgrades
PreventTenantVersionUpgrade bool `mapstructure:"preventTenantVersionUpgrade" json:"preventTenantVersionUpgrade,omitempty" default:"false"`
}
type InternalClientTLSConfigFile struct {
@@ -526,6 +529,7 @@ func BindAllEnv(v *viper.Viper) {
_ = v.BindEnv("runtime.bufferCreateWorkflowRuns", "SERVER_BUFFER_CREATE_WORKFLOW_RUNS")
_ = v.BindEnv("runtime.disableTenantPubs", "SERVER_DISABLE_TENANT_PUBS")
_ = v.BindEnv("runtime.maxInternalRetryCount", "SERVER_MAX_INTERNAL_RETRY_COUNT")
_ = v.BindEnv("runtime.preventTenantVersionUpgrade", "SERVER_PREVENT_TENANT_VERSION_UPGRADE")
// security check options
_ = v.BindEnv("securityCheck.enabled", "SERVER_SECURITY_CHECK_ENABLED")

View File

@@ -1619,6 +1619,7 @@ type Tenant struct {
WorkerPartitionId pgtype.Text `json:"workerPartitionId"`
DataRetentionPeriod string `json:"dataRetentionPeriod"`
SchedulerPartitionId pgtype.Text `json:"schedulerPartitionId"`
CanUpgradeV1 bool `json:"canUpgradeV1"`
}
type TenantAlertEmailGroup struct {

View File

@@ -99,7 +99,7 @@ VALUES (
),
COALESCE($4::text, '720h')
)
RETURNING id, "createdAt", "updatedAt", "deletedAt", version, name, slug, "analyticsOptOut", "alertMemberEmails", "controllerPartitionId", "workerPartitionId", "dataRetentionPeriod", "schedulerPartitionId"
RETURNING id, "createdAt", "updatedAt", "deletedAt", version, name, slug, "analyticsOptOut", "alertMemberEmails", "controllerPartitionId", "workerPartitionId", "dataRetentionPeriod", "schedulerPartitionId", "canUpgradeV1"
`
type CreateTenantParams struct {
@@ -131,6 +131,7 @@ func (q *Queries) CreateTenant(ctx context.Context, db DBTX, arg CreateTenantPar
&i.WorkerPartitionId,
&i.DataRetentionPeriod,
&i.SchedulerPartitionId,
&i.CanUpgradeV1,
)
return &i, err
}
@@ -370,7 +371,7 @@ func (q *Queries) GetEmailGroups(ctx context.Context, db DBTX, tenantid pgtype.U
const getInternalTenantForController = `-- name: GetInternalTenantForController :one
SELECT
id, "createdAt", "updatedAt", "deletedAt", version, name, slug, "analyticsOptOut", "alertMemberEmails", "controllerPartitionId", "workerPartitionId", "dataRetentionPeriod", "schedulerPartitionId"
id, "createdAt", "updatedAt", "deletedAt", version, name, slug, "analyticsOptOut", "alertMemberEmails", "controllerPartitionId", "workerPartitionId", "dataRetentionPeriod", "schedulerPartitionId", "canUpgradeV1"
FROM
"Tenant" as tenants
WHERE
@@ -395,6 +396,7 @@ func (q *Queries) GetInternalTenantForController(ctx context.Context, db DBTX, c
&i.WorkerPartitionId,
&i.DataRetentionPeriod,
&i.SchedulerPartitionId,
&i.CanUpgradeV1,
)
return &i, err
}
@@ -520,7 +522,7 @@ func (q *Queries) GetTenantAlertingSettings(ctx context.Context, db DBTX, tenant
const getTenantByID = `-- name: GetTenantByID :one
SELECT
id, "createdAt", "updatedAt", "deletedAt", version, name, slug, "analyticsOptOut", "alertMemberEmails", "controllerPartitionId", "workerPartitionId", "dataRetentionPeriod", "schedulerPartitionId"
id, "createdAt", "updatedAt", "deletedAt", version, name, slug, "analyticsOptOut", "alertMemberEmails", "controllerPartitionId", "workerPartitionId", "dataRetentionPeriod", "schedulerPartitionId", "canUpgradeV1"
FROM
"Tenant" as tenants
WHERE
@@ -544,13 +546,14 @@ func (q *Queries) GetTenantByID(ctx context.Context, db DBTX, id pgtype.UUID) (*
&i.WorkerPartitionId,
&i.DataRetentionPeriod,
&i.SchedulerPartitionId,
&i.CanUpgradeV1,
)
return &i, err
}
const getTenantBySlug = `-- name: GetTenantBySlug :one
SELECT
id, "createdAt", "updatedAt", "deletedAt", version, name, slug, "analyticsOptOut", "alertMemberEmails", "controllerPartitionId", "workerPartitionId", "dataRetentionPeriod", "schedulerPartitionId"
id, "createdAt", "updatedAt", "deletedAt", version, name, slug, "analyticsOptOut", "alertMemberEmails", "controllerPartitionId", "workerPartitionId", "dataRetentionPeriod", "schedulerPartitionId", "canUpgradeV1"
FROM
"Tenant" as tenants
WHERE
@@ -574,6 +577,7 @@ func (q *Queries) GetTenantBySlug(ctx context.Context, db DBTX, slug string) (*T
&i.WorkerPartitionId,
&i.DataRetentionPeriod,
&i.SchedulerPartitionId,
&i.CanUpgradeV1,
)
return &i, err
}
@@ -871,7 +875,7 @@ func (q *Queries) ListTenantMembers(ctx context.Context, db DBTX, tenantid pgtyp
const listTenants = `-- name: ListTenants :many
SELECT
id, "createdAt", "updatedAt", "deletedAt", version, name, slug, "analyticsOptOut", "alertMemberEmails", "controllerPartitionId", "workerPartitionId", "dataRetentionPeriod", "schedulerPartitionId"
id, "createdAt", "updatedAt", "deletedAt", version, name, slug, "analyticsOptOut", "alertMemberEmails", "controllerPartitionId", "workerPartitionId", "dataRetentionPeriod", "schedulerPartitionId", "canUpgradeV1"
FROM
"Tenant" as tenants
`
@@ -899,6 +903,7 @@ func (q *Queries) ListTenants(ctx context.Context, db DBTX) ([]*Tenant, error) {
&i.WorkerPartitionId,
&i.DataRetentionPeriod,
&i.SchedulerPartitionId,
&i.CanUpgradeV1,
); err != nil {
return nil, err
}
@@ -912,7 +917,7 @@ func (q *Queries) ListTenants(ctx context.Context, db DBTX) ([]*Tenant, error) {
const listTenantsByControllerPartitionId = `-- name: ListTenantsByControllerPartitionId :many
SELECT
id, "createdAt", "updatedAt", "deletedAt", version, name, slug, "analyticsOptOut", "alertMemberEmails", "controllerPartitionId", "workerPartitionId", "dataRetentionPeriod", "schedulerPartitionId"
id, "createdAt", "updatedAt", "deletedAt", version, name, slug, "analyticsOptOut", "alertMemberEmails", "controllerPartitionId", "workerPartitionId", "dataRetentionPeriod", "schedulerPartitionId", "canUpgradeV1"
FROM
"Tenant" as tenants
WHERE
@@ -948,6 +953,7 @@ func (q *Queries) ListTenantsByControllerPartitionId(ctx context.Context, db DBT
&i.WorkerPartitionId,
&i.DataRetentionPeriod,
&i.SchedulerPartitionId,
&i.CanUpgradeV1,
); err != nil {
return nil, err
}
@@ -961,7 +967,7 @@ func (q *Queries) ListTenantsByControllerPartitionId(ctx context.Context, db DBT
const listTenantsBySchedulerPartitionId = `-- name: ListTenantsBySchedulerPartitionId :many
SELECT
id, "createdAt", "updatedAt", "deletedAt", version, name, slug, "analyticsOptOut", "alertMemberEmails", "controllerPartitionId", "workerPartitionId", "dataRetentionPeriod", "schedulerPartitionId"
id, "createdAt", "updatedAt", "deletedAt", version, name, slug, "analyticsOptOut", "alertMemberEmails", "controllerPartitionId", "workerPartitionId", "dataRetentionPeriod", "schedulerPartitionId", "canUpgradeV1"
FROM
"Tenant" as tenants
WHERE
@@ -997,6 +1003,7 @@ func (q *Queries) ListTenantsBySchedulerPartitionId(ctx context.Context, db DBTX
&i.WorkerPartitionId,
&i.DataRetentionPeriod,
&i.SchedulerPartitionId,
&i.CanUpgradeV1,
); err != nil {
return nil, err
}
@@ -1010,7 +1017,7 @@ func (q *Queries) ListTenantsBySchedulerPartitionId(ctx context.Context, db DBTX
const listTenantsByTenantWorkerPartitionId = `-- name: ListTenantsByTenantWorkerPartitionId :many
SELECT
id, "createdAt", "updatedAt", "deletedAt", version, name, slug, "analyticsOptOut", "alertMemberEmails", "controllerPartitionId", "workerPartitionId", "dataRetentionPeriod", "schedulerPartitionId"
id, "createdAt", "updatedAt", "deletedAt", version, name, slug, "analyticsOptOut", "alertMemberEmails", "controllerPartitionId", "workerPartitionId", "dataRetentionPeriod", "schedulerPartitionId", "canUpgradeV1"
FROM
"Tenant" as tenants
WHERE
@@ -1046,6 +1053,7 @@ func (q *Queries) ListTenantsByTenantWorkerPartitionId(ctx context.Context, db D
&i.WorkerPartitionId,
&i.DataRetentionPeriod,
&i.SchedulerPartitionId,
&i.CanUpgradeV1,
); err != nil {
return nil, err
}
@@ -1414,7 +1422,7 @@ SET
"version" = COALESCE($4::"TenantMajorEngineVersion", "version")
WHERE
"id" = $5::uuid
RETURNING id, "createdAt", "updatedAt", "deletedAt", version, name, slug, "analyticsOptOut", "alertMemberEmails", "controllerPartitionId", "workerPartitionId", "dataRetentionPeriod", "schedulerPartitionId"
RETURNING id, "createdAt", "updatedAt", "deletedAt", version, name, slug, "analyticsOptOut", "alertMemberEmails", "controllerPartitionId", "workerPartitionId", "dataRetentionPeriod", "schedulerPartitionId", "canUpgradeV1"
`
type UpdateTenantParams struct {
@@ -1448,6 +1456,7 @@ func (q *Queries) UpdateTenant(ctx context.Context, db DBTX, arg UpdateTenantPar
&i.WorkerPartitionId,
&i.DataRetentionPeriod,
&i.SchedulerPartitionId,
&i.CanUpgradeV1,
)
return &i, err
}

View File

@@ -2199,6 +2199,7 @@ type Tenant struct {
WorkerPartitionId pgtype.Text `json:"workerPartitionId"`
DataRetentionPeriod string `json:"dataRetentionPeriod"`
SchedulerPartitionId pgtype.Text `json:"schedulerPartitionId"`
CanUpgradeV1 bool `json:"canUpgradeV1"`
}
type TenantAlertEmailGroup struct {

View File

@@ -604,6 +604,7 @@ CREATE TABLE "Tenant" (
"workerPartitionId" TEXT,
"dataRetentionPeriod" TEXT NOT NULL DEFAULT '720h',
"schedulerPartitionId" TEXT,
"canUpgradeV1" BOOLEAN NOT NULL DEFAULT true,
CONSTRAINT "Tenant_pkey" PRIMARY KEY ("id")
);