Skip to content

Commit

Permalink
Flatten the adapters class hierarchy (#5281)
Browse files Browse the repository at this point in the history
  • Loading branch information
timleslie authored Mar 30, 2021
1 parent 9e450d6 commit 4f0abec
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 284 deletions.
6 changes: 6 additions & 0 deletions .changeset/thin-geese-worry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@keystone-next/keystone-legacy': major
'@keystone-next/adapter-prisma-legacy': patch
---

Removed the legacy `BaseKeystoneAdapter`, `BaseListAdapter`, and `BaseFieldAdapter` exports.
1 change: 1 addition & 0 deletions packages/adapter-prisma/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@sindresorhus/slugify": "^1.1.0",
"@types/prompts": "^2.0.9",
"chalk": "^4.1.0",
"p-waterfall": "^2.1.1",
"prompts": "^2.4.0"
},
"repository": "https://github.com/keystonejs/keystone/tree/master/packages/adapter-prisma"
Expand Down
166 changes: 153 additions & 13 deletions packages/adapter-prisma/src/adapter-prisma.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import fs from 'fs';
import path from 'path';
import pWaterfall from 'p-waterfall';
import { getGenerator, formatSchema } from '@prisma/sdk';
import {
BaseKeystoneAdapter,
BaseListAdapter,
BaseFieldAdapter,
} from '@keystone-next/keystone-legacy';
import { defaultObj, mapKeys, identity, flatten } from '@keystone-next/utils-legacy';
import {
runPrototypeMigrations,
Expand All @@ -15,9 +11,11 @@ import {
// eslint-disable-next-line import/no-unresolved
} from './migrations';

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

this.listAdapterClass = PrismaListAdapter;
Expand All @@ -31,6 +29,48 @@ class PrismaAdapter extends BaseKeystoneAdapter {
this.url = this.config.url || process.env.DATABASE_URL;
}

newListAdapter(key, adapterConfig) {
this.listAdapters[key] = new this.listAdapterClass(key, this, adapterConfig);
return this.listAdapters[key];
}

getListAdapterByKey(key) {
return this.listAdapters[key];
}

async connect({ rels }) {
// Connect to the database
await this._connect({ rels }, this.config);

// Set up all list adapters
try {
// Validate the minimum database version requirements are met.
await this.checkDatabaseVersion();

const taskResults = await this.postConnect({ rels });
const errors = taskResults.filter(({ isRejected }) => isRejected).map(({ reason }) => reason);

if (errors.length) {
if (errors.length === 1) throw errors[0];
const error = new Error('Multiple errors in PrismaAdapter.postConnect():');
error.errors = errors;
throw error;
}
} catch (error) {
// close the database connection if it was opened
try {
await this.disconnect();
} catch (closeError) {
// Add the inability to close the database connection as an additional
// error
error.errors = error.errors || [];
error.errors.push(closeError);
}
// re-throw the error
throw error;
}
}

async _prepareSchema(rels) {
const clientDir = 'generated-client';
const prismaSchema = await this._generatePrismaSchema({ rels, clientDir });
Expand Down Expand Up @@ -294,12 +334,104 @@ class PrismaAdapter extends BaseKeystoneAdapter {
}
}

class PrismaListAdapter extends BaseListAdapter {
constructor(key, parentAdapter) {
super(...arguments);
class PrismaListAdapter {
constructor(key, parentAdapter, config) {
this.key = key;
this.parentAdapter = parentAdapter;
this.fieldAdapters = [];
this.fieldAdaptersByPath = {};
this.config = config;

this.preSaveHooks = [];
this.postReadHooks = [
item => {
// FIXME: This can hopefully be removed once graphql 14.1.0 is released.
// https://github.com/graphql/graphql-js/pull/1520
if (item && item.id) item.id = item.id.toString();
return item;
},
];
this.getListAdapterByKey = parentAdapter.getListAdapterByKey.bind(parentAdapter);
}

newFieldAdapter(fieldAdapterClass, name, path, field, getListByKey, config) {
const adapter = new fieldAdapterClass(name, path, field, this, getListByKey, config);
adapter.setupHooks({
addPreSaveHook: this.addPreSaveHook.bind(this),
addPostReadHook: this.addPostReadHook.bind(this),
});
this.fieldAdapters.push(adapter);
this.fieldAdaptersByPath[adapter.path] = adapter;
return adapter;
}

addPreSaveHook(hook) {
this.preSaveHooks.push(hook);
}

addPostReadHook(hook) {
this.postReadHooks.push(hook);
}

onPreSave(item) {
// We waterfall so the final item is a composed version of the input passing
// through each consecutive hook
return pWaterfall(this.preSaveHooks, item);
}

async onPostRead(item) {
// We waterfall so the final item is a composed version of the input passing
// through each consecutive hook
return pWaterfall(this.postReadHooks, await item);
}

async create(data) {
return this.onPostRead(this._create(await this.onPreSave(data)));
}

async delete(id) {
return this._delete(id);
}

async update(id, data) {
return this.onPostRead(this._update(id, await this.onPreSave(data)));
}

async findAll() {
return Promise.all((await this._itemsQuery({})).map(item => this.onPostRead(item)));
}

async findById(id) {
return this.onPostRead((await this._itemsQuery({ where: { id }, first: 1 }))[0] || null);
}

async find(condition) {
return Promise.all(
(await this._itemsQuery({ where: condition })).map(item => this.onPostRead(item))
);
}

async findOne(condition) {
return this.onPostRead((await this._itemsQuery({ where: condition, first: 1 }))[0]);
}

async itemsQuery(args, { meta = false, from = {} } = {}) {
const results = await this._itemsQuery(args, { meta, from });
return meta ? results : Promise.all(results.map(item => this.onPostRead(item)));
}

itemsQueryMeta(args) {
return this.itemsQuery(args, { meta: true });
}

getFieldAdapterByPath(path) {
return this.fieldAdaptersByPath[path];
}

getPrimaryKeyAdapter() {
return this.fieldAdaptersByPath['id'];
}

_postConnect({ rels, prisma }) {
// https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-schema/models#queries-crud
// "By default the name of the property is the lowercase form of the model name,
Expand Down Expand Up @@ -531,11 +663,19 @@ class PrismaListAdapter extends BaseListAdapter {
}
}

class PrismaFieldAdapter extends BaseFieldAdapter {
constructor() {
super(...arguments);
class PrismaFieldAdapter {
constructor(fieldName, path, field, listAdapter, getListByKey, config = {}) {
this.fieldName = fieldName;
this.path = path;
this.field = field;
this.listAdapter = listAdapter;
this.config = config;
this.getListByKey = getListByKey;
this.dbPath = path;
}

setupHooks() {}

_schemaField({ type, extra = '' }) {
const { isRequired, isUnique } = this.config;
return `${this.path} ${type}${isRequired || this.field.isPrimaryKey ? '' : '?'} ${
Expand Down
8 changes: 1 addition & 7 deletions packages/keystone/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,2 @@
const Keystone = require('./lib/Keystone');
const { BaseKeystoneAdapter, BaseListAdapter, BaseFieldAdapter } = require('./lib/adapters');
module.exports = {
Keystone,
BaseKeystoneAdapter,
BaseListAdapter,
BaseFieldAdapter,
};
module.exports = { Keystone };
91 changes: 0 additions & 91 deletions packages/keystone/lib/adapters/README.md

This file was deleted.

Loading

1 comment on commit 4f0abec

@vercel
Copy link

@vercel vercel bot commented on 4f0abec 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.