Skip to content

Commit

Permalink
Merge branch 'main' into renovate/eslint-8.x
Browse files Browse the repository at this point in the history
  • Loading branch information
gwyneplaine committed Oct 19, 2021
2 parents d17e191 + 111c8f2 commit ea58138
Show file tree
Hide file tree
Showing 22 changed files with 63 additions and 90 deletions.
5 changes: 5 additions & 0 deletions .changeset/gentle-plums-end.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@keystone-next/keystone': major
---

Removed the deprecated `resolveFields` from `context.query`, if you were still using it, you should switch to providing `the query option` to `context.query` or use `context.db` if you were providing `false`. The `context.query` functions will now also throw an error if an empty string is passed to `query` rather than silently returning what the `context.db` functions return, you must select at least one field or omit the `query` option to default to selecting the `id`.
6 changes: 6 additions & 0 deletions .changeset/lemon-paws-sneeze.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@keystone-next/auth': patch
'@keystone-next/keystone': patch
---

Readonly arrays are now accepted where previously mutable arrays were required. This means that if you use `as const` when writing an array and then pass it to various APIs in keystone, that will now work.
2 changes: 1 addition & 1 deletion examples/blog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ To run this project:

1. Clone the Keystone repository locally
2. Navigate to this directory `cd examples/blog`
3. Run `yarn dev`
3. Run `yarn install` and then `yarn dev`

