Skip to content

Commit

Permalink
Simplify deploy flow (#875)
Browse files Browse the repository at this point in the history
* Simplify deploy flow

* Deployments

* remove releases
  • Loading branch information
Janpot authored Sep 2, 2022
1 parent 7bbe987 commit 47684a0
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 464 deletions.
8 changes: 8 additions & 0 deletions packages/toolpad-app/pages/api/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
findActiveDeployment,
findLastRelease,
deleteApp,
deploy,
getDeployments,
} from '../../src/server/data';
import { hasOwnProperty } from '../../src/utils/collections';

Expand Down Expand Up @@ -117,6 +119,9 @@ const rpcServer = {
getActiveDeployments: createMethod<typeof getActiveDeployments>((params) => {
return getActiveDeployments(...params);
}),
getDeployments: createMethod<typeof getDeployments>((params) => {
return getDeployments(...params);
}),
getApp: createMethod<typeof getApp>((params) => {
return getApp(...params);
}),
Expand Down Expand Up @@ -155,6 +160,9 @@ const rpcServer = {
createDeployment: createMethod<typeof createDeployment>((params) => {
return createDeployment(...params);
}),
deploy: createMethod<typeof deploy>((params) => {
return deploy(...params);
}),
saveDom: createMethod<typeof saveDom>((params) => {
return saveDom(...params);
}),
Expand Down
123 changes: 68 additions & 55 deletions packages/toolpad-app/src/server/data.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,37 @@
import { NodeId, BindableAttrValue } from '@mui/toolpad-core';
import * as _ from 'lodash-es';
import {
App,
DomNodeAttributeType,
PrismaClient,
Release,
Prisma,
} from '../../prisma/generated/client';
import * as prisma from '../../prisma/generated/client';
import { ServerDataSource, ApiResult, VersionOrPreview } from '../types';
import serverDataSources from '../toolpadDataSources/server';
import * as appDom from '../appDom';
import { omit } from '../utils/immutability';
import { asArray } from '../utils/collections';
import { decryptSecret, encryptSecret } from './secrets';
import applyTransform from './applyTransform';
import { excludeFields } from '../utils/prisma';

// See https://github.com/prisma/prisma/issues/5042#issuecomment-1104679760
function excludeFields<T, K extends (keyof T)[]>(
fields: T,
excluded: K,
): Record<Exclude<keyof T, K[number]>, boolean> {
const result = {} as Record<Exclude<keyof T, K[number]>, boolean>;
for (const key of Object.keys(fields)) {
if (!excluded.includes(key as any)) {
result[key as Exclude<keyof T, K[number]>] = true;
}
}
return result;
}
const SELECT_RELEASE_META = excludeFields(prisma.Prisma.ReleaseScalarFieldEnum, ['snapshot']);
const SELECT_APP_META = excludeFields(prisma.Prisma.AppScalarFieldEnum, ['dom']);

const SELECT_RELEASE_META = excludeFields(Prisma.ReleaseScalarFieldEnum, ['snapshot']);
const SELECT_APP_META = excludeFields(Prisma.AppScalarFieldEnum, ['dom']);
export type AppMeta = Omit<prisma.App, 'dom'>;

export type AppMeta = Omit<App, 'dom'>;

function getPrismaClient(): PrismaClient {
function getPrismaClient(): prisma.PrismaClient {
if (process.env.NODE_ENV === 'production') {
return new PrismaClient();
return new prisma.PrismaClient();
}

// avoid Next.js dev server from creating too many prisma clients
// See https://github.com/prisma/prisma/issues/1983
if (!(globalThis as any).prisma) {
(globalThis as any).prisma = new PrismaClient();
(globalThis as any).prisma = new prisma.PrismaClient();
}

return (globalThis as any).prisma;
}

const prisma = getPrismaClient();
const prismaClient = getPrismaClient();

function deserializeValue(dbValue: string, type: DomNodeAttributeType): unknown {
function deserializeValue(dbValue: string, type: prisma.DomNodeAttributeType): unknown {
const serialized = type === 'secret' ? decryptSecret(dbValue) : dbValue;
return serialized.length <= 0 ? undefined : JSON.parse(serialized);
}
Expand Down Expand Up @@ -90,7 +71,7 @@ function decryptSecrets(dom: appDom.AppDom): appDom.AppDom {
}

export async function saveDom(appId: string, app: appDom.AppDom): Promise<void> {
await prisma.app.update({
await prismaClient.app.update({
where: {
id: appId,
},
Expand All @@ -100,7 +81,7 @@ export async function saveDom(appId: string, app: appDom.AppDom): Promise<void>
}

async function loadPreviewDomLegacy(appId: string): Promise<appDom.AppDom> {
const dbNodes = await prisma.domNode.findMany({
const dbNodes = await prismaClient.domNode.findMany({
where: { appId },
include: { attributes: true },
});
Expand Down Expand Up @@ -146,7 +127,7 @@ async function loadPreviewDomLegacy(appId: string): Promise<appDom.AppDom> {
}

async function loadPreviewDom(appId: string): Promise<appDom.AppDom> {
const { dom } = await prisma.app.findUniqueOrThrow({
const { dom } = await prismaClient.app.findUniqueOrThrow({
where: { id: appId },
});

Expand All @@ -158,7 +139,7 @@ async function loadPreviewDom(appId: string): Promise<appDom.AppDom> {
}

export async function getApps(): Promise<AppMeta[]> {
return prisma.app.findMany({
return prismaClient.app.findMany({
orderBy: {
editedAt: 'desc',
},
Expand All @@ -167,14 +148,14 @@ export async function getApps(): Promise<AppMeta[]> {
}

export async function getActiveDeployments() {
return prisma.deployment.findMany({
return prismaClient.deployment.findMany({
distinct: ['appId'],
orderBy: { createdAt: 'desc' },
});
}

export async function getApp(id: string): Promise<AppMeta | null> {
return prisma.app.findUnique({ where: { id }, select: SELECT_APP_META });
return prismaClient.app.findUnique({ where: { id }, select: SELECT_APP_META });
}

function createDefaultDom(): appDom.AppDom {
Expand All @@ -198,9 +179,9 @@ export interface CreateAppOptions {
dom?: appDom.AppDom | null;
}

export async function createApp(name: string, opts: CreateAppOptions = {}): Promise<App> {
return prisma.$transaction(async () => {
const app = await prisma.app.create({
export async function createApp(name: string, opts: CreateAppOptions = {}): Promise<prisma.App> {
return prismaClient.$transaction(async () => {
const app = await prismaClient.app.create({
data: { name },
});

Expand All @@ -213,7 +194,7 @@ export async function createApp(name: string, opts: CreateAppOptions = {}): Prom
}

export async function updateApp(appId: string, name: string): Promise<void> {
await prisma.app.update({
await prismaClient.app.update({
where: {
id: appId,
},
Expand All @@ -226,7 +207,7 @@ export async function updateApp(appId: string, name: string): Promise<void> {
}

export async function deleteApp(id: string): Promise<void> {
await prisma.app.delete({
await prismaClient.app.delete({
where: { id },
select: {
// Only return the id to reduce amount of data returned from the db
Expand All @@ -235,19 +216,21 @@ export async function deleteApp(id: string): Promise<void> {
});
}

interface CreateReleaseParams {
export interface CreateReleaseParams {
description: string;
}

export type ReleaseMeta = Pick<prisma.Release, keyof typeof SELECT_RELEASE_META>;

async function findLastReleaseInternal(appId: string) {
return prisma.release.findFirst({
return prismaClient.release.findFirst({
where: { appId },
orderBy: { version: 'desc' },
});
}

export async function findLastRelease(appId: string) {
return prisma.release.findFirst({
export async function findLastRelease(appId: string): Promise<ReleaseMeta | null> {
return prismaClient.release.findFirst({
where: { appId },
orderBy: { version: 'desc' },
select: SELECT_RELEASE_META,
Expand All @@ -257,14 +240,14 @@ export async function findLastRelease(appId: string) {
export async function createRelease(
appId: string,
{ description }: CreateReleaseParams,
): Promise<Pick<Release, keyof typeof SELECT_RELEASE_META>> {
): Promise<ReleaseMeta> {
const currentDom = await loadPreviewDom(appId);
const snapshot = Buffer.from(JSON.stringify(currentDom), 'utf-8');

const lastRelease = await findLastReleaseInternal(appId);
const versionNumber = lastRelease ? lastRelease.version + 1 : 1;

const release = await prisma.release.create({
const release = await prismaClient.release.create({
select: SELECT_RELEASE_META,
data: {
appId,
Expand All @@ -277,8 +260,8 @@ export async function createRelease(
return release;
}

export async function getReleases(appId: string) {
return prisma.release.findMany({
export async function getReleases(appId: string): Promise<ReleaseMeta[]> {
return prismaClient.release.findMany({
where: { appId },
select: SELECT_RELEASE_META,
orderBy: {
Expand All @@ -287,15 +270,31 @@ export async function getReleases(appId: string) {
});
}

export async function getRelease(appId: string, version: number) {
return prisma.release.findUnique({
export async function getRelease(appId: string, version: number): Promise<ReleaseMeta | null> {
return prismaClient.release.findUnique({
where: { release_app_constraint: { appId, version } },
select: SELECT_RELEASE_META,
});
}

export async function createDeployment(appId: string, version: number) {
return prisma.deployment.create({
export type Deployment = prisma.Deployment & {
release: ReleaseMeta;
};

export function getDeployments(appId: string): Promise<Deployment[]> {
return prismaClient.deployment.findMany({
where: { appId },
orderBy: { createdAt: 'desc' },
include: {
release: {
select: SELECT_RELEASE_META,
},
},
});
}

export async function createDeployment(appId: string, version: number): Promise<Deployment> {
return prismaClient.deployment.create({
data: {
app: {
connect: { id: appId },
Expand All @@ -304,11 +303,25 @@ export async function createDeployment(appId: string, version: number) {
connect: { release_app_constraint: { appId, version } },
},
},
include: {
release: {
select: SELECT_RELEASE_META,
},
},
});
}

export async function findActiveDeployment(appId: string) {
return prisma.deployment.findFirst({
export async function deploy(
appId: string,
releaseInput: CreateReleaseParams,
): Promise<Deployment> {
const release = await createRelease(appId, releaseInput);
const deployment = await createDeployment(appId, release.version);
return deployment;
}

export async function findActiveDeployment(appId: string): Promise<Deployment | null> {
return prismaClient.deployment.findFirst({
where: { appId },
orderBy: { createdAt: 'desc' },
include: {
Expand All @@ -320,7 +333,7 @@ export async function findActiveDeployment(appId: string) {
}

export async function loadReleaseDom(appId: string, version: number): Promise<appDom.AppDom> {
const release = await prisma.release.findUnique({
const release = await prismaClient.release.findUnique({
where: { release_app_constraint: { appId, version } },
});
if (!release) {
Expand Down
Loading

0 comments on commit 47684a0

Please sign in to comment.