Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Singleton List refactor #3013

Merged
merged 11 commits into from
May 26, 2020
5 changes: 5 additions & 0 deletions .changeset/wise-icons-shake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@keystonejs/list-plugins': major
---

* Added `singleton` list plugin which prevents creating more items for a list or delete the only item in the list.
19 changes: 18 additions & 1 deletion packages/list-plugins/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const { createdAt, updatedAt } = require('@keystonejs/list-plugins');

_Note_: The API is the same.

## byTracking
# byTracking

Adds `createdBy` and `updatedBy` fields to a list. These fields are read-only by will be updated automatically when items are created or updated.

Expand Down Expand Up @@ -103,3 +103,20 @@ const { createdBy, updatedBy } = require('@keystonejs/list-plugins');
```

_Note_: The API is the same.

# singleton

This plugin makes a list singleton by allowing only one item in the list. Useful for list which must contain only one items.

## Usage

```js
const { singleton } = require('@keystonejs/list-plugins');

keystone.createList('ListWithPlugin', {
fields: {...},
plugins: [
singleton(),
Copy link
Member Author

Choose a reason for hiding this comment

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

This plugin does not need argument. We have this pattern to call the plugin when adding to list, if there is no argument this nesting of function call can be avoided. should this be a pattern or dependent on plugin whether to add as [plugin] or as [plugin()].

],
});
```
2 changes: 2 additions & 0 deletions packages/list-plugins/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const { atTracking, createdAt, updatedAt } = require('./lib/tracking/atTracking');
const { byTracking, createdBy, updatedBy } = require('./lib/tracking/byTracking');
const { singleton } = require('./lib/limiting/singleton');

module.exports = {
atTracking,
Expand All @@ -8,4 +9,5 @@ module.exports = {
byTracking,
createdBy,
updatedBy,
singleton,
};
34 changes: 34 additions & 0 deletions packages/list-plugins/lib/limiting/singleton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const { composeHook, composeAccess } = require('../utils');

exports.singleton = () => (
{ hooks = {}, access = {}, adminConfig = {}, ...rest },
{ listKey, keystone }
) => {
const newResolveInput = async ({ resolvedData, operation }) => {
if (operation === 'create') {
const list = keystone.getListByKey(listKey);
const query = `{${list.gqlNames.listQueryMetaName} { count }}`;
const {
data: { [list.gqlNames.listQueryMetaName]: listQuery } = {},
errors,
} = await keystone.executeQuery(query);
if (errors) {
throw errors;
}
if (listQuery && listQuery.count && listQuery.count > 0) {
throw new Error(`ItemLimit reached, This Singleton list can not add more item`);
}
}
return resolvedData;
};

const listAccess = composeAccess(access, { delete: false }, keystone.defaultAccess.list);
const originalResolveInput = hooks.resolveInput;
hooks.resolveInput = composeHook(originalResolveInput, newResolveInput);
return {
access: listAccess,
hooks,
adminConfig: { ...adminConfig, singleton: true },
...rest,
};
};
24 changes: 24 additions & 0 deletions packages/list-plugins/lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,27 @@ exports.composeHook = (originalHook, newHook) => async params => {
}
return newHook({ ...params, resolvedData });
};

exports.composeAccess = (originalAccess, newAccess = {}) => {
if (typeof originalAccess === 'undefined') {
return {
...newAccess,
};
}

const isShorthand = typeof originalAccess === 'boolean';
if (isShorthand) {
return {
create: originalAccess,
read: originalAccess,
update: originalAccess,
delete: originalAccess,
auth: originalAccess,
...newAccess,
};
}
return {
...originalAccess,
...newAccess,
};
};
21 changes: 21 additions & 0 deletions packages/list-plugins/singleton.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!--[meta]
section: list-plugins
title: singletoon
[meta]-->

# singleton Plugin

This plugin makes a list singleton by allowing only one item in the list. Useful for list which must contain only one items.

## Usage

```js
const { singleton } = require('@keystonejs/list-plugins');

keystone.createList('ListWithPlugin', {
fields: {...},
plugins: [
singleton(),
],
});
```