This will start the Admin UI at [localhost:3000](http://localhost:3000).
You can use the Admin UI to create items in your database.
Expand Down
2 changes: 1 addition & 1 deletion packages/auth/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export type AuthConfig<GeneratedListTypes extends BaseGeneratedListTypes> = {

export type InitFirstItemConfig<GeneratedListTypes extends BaseGeneratedListTypes> = {
/** Array of fields to collect, e.g ['name', 'email', 'password'] */
fields: GeneratedListTypes['fields'][];
fields: readonly GeneratedListTypes['fields'][];
/** Suppresses the second screen where we ask people to subscribe and follow Keystone */
skipKeystoneWelcome?: boolean;
/** Extra input to add for the create mutation */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ export const crudTests = (keystoneTestWrapper: any) => {
await expect(
context.query.Test.createOne({
data: { passwordRejectCommon: 'password' },
query: ``,
})
).rejects.toMatchInlineSnapshot(`
[GraphQLError: You provided invalid data for this operation.
Expand Down
6 changes: 3 additions & 3 deletions packages/keystone/src/fields/types/relationship/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ type CardsDisplayConfig = {
// Sets the relationship to display as a list of Cards
displayMode: 'cards';
/* The set of fields to render in the default Card component **/
cardFields: string[];
cardFields: readonly string[];
/** Causes the default Card component to render as a link to navigate to the related item */
linkToItem?: boolean;
/** Determines whether removing a related item in the UI will delete or unlink it */
removeMode?: 'disconnect' | 'none'; // | 'delete';
/** Configures inline create mode for cards (alternative to opening the create modal) */
inlineCreate?: { fields: string[] };
inlineCreate?: { fields: readonly string[] };
/** Configures inline edit mode for cards */
inlineEdit?: { fields: string[] };
inlineEdit?: { fields: readonly string[] };
/** Configures whether a select to add existing items should be shown or not */
inlineConnect?: boolean;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function InlineCreate({
}: {
list: ListMeta;
selectedFields: string;
fields: string[];
fields: readonly string[];
onCancel: () => void;
onCreate: (itemGetter: DataGetter<ItemData>) => void;
}) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function InlineEdit({
onCancel,
onSave,
}: {
fields: string[];
fields: readonly string[];
list: ListMeta;
selectedFields: string;
itemGetter: DataGetter<ItemData>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ export function useItemState({
};
}

export function useFieldsObj(list: ListMeta, fields: string[] | undefined) {
export function useFieldsObj(list: ListMeta, fields: readonly string[] | undefined) {
return useMemo(() => {
const editFields: Record<string, FieldMeta> = {};
fields?.forEach(fieldPath => {
Expand Down
12 changes: 6 additions & 6 deletions packages/keystone/src/fields/types/relationship/views/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -375,11 +375,11 @@ type RelationshipController = FieldController<
}
| {
mode: 'cards';
cardFields: string[];
cardFields: readonly string[];
linkToItem: boolean;
removeMode: 'disconnect' | 'none';
inlineCreate: { fields: string[] } | null;
inlineEdit: { fields: string[] } | null;
inlineCreate: { fields: readonly string[] } | null;
inlineEdit: { fields: readonly string[] } | null;
inlineConnect: boolean;
}
| { mode: 'count' };
Expand All @@ -404,11 +404,11 @@ export const controller = (
}
| {
displayMode: 'cards';
cardFields: string[];
cardFields: readonly string[];
linkToItem: boolean;
removeMode: 'disconnect' | 'none';
inlineCreate: { fields: string[] } | null;
inlineEdit: { fields: string[] } | null;
inlineCreate: { fields: readonly string[] } | null;
inlineEdit: { fields: readonly string[] } | null;
inlineConnect: boolean;
refLabelField: string;
}
Expand Down
6 changes: 3 additions & 3 deletions packages/keystone/src/fields/types/select/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ export type SelectFieldConfig<TGeneratedListTypes extends BaseGeneratedListTypes
* When a value is provided as just a string, it will be formatted in the same way
* as field labels are to create the label.
*/
options: ({ label: string; value: string } | string)[];
options: readonly ({ label: string; value: string } | string)[];
/**
* If `enum` is provided on SQLite, it will use an enum in GraphQL but a string in the database.
*/
type?: 'string' | 'enum';
defaultValue?: string;
}
| {
options: { label: string; value: number }[];
options: readonly { label: string; value: number }[];
type: 'integer';
defaultValue?: number;
}
Expand Down Expand Up @@ -80,7 +80,7 @@ export const select =

assertCreateIsNonNullAllowed(meta, config);
const commonConfig = (
options: { value: string | number; label: string }[]
options: readonly { value: string | number; label: string }[]
): CommonFieldConfig<TGeneratedListTypes> & {
views: string;
getAdminMeta: () => import('./views').AdminSelectFieldMeta;
Expand Down
2 changes: 1 addition & 1 deletion packages/keystone/src/fields/types/select/views/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export const CardValue: CardValueComponent<typeof controller> = ({ item, field }
};

export type AdminSelectFieldMeta = {
options: { label: string; value: string | number }[];
options: readonly { label: string; value: string | number }[];
type: 'string' | 'integer' | 'enum';
displayMode: 'select' | 'segmented-control';
isRequired: boolean;
Expand Down
2 changes: 1 addition & 1 deletion packages/keystone/src/fields/types/virtual/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export type VirtualFieldConfig<TGeneratedListTypes extends BaseGeneratedListType
field:
| VirtualFieldGraphQLField
| ((lists: Record<string, ListInfo>) => VirtualFieldGraphQLField);
unreferencedConcreteInterfaceImplementations?: graphql.ObjectType<any>[];
unreferencedConcreteInterfaceImplementations?: readonly graphql.ObjectType<any>[];
ui?: {
/**
* Defines what the Admin UI should fetch from this field, it's interpolated into a query like this:
Expand Down
2 changes: 1 addition & 1 deletion packages/keystone/src/lib/context/createContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export function makeCreateContext({
const dbAPIFactories = sudo ? sudoDbApiFactories : publicDbApiFactories;
for (const listKey of Object.keys(gqlNamesByList)) {
dbAPI[listKey] = dbAPIFactories[listKey](contextToReturn);
itemAPI[listKey] = itemAPIForList(listKey, contextToReturn, dbAPI[listKey]);
itemAPI[listKey] = itemAPIForList(listKey, contextToReturn);
}
return contextToReturn;
};
Expand Down
56 changes: 13 additions & 43 deletions packages/keystone/src/lib/context/itemAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,62 +52,32 @@ export function getDbAPIFactory(
) as Record<keyof typeof api, any>;
}

function defaultQueryParam(query?: string, resolveFields?: string | false) {
if (query !== undefined && resolveFields !== undefined) {
throw new Error('query and resolveFields cannot both be passed to an Items API query');
}
if (query !== undefined) return query;
if (resolveFields !== undefined) return resolveFields;
return 'id';
}

/* NOTE
*
* The `resolveFields` param has been deprecated in favor of `query` (when selecting fields to
* query) or the new dbAPI which is available via `context.db.{List}`, which replaces
* the previous `resolveFields: false` behaviour.
*
* We'll be removing the option to use `resolveFields` entirely in a future release.
*/
export function itemAPIForList(
listKey: string,
context: KeystoneContext,
dbAPI: KeystoneDbAPI<Record<string, BaseGeneratedListTypes>>[string]
context: KeystoneContext
): KeystoneListsAPI<Record<string, BaseGeneratedListTypes>>[string] {
const f = (
operation: 'query' | 'mutation',
field: string,
dbAPIVersionOfAPI: (args: any) => Promise<any>
) => {
const f = (operation: 'query' | 'mutation', field: string) => {
const exec = executeGraphQLFieldWithSelection(context.graphql.schema, operation, field);
return ({
query,
resolveFields,
...args
}: { resolveFields?: false | string; query?: string } & Record<string, any> = {}) => {
const returnFields = defaultQueryParam(query, resolveFields);
if (returnFields) {
return exec(args, returnFields, context);
} else {
return dbAPIVersionOfAPI(args);
}
return ({ query, ...args }: { query?: string } & Record<string, any> = {}) => {
const returnFields = query ?? 'id';
return exec(args, returnFields, context);
};
};
const gqlNames = context.gqlNames(listKey);
return {
findOne: f('query', gqlNames.itemQueryName, dbAPI.findOne),
findMany: f('query', gqlNames.listQueryName, dbAPI.findMany),
findOne: f('query', gqlNames.itemQueryName),
findMany: f('query', gqlNames.listQueryName),
async count({ where = {} } = {}) {
const { listQueryCountName, whereInputName } = context.gqlNames(listKey);
const query = `query ($where: ${whereInputName}!) { count: ${listQueryCountName}(where: $where) }`;
const response = await context.graphql.run({ query, variables: { where } });
return response.count;
},
createOne: f('mutation', gqlNames.createMutationName, dbAPI.createOne),
createMany: f('mutation', gqlNames.createManyMutationName, dbAPI.createMany),
updateOne: f('mutation', gqlNames.updateMutationName, dbAPI.updateOne),
updateMany: f('mutation', gqlNames.updateManyMutationName, dbAPI.updateMany),
deleteOne: f('mutation', gqlNames.deleteMutationName, dbAPI.deleteOne),
deleteMany: f('mutation', gqlNames.deleteManyMutationName, dbAPI.deleteMany),
createOne: f('mutation', gqlNames.createMutationName),
createMany: f('mutation', gqlNames.createManyMutationName),
updateOne: f('mutation', gqlNames.updateMutationName),
updateMany: f('mutation', gqlNames.updateManyMutationName),
deleteOne: f('mutation', gqlNames.deleteMutationName),
deleteMany: f('mutation', gqlNames.deleteManyMutationName),
};
}
6 changes: 3 additions & 3 deletions packages/keystone/src/lib/core/prisma-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ScalarDBField, ScalarDBFieldDefault, DatabaseProvider } from '../../typ
import { ResolvedDBField, ListsWithResolvedRelations } from './resolve-relationships';
import { getDBFieldKeyForFieldOnMultiField } from './utils';

function areArraysEqual(a: unknown[], b: unknown[]) {
function areArraysEqual(a: readonly unknown[], b: readonly unknown[]) {
if (a.length !== b.length) {
return false;
}
Expand Down Expand Up @@ -116,7 +116,7 @@ function printField(
}

function collectEnums(lists: ListsWithResolvedRelations) {
const enums: Record<string, { values: string[]; firstDefinedByRef: string }> = {};
const enums: Record<string, { values: readonly string[]; firstDefinedByRef: string }> = {};
for (const [listKey, { resolvedDbFields }] of Object.entries(lists)) {
for (const [fieldPath, field] of Object.entries(resolvedDbFields)) {
const fields =
Expand Down Expand Up @@ -195,7 +195,7 @@ export function printPrismaSchema(
lists: ListsWithResolvedRelations,
provider: DatabaseProvider,
clientDir: string,
prismaPreviewFeatures?: string[]
prismaPreviewFeatures: readonly string[] | undefined
) {
let prismaFlags = '';
if (prismaPreviewFeatures && prismaPreviewFeatures.length) {
Expand Down
2 changes: 1 addition & 1 deletion packages/keystone/src/types/config/fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export type CommonFieldConfig<TGeneratedListTypes extends BaseGeneratedListTypes
// If `true` then the field will be completely removed from all types.
//
// Default: undefined
omit?: true | ('read' | 'create' | 'update')[];
omit?: true | readonly ('read' | 'create' | 'update')[];
};
// Disabled by default...
isFilterable?: boolean | ((args: FilterOrderArgs) => MaybePromise<boolean>);
Expand Down
8 changes: 5 additions & 3 deletions packages/keystone/src/types/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export type DatabaseConfig = {
enableLogging?: boolean;
idField?: IdFieldConfig;
provider: 'postgresql' | 'sqlite';
prismaPreviewFeatures?: string[]; // https://www.prisma.io/docs/concepts/components/preview-features
prismaPreviewFeatures?: readonly string[]; // https://www.prisma.io/docs/concepts/components/preview-features
};

// config.ui
Expand All @@ -71,11 +71,13 @@ export type AdminUIConfig = {
/** A function that can be run to validate that the current session should have access to the Admin UI */
isAccessAllowed?: (context: KeystoneContext) => MaybePromise<boolean>;
/** An array of page routes that can be accessed without passing the isAccessAllowed check */
publicPages?: string[];
publicPages?: readonly string[];
/** The basePath for the Admin UI App */
// FIXME: currently unused
// path?: string;
getAdditionalFiles?: ((config: KeystoneConfig) => MaybePromise<AdminFileToWrite[]>)[];
getAdditionalFiles?: readonly ((
config: KeystoneConfig
) => MaybePromise<readonly AdminFileToWrite[]>)[];
pageMiddleware?: (args: {
context: KeystoneContext;
isValidSession: boolean;
Expand Down
6 changes: 3 additions & 3 deletions packages/keystone/src/types/config/lists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export type ListAdminUIConfig<
* It is always possible to search by id and `id` should not be specified in this option.
* @default The `labelField` if it has a string `contains` filter, otherwise none.
*/
searchFields?: Extract<keyof Fields, string>[];
searchFields?: readonly Extract<keyof Fields, string>[];

/** The path that the list should be at in the Admin UI */
// Not currently used. Should be passed into `keystone.createList()`.
Expand Down Expand Up @@ -162,7 +162,7 @@ export type ListAdminUIConfig<
* Users of the Admin UI can select different columns to show in the UI.
* @default the first three fields in the list
*/
initialColumns?: ('id' | keyof Fields)[];
initialColumns?: readonly ('id' | keyof Fields)[];
// was previously top-level defaultSort
initialSort?: { field: 'id' | keyof Fields; direction: 'ASC' | 'DESC' };
// was previously defaultPageSize
Expand Down Expand Up @@ -211,7 +211,7 @@ export type ListGraphQLConfig = {
// including from the point of view of relationships to this list.
//
// Default: undefined
omit?: true | ('query' | 'create' | 'update' | 'delete')[];
omit?: true | readonly ('query' | 'create' | 'update' | 'delete')[];
};

export type CacheHintArgs = { results: any; operationName?: string; meta: boolean };
Expand Down
7 changes: 0 additions & 7 deletions packages/keystone/src/types/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,6 @@ type ResolveFields = {
* @default 'id'
*/
readonly query?: string;
/**
* @deprecated
*
* resolveFields has been deprecated. Please use the `query` param to query fields,
* or `context.db.{List}` instead of passing `resolveFields: false`
*/
readonly resolveFields?: false | string;
};

export type KeystoneDbAPI<KeystoneListsTypeInfo extends Record<string, BaseGeneratedListTypes>> = {
Expand Down

0 comments on commit ea58138

Please sign in to comment.