Skip to content

Commit

Permalink
Modify registration flow (#80)
Browse files Browse the repository at this point in the history
  • Loading branch information
nikrooz authored Oct 31, 2024
1 parent a605493 commit b009f77
Show file tree
Hide file tree
Showing 28 changed files with 1,097 additions and 395 deletions.
2 changes: 1 addition & 1 deletion apps/web-ui/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { vitePlugin as remix } from '@remix-run/dev';
import { defineConfig, loadEnv } from 'vite';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';

const BASE_URL = '/ui/';
const BASE_URL = '/ui';

export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '');
Expand Down
30 changes: 17 additions & 13 deletions libs/data-access/admin-api-fixtures/src/lib/adminApiDb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,24 @@ import { factory, manyOf, oneOf, primaryKey } from '@mswjs/data';
import { faker } from '@faker-js/faker';

faker.seed(Date.now());
const names = faker.helpers.uniqueArray(faker.word.noun, 1000);

let index = 0;
export function getName() {
return names[index++];
}

export const adminApiDb = factory({
handler: {
name: primaryKey(() => `${faker.hacker.noun()}`),
name: primaryKey(() => `${getName()}`),
ty: () =>
faker.helpers.arrayElement(['Exclusive', 'Shared', 'Workflow'] as const),
input_description: () =>
'one of ["none", "value of content-type \'application/json\'"]',
output_description: () => "value of content-type 'application/json'",
},
service: {
name: primaryKey(() => `${faker.hacker.noun()}Service`),
name: primaryKey<string>(() => `${getName()}Service`),
handlers: manyOf('handler'),
deployment: oneOf('deployment'),
ty: () =>
Expand All @@ -30,23 +36,21 @@ export const adminApiDb = factory({
deployment: {
id: primaryKey(() => `dp_${faker.string.nanoid(27)}`),
services: manyOf('service'),
dryRun: Boolean,
endpoint: () => faker.internet.url(),
},
});

const isE2E = process.env['SCENARIO'] === 'E2E';

if (!isE2E) {
const services = Array(3)
.fill(null)
.map(() => adminApiDb.service.create());
Array(30)
.fill(null)
.map(() =>
adminApiDb.deployment.create({
services: services.slice(
0,
Math.floor(Math.random() * services.length + 1)
),
})
);
.map(() => {
const deployment = adminApiDb.deployment.create();
Array(Math.floor(Math.random() * 3 + 1))
.fill(null)
.map(() => adminApiDb.service.create({ deployment }));
return deployment;
});
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as adminApi from '@restate/data-access/admin-api/spec';
import { http, HttpResponse } from 'msw';
import { adminApiDb } from './adminApiDb';
import { faker } from '@faker-js/faker';
import { adminApiDb, getName } from './adminApiDb';

type FormatParameterWithColon<S extends string> =
S extends `${infer A}{${infer P}}${infer B}` ? `${A}:${P}${B}` : S;
Expand All @@ -15,12 +14,26 @@ const listDeploymentsHandler = http.get<
adminApi.operations['list_deployments']['responses']['200']['content']['application/json'],
GetPath<'/deployments'>
>('/deployments', async () => {
const deployments = adminApiDb.deployment.getAll();
const deployments = adminApiDb.deployment
.getAll()
.filter(({ dryRun }) => !dryRun);
return HttpResponse.json({
deployments: deployments.map((deployment) => ({
id: deployment.id,
services: deployment.services,
uri: faker.internet.url(),
services: adminApiDb.service
.findMany({
where: { deployment: { id: { equals: deployment.id } } },
})
.map((service) => ({
name: service.name,
deployment_id: deployment.id,
public: service.public,
revision: service.revision,
ty: service.ty,
idempotency_retention: service.idempotency_retention,
workflow_completion_retention: service.idempotency_retention,
})),
uri: deployment.endpoint,
protocol_type: 'RequestResponse',
created_at: new Date().toISOString(),
http_version: 'HTTP/2.0',
Expand All @@ -37,10 +50,68 @@ const registerDeploymentHandler = http.post<
GetPath<'/deployments'>
>('/deployments', async ({ request }) => {
const requestBody = await request.json();
const newDeployment = adminApiDb.deployment.create({});
const requestEndpoint =
'uri' in requestBody ? requestBody.uri : requestBody.arn;
const existingDeployment = adminApiDb.deployment.findFirst({
where: {
endpoint: {
equals: requestEndpoint,
},
dryRun: {
equals: true,
},
},
});

if (existingDeployment) {
adminApiDb.deployment.update({
where: {
id: {
equals: existingDeployment.id,
},
},
data: { dryRun: false },
});

return HttpResponse.json({
id: existingDeployment.id,
services: adminApiDb.service
.findMany({
where: { deployment: { id: { equals: existingDeployment.id } } },
})
.map((service) => ({
name: service.name,
deployment_id: service.deployment!.id,
public: service.public,
revision: service.revision,
ty: service.ty,
idempotency_retention: service.idempotency_retention,
workflow_completion_retention: service.idempotency_retention,
handlers: service.handlers.map((handler) => ({
name: handler.name,
ty: handler.ty,
input_description: handler.input_description,
output_description: handler.output_description,
})),
})),
});
}

const newDeployment = adminApiDb.deployment.create({
dryRun: requestBody.dry_run,
endpoint: requestEndpoint,
});
const services = Array(3)
.fill(null)
.map(() => adminApiDb.service.create({ deployment: newDeployment }));
.map(() =>
adminApiDb.service.create({
deployment: newDeployment,
name: `${getName()}Service`,
handlers: Array(Math.floor(Math.random() * 6))
.fill(null)
.map(() => adminApiDb.handler.create({ name: getName() })),
})
);

return HttpResponse.json({
id: newDeployment.id,
Expand Down
1 change: 1 addition & 0 deletions libs/features/explainers/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './lib/ServiceDeployment';
export * from './lib/Service';
export * from './lib/ServiceType';
4 changes: 3 additions & 1 deletion libs/features/explainers/src/lib/ServiceDeployment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { PropsWithChildren } from 'react';

export function ServiceDeploymentExplainer({
children,
}: PropsWithChildren<unknown>) {
className,
}: PropsWithChildren<{ className?: string }>) {
return (
<InlineTooltip
className={className}
title="Service Deployment"
description={
<p>
Expand Down
47 changes: 47 additions & 0 deletions libs/features/explainers/src/lib/ServiceType.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { InlineTooltip } from '@restate/ui/tooltip';
import { ComponentProps, PropsWithChildren } from 'react';
import * as adminApi from '@restate/data-access/admin-api/spec';
type ServiceType = adminApi.components['schemas']['ServiceMetadata']['ty'];

const TITLES: Record<ServiceType, string> = {
Service: 'Service',
VirtualObject: 'Virtual object',
Workflow: 'Workflow',
};

const DESCRIPTIONS: Record<ServiceType, string> = {
Service:
'Services expose a collection of durably executed handlers. They do not have any concurrency limits nor K/V store.',
VirtualObject:
'Virtual objects expose a set of durably executed handlers with access to K/V state stored in Restate. To ensure consistent writes to the state, Restate provides concurrency guarantees for Virtual Objects.',
Workflow:
'A workflow is a special type of Virtual Object that can be used to implement a set of steps that need to be executed durably. Workflows have additional capabilities such as signaling, querying, additional invocation options',
};

const LEARN_MORE: Record<ServiceType, string> = {
Service: 'https://docs.restate.dev/concepts/services/#services-1',
VirtualObject: 'https://docs.restate.dev/concepts/services/#virtual-objects',
Workflow: 'https://docs.restate.dev/concepts/services/#workflows',
};
export function ServiceTypeExplainer({
children,
className,
variant,
type,
}: PropsWithChildren<{
className?: string;
variant?: ComponentProps<typeof InlineTooltip>['variant'];
type: ServiceType;
}>) {
return (
<InlineTooltip
variant={variant}
className={className}
title={TITLES[type]}
description={<p>{DESCRIPTIONS[type]}</p>}
learnMoreHref={LEARN_MORE[type]}
>
{children}
</InlineTooltip>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,10 @@ import {
FormFieldInput,
} from '@restate/ui/form-field';
import { IconName, Icon } from '@restate/ui/icons';
import { useListData } from 'react-stately';
import { useRegisterDeploymentContext } from './Context';

export function AdditionalHeaders() {
const list = useListData<{ key: string; value: string; index: number }>({
initialItems: [{ key: '', value: '', index: 0 }],
getKey: (item) => item.index,
});
const { additionalHeaders: list } = useRegisterDeploymentContext();

return (
<FormFieldGroup className="h-auto flex gap-1 flex-col items-start">
Expand All @@ -23,7 +20,7 @@ export function AdditionalHeaders() {
Headers added to the discover/invoke requests to the deployment.
</span>
</FormFieldLabel>
{list.items.map((item) => (
{list?.items.map((item) => (
<div key={item.index} className="flex gap-1.5 items-center w-full">
<FormFieldInput
name="key"
Expand Down Expand Up @@ -80,7 +77,7 @@ export function AdditionalHeaders() {
))}
<Button
onClick={() =>
list.append({
list?.append({
key: '',
value: '',
index: Math.floor(Math.random() * 1000000),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ export function AssumeARNRole() {
<>
<span slot="title">Assume role ARN</span>
<span slot="description" className="leading-5 text-code block">
Optional ARN of a role to assume when invoking the addressed Lambda,
to support role chaining
ARN of a role to use when invoking the Lambda
</span>
</>
}
Expand Down
Loading

0 comments on commit b009f77

Please sign in to comment.