Skip to content

Commit

Permalink
Remove getDbSchemaName, getPrismaPath and migration things on adapter (
Browse files Browse the repository at this point in the history
…#5287)

* Remove getDbSchemaName, getPrismaPath and migration things on adapter

* Remove an await

* Fix a thing

* Fix things

* Update a changeset
  • Loading branch information
emmatown authored Mar 30, 2021
1 parent af5f31f commit 95fefaf
Show file tree
Hide file tree
Showing 16 changed files with 33 additions and 173 deletions.
3 changes: 2 additions & 1 deletion .changeset/gentle-flies-invent.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
'@keystone-next/keystone': major
'@keystone-next/types': major
'@keystone-next/test-utils-legacy': patch
---

Removed the `none` case in `MigrationAction` and require that the PrismaClient is passed to be able to connect to the database for the `none-skip-client-generation` case.
Removed `migrationAction` argument to `createSystem` and require that the PrismaClient is passed to `createSystem` to be able to connect to the database.
6 changes: 6 additions & 0 deletions .changeset/great-tables-juggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@keystone-next/keystone': major
'@keystone-next/adapter-prisma-legacy': major
---

Removed `getDbSchemaName` and `getPrismaPath` database adapter options. To change the database schema that Keystone uses, you can add `?schema=whatever` to the database url.
5 changes: 5 additions & 0 deletions .changeset/two-owls-pump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@keystone-next/adapter-prisma-legacy': major
---

Removed migrationMode and all migration related methods on the adapter and instead require that a prisma client is passed to the adapter to be able to connect to the database
10 changes: 0 additions & 10 deletions docs-next/pages/apis/config.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,8 @@ As well as these common options, each adapter supports a number of optional adva
Advanced configuration:

- `enableLogging` (default: `false`): Enable logging from the Prisma client.
- `getPrismaPath` (default: `() => '.keystone/prisma'` ): Set the location of the generated Prisma schema and client.
- `getDbSchemaName` (default: `() => 'public'` ): Set the schema named used in the database.
- `useMigrations` (default: `false`): Determines whether to use migrations or automatically force-update the database with the latest schema and potentially lose data.

The functions for `getPrismaPath` and `getDbSchemaName` are each provided with the generated Prisma schema as a `string` in the `{ prismaSchema }` argument.

```typescript
export default config({
db: {
Expand All @@ -83,8 +79,6 @@ export default config({
onConnect: async context => { /* ... */ },
// Optional advanced configuration
enableLogging: true,
getPrismaPath: ({ prismaSchema }) => '.prisma',
getDbSchemaName: ({ prismaSchema }) => 'prisma',
useMigrations: true,
},
/* ... */
Expand All @@ -99,11 +93,8 @@ To use this option you must also set `{ experimental: { prismaSqlite: true } }`.
Advanced configuration:

- `enableLogging` (default: `false`): Enable logging from the Prisma client.
- `getPrismaPath` (default: `() => '.keystone/prisma'` ): Set the location of the generated Prisma schema and client.
- `useMigrations` (default: `false`): Determines whether to use migrations or automatically force-update the database with the latest schema and potentially lose data.

The function for `getPrismaPath` is provided with the generated Prisma schema as a `string` in the `{ prismaSchema }` argument.

```typescript
export default config({
db: {
Expand All @@ -112,7 +103,6 @@ export default config({
onConnect: async context => { /* ... */ },
// Optional advanced configuration
enableLogging: true,
getPrismaPath: ({ prismaSchema }) => '.prisma',
useMigrations: true,
},
/* ... */
Expand Down
12 changes: 2 additions & 10 deletions packages-next/keystone/src/lib/createKeystone.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
// @ts-ignore
import { Keystone } from '@keystone-next/keystone-legacy';
import { PrismaAdapter } from '@keystone-next/adapter-prisma-legacy';
import type { KeystoneConfig, BaseKeystone, MigrationAction } from '@keystone-next/types';
import type { KeystoneConfig, BaseKeystone } from '@keystone-next/types';

export function createKeystone(
config: KeystoneConfig,
migrationAction: MigrationAction,
prismaClient?: any
) {
export function createKeystone(config: KeystoneConfig, prismaClient?: any) {
// Note: For backwards compatibility we may want to expose
// this as a public API so that users can start their transition process
// by using this pattern for creating their Keystone object before using
Expand All @@ -16,8 +12,6 @@ export function createKeystone(
let adapter;
if (db.adapter === 'prisma_postgresql') {
adapter = new PrismaAdapter({
migrationMode:
migrationAction === 'dev' ? (db.useMigrations ? 'dev' : 'prototype') : migrationAction,
prismaClient,
...db,
provider: 'postgresql',
Expand All @@ -30,8 +24,6 @@ export function createKeystone(
}
adapter = new PrismaAdapter({
prismaClient,
migrationMode:
migrationAction === 'dev' ? (db.useMigrations ? 'dev' : 'prototype') : migrationAction,
...db,
provider: 'sqlite',
});
Expand Down
10 changes: 3 additions & 7 deletions packages-next/keystone/src/lib/createSystem.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import type { KeystoneConfig, MigrationAction } from '@keystone-next/types';
import type { KeystoneConfig } from '@keystone-next/types';

import { createGraphQLSchema } from './createGraphQLSchema';
import { makeCreateContext } from './createContext';
import { createKeystone } from './createKeystone';

export function createSystem(
config: KeystoneConfig,
migrationAction: MigrationAction,
prismaClient?: any
) {
const keystone = createKeystone(config, migrationAction, prismaClient);
export function createSystem(config: KeystoneConfig, prismaClient?: any) {
const keystone = createKeystone(config, prismaClient);

const graphQLSchema = createGraphQLSchema(config, keystone, 'public');

Expand Down
2 changes: 1 addition & 1 deletion packages-next/keystone/src/scripts/build/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export async function build(cwd: string) {

const config = initConfig(requireSource(CONFIG_PATH).default);

const { keystone, graphQLSchema } = createSystem(config, 'none-skip-client-generation');
const { keystone, graphQLSchema } = createSystem(config);

await validateCommittedArtifacts(graphQLSchema, keystone, cwd);

Expand Down
2 changes: 1 addition & 1 deletion packages-next/keystone/src/scripts/postinstall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { CONFIG_PATH } from './utils';
export async function postinstall(cwd: string, shouldFix: boolean) {
const config = initConfig(requireSource(CONFIG_PATH).default);

const { keystone, graphQLSchema } = createSystem(config, 'none-skip-client-generation');
const { keystone, graphQLSchema } = createSystem(config);

if (shouldFix) {
await generateCommittedArtifacts(graphQLSchema, keystone, cwd);
Expand Down
2 changes: 1 addition & 1 deletion packages-next/keystone/src/scripts/prisma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { CONFIG_PATH } from './utils';
export async function prisma(cwd: string, args: string[]) {
const config = initConfig(requireSource(CONFIG_PATH).default);

const { keystone, graphQLSchema } = createSystem(config, 'none-skip-client-generation');
const { keystone, graphQLSchema } = createSystem(config);

await validateCommittedArtifacts(graphQLSchema, keystone, cwd);
await generateNodeModulesArtifacts(graphQLSchema, keystone, cwd);
Expand Down
8 changes: 2 additions & 6 deletions packages-next/keystone/src/scripts/run/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const dev = async (cwd: string) => {
const config = initConfig(requireSource(CONFIG_PATH).default);
const initKeystone = async () => {
{
const { keystone, graphQLSchema } = createSystem(config, 'none-skip-client-generation');
const { keystone, graphQLSchema } = createSystem(config);

console.log('✨ Generating GraphQL and Prisma schemas');
const prismaSchema = (await generateCommittedArtifacts(graphQLSchema, keystone, cwd)).prisma;
Expand All @@ -48,11 +48,7 @@ export const dev = async (cwd: string) => {

const prismaClient = requirePrismaClient(cwd);

const { keystone, graphQLSchema, createContext } = createSystem(
config,
'none-skip-client-generation',
prismaClient
);
const { keystone, graphQLSchema, createContext } = createSystem(config, prismaClient);

console.log('✨ Connecting to the database');
await keystone.connect({ context: createContext().sudo() });
Expand Down
6 changes: 1 addition & 5 deletions packages-next/keystone/src/scripts/run/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@ export const start = async (cwd: string) => {
throw new Error('keystone-next build must be run before running keystone-next start');
}
const config = initConfig(require(apiFile).config);
const { keystone, graphQLSchema, createContext } = createSystem(
config,
'none-skip-client-generation',
requirePrismaClient(cwd)
);
const { keystone, graphQLSchema, createContext } = createSystem(config, requirePrismaClient(cwd));

console.log('✨ Connecting to the database');
await keystone.connect({ context: createContext().sudo() });
Expand Down
3 changes: 0 additions & 3 deletions packages-next/types/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,11 @@ export type DatabaseConfig = DatabaseCommon &
adapter: 'prisma_postgresql';
useMigrations?: boolean;
enableLogging?: boolean;
getPrismaPath?: (arg: { prismaSchema: any }) => string;
getDbSchemaName?: (arg: { prismaSchema: any }) => string;
}
| {
adapter: 'prisma_sqlite';
useMigrations?: boolean;
enableLogging?: boolean;
getPrismaPath?: (arg: { prismaSchema: any }) => string;
}
);

Expand Down
2 changes: 0 additions & 2 deletions packages-next/types/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,3 @@ export function getGqlNames({
relateToOneInputName: `${_itemQueryName}RelateToOneInput`,
};
}

export type MigrationAction = 'none-skip-client-generation' | 'dev';
20 changes: 0 additions & 20 deletions packages/adapter-prisma/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,32 +35,12 @@ _**Default:**_ `DATABASE_URL`
The connection string for your database, in the form `postgres://<user>:<password>@<host>:<port>/<dbname>`.
By default it will use the value of the environment variable `DATABASE_URL`. You can learn more about the connection string format used in the [Prisma docs](https://www.prisma.io/docs/reference/database-connectors/connection-urls).

### `getPrismaPath`

_**Default:**_ `({ prismaSchema }) => '.prisma'`

A function which returns a directory name for storing the generated Prisma schema and client.

### `getDbSchemaName`

_**Default:**_ `({ prismaSchema }) => 'public'`

A function which returns a database schema name to use for storage of all Keystone tables in your database.

> You can also set the schema name by including the suffix `?schema=...` in your `DATABASE_URL` or `url`. In this case you should set this value to `() => null`.
### `enableLogging`

_**Default:**_ `false`

Enables logging at the [`query`](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client/logging#overview) level in the Prisma client.

### `migrationMode`

_**Default:**_ `'dev'`

Controls how and when migrations are applied. One of `'dev'`, `'prototype'`, `'createOnly'`, or `'none'`. In `prototype` mode, `prisma db push` is used to sync the database tables without generating migration files. In `dev` mode, migrations files are generated and applied whenever the schema changes. In `createOnly` mode, migrations are generated but not applied. In `none` mode, no migrations are generated or applied.

## Setup

Before running Keystone with the Prisma adapter you will need to have a PostgreSQL database to connect to.
Expand Down
105 changes: 7 additions & 98 deletions packages/adapter-prisma/src/adapter-prisma.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,17 @@
import fs from 'fs';
import path from 'path';
import pWaterfall from 'p-waterfall';
import { getGenerator, formatSchema } from '@prisma/sdk';
import { formatSchema } from '@prisma/sdk';
import { defaultObj, mapKeys, identity, flatten } from '@keystone-next/utils-legacy';
import {
runPrototypeMigrations,
devMigrations,
deployMigrations,
// eslint-disable-next-line import/no-unresolved
} from './migrations';

class PrismaAdapter {
constructor(config = {}) {
this.config = { ...config };
this.listAdapters = {};
this.listAdapterClass = undefined;
this._prismaClient = config.prismaClient;

this.listAdapterClass = PrismaListAdapter;
this.name = 'prisma';
this.provider = this.config.provider || 'postgresql';
this.migrationMode = this.config.migrationMode || 'prototype';

this.getPrismaPath = this.config.getPrismaPath || (() => '.prisma');
this.getDbSchemaName = this.config.getDbSchemaName || (() => 'public');
this.enableLogging = this.config.enableLogging || false;
this.url = this.config.url || process.env.DATABASE_URL;
}
Expand Down Expand Up @@ -70,104 +58,25 @@ class PrismaAdapter {
}
}

async _prepareSchema(rels) {
const clientDir = 'generated-client';
const prismaSchema = await this._generatePrismaSchema({ rels, clientDir });
// See if there is a prisma client available for this hash
const prismaPath = this.getPrismaPath({ prismaSchema });
this.schemaPath = path.join(prismaPath, 'schema.prisma');
this.clientPath = path.resolve(`${prismaPath}/${clientDir}`);
this.dbSchemaName = this.getDbSchemaName({ prismaSchema });
this.prismaSchema = prismaSchema;
return { prismaSchema };
}

_url() {
// By default we put `schema=public` onto all `DATABASE_URL` values.
// If this isn't what a user wants, they can update `getSchemaName` to return either
// a different dbSchemaName, or null if they just want to use the DATABASE_URL as it is.
// TODO: Should we default to 'public' or null?
if (this.provider === 'postgresql') {
return this.dbSchemaName ? `${this.url}?schema=${this.dbSchemaName}` : this.url;
} else if (this.provider === 'sqlite') {
return this.url;
}
}

async deploy(rels) {
// Apply any migrations which haven't already been applied
await this._prepareSchema(rels);
await deployMigrations(this._url(), path.resolve(this.schemaPath));
}

async _getPrismaClient({ rels }) {
if (this._prismaClient) {
return this._prismaClient;
}
await this._generateClient(rels);
return require(this.clientPath).PrismaClient;
}

async _connect({ rels }) {
async _connect() {
// the adapter was already connected since we have a prisma client
// it may have been disconnected since it was connected though
// so connect but don't regenerate the prisma client
if (this.prisma) {
await this.prisma.$connect();
return;
}
const PrismaClient = await this._getPrismaClient({ rels });
if (!this.config.prismaClient) {
throw new Error('You must pass the prismaClient option to connect to a database');
}
const PrismaClient = this.config.prismaClient;
this.prisma = new PrismaClient({
log: this.enableLogging && ['query'],
datasources: { [this.provider]: { url: this._url() } },
datasources: { [this.provider]: { url: this.url } },
});
await this.prisma.$connect();
}

async _generateClient(rels) {
// Generate a formatted schema
// note that we currently still need to call _prepareSchema even during
// a `keystone-next start` because it has various side effects
const { prismaSchema } = await this._prepareSchema(rels);

if (this.migrationMode !== 'none-skip-client-generation') {
this._writePrismaSchema({ prismaSchema });

// Generate prisma client and run prisma migrations
await Promise.all([this._generatePrismaClient(), this._runMigrations({ prismaSchema })]);
}
}

async _runMigrations({ prismaSchema }) {
if (this.migrationMode === 'prototype') {
// Sync the database directly, without generating any migration
await runPrototypeMigrations(this._url(), prismaSchema, path.resolve(this.schemaPath));
} else if (this.migrationMode === 'dev') {
// Generate and apply a migration if required.
await devMigrations(this._url(), prismaSchema, path.resolve(this.schemaPath));
} else if (this.migrationMode === 'none') {
// Explicitly disable running any migrations
} else {
throw new Error(
`migrationMode must be one of 'dev', 'prototype', 'none-skip-client-generation', or 'none`
);
}
}

async _writePrismaSchema({ prismaSchema }) {
// Make output dir (you know, just in case!)
fs.mkdirSync(this.clientPath, { recursive: true });

// Write prisma file
fs.writeSync(fs.openSync(this.schemaPath, 'w'), prismaSchema);
}

async _generatePrismaClient() {
const generator = await getGenerator({ schemaPath: this.schemaPath });
await generator.generate();
generator.stop();
}

async _generatePrismaSchema({ rels, clientDir }) {
const models = Object.values(this.listAdapters).map(listAdapter => {
const scalarFields = flatten(
Expand Down
Loading

1 comment on commit 95fefaf

@vercel
Copy link

@vercel vercel bot commented on 95fefaf Mar 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.