Improving SDK types (#19286)

Co-authored-by: Pascal Jufer <pascal-jufer@bluewin.ch>
This commit is contained in:
Brainslug
2023-07-31 17:52:08 +02:00
committed by GitHub
parent 318c77270f
commit 91c4863f22
12 changed files with 130 additions and 85 deletions

View File

@@ -0,0 +1,5 @@
---
"@directus/sdk": patch
---
Added missing filter operators, removed the reliance on `@directus/types`, fixed content-type regression on graphql

4
pnpm-lock.yaml generated
View File

@@ -1833,10 +1833,6 @@ importers:
version: 0.31.1(happy-dom@9.18.3)(sass@1.62.1)
sdk:
dependencies:
'@directus/types':
specifier: workspace:*
version: link:../packages/types
devDependencies:
'@directus/tsconfig':
specifier: workspace:*

View File

@@ -54,9 +54,6 @@
"typescript": "5.0.4",
"vitest": "0.31.1"
},
"dependencies": {
"@directus/types": "workspace:*"
},
"engines": {
"node": ">=18.0.0"
}

View File

@@ -20,18 +20,23 @@ export const graphql = () => {
const options: RequestInit = {
method: 'POST',
body: JSON.stringify({ query, variables }),
headers: {},
};
const headers: Record<string, string> = {};
if ('getToken' in this) {
const token = await (this.getToken as AuthenticationClient<Schema>['getToken'])();
if (token) {
if (!options.headers) options.headers = {};
options.headers = { Authorization: `Bearer ${token}` };
headers['Authorization'] = `Bearer ${token}`;
}
}
if ('Content-Type' in headers === false) {
headers['Content-Type'] = 'application/json';
}
options.headers = headers;
const requestPath = scope === 'items' ? '/graphql' : '/graphql/system';
const requestUrl = getRequestUrl(client.url, requestPath);

View File

@@ -1,4 +1,3 @@
import type { PrimaryKey } from '@directus/types';
import type { Query } from '../types/query.js';
import type { ApplyQueryFields, CollectionType } from '../index.js';
@@ -73,5 +72,5 @@ export type SubscriptionPayload<Item> = {
init: Item[];
create: Item[];
update: Item[];
delete: PrimaryKey[];
delete: string[] | number[];
};

View File

@@ -1,4 +1,3 @@
import type { PrimaryKey } from '@directus/types';
import type { Query } from '../../../types/index.js';
import type { RestCommand } from '../../types.js';
@@ -13,7 +12,7 @@ import type { RestCommand } from '../../types.js';
export const deleteItems =
<Schema extends object, Collection extends keyof Schema, const TQuery extends Query<Schema, Schema[Collection]>>(
collection: Collection,
keysOrQuery: PrimaryKey[] | TQuery
keysOrQuery: string[] | number[] | TQuery
): RestCommand<void, Schema> =>
() => {
const _collection = String(collection);
@@ -39,7 +38,7 @@ export const deleteItems =
export const deleteItem =
<Schema extends object, Collection extends keyof Schema>(
collection: Collection,
key: PrimaryKey
key: string | number
): RestCommand<void, Schema> =>
() => {
const _collection = String(collection);

View File

@@ -1,4 +1,3 @@
import type { PrimaryKey } from '@directus/types';
import type { ApplyQueryFields, CollectionType, Query, RegularCollections } from '../../../types/index.js';
import type { RestCommand } from '../../types.js';
@@ -55,7 +54,7 @@ export const readItem =
const TQuery extends Query<Schema, CollectionType<Schema, Collection>>
>(
collection: Collection,
key: PrimaryKey,
key: string | number,
query?: TQuery
): RestCommand<ReadItemOutput<Schema, Collection, TQuery>, Schema> =>
() => {

View File

@@ -1,4 +1,3 @@
import type { PrimaryKey } from '@directus/types';
import type { ApplyQueryFields, CollectionType, Query, UnpackList } from '../../../types/index.js';
import type { RestCommand } from '../../types.js';
@@ -21,7 +20,7 @@ export type UpdateItemOutput<
export const updateItems =
<Schema extends object, Collection extends keyof Schema, const TQuery extends Query<Schema, Schema[Collection]>>(
collection: Collection,
keys: PrimaryKey[],
keys: string[] | number[],
item: Partial<UnpackList<Schema[Collection]>>,
query?: TQuery
): RestCommand<UpdateItemOutput<Schema, Collection, TQuery>[], Schema> =>
@@ -54,7 +53,7 @@ export const updateItem =
Item = UnpackList<Schema[Collection]>
>(
collection: Collection,
key: PrimaryKey,
key: string | number,
item: Partial<Item>,
query?: TQuery
): RestCommand<UpdateItemOutput<Schema, Collection, TQuery>, Schema> =>

27
sdk/src/types/deep.ts Normal file
View File

@@ -0,0 +1,27 @@
import type { MergeObjects, Query } from './query.js';
import type { ItemType, RelationalFields } from './schema.js';
import type { UnpackList } from './utils.js';
/**
* Deep filter object
*/
export type QueryDeep<Schema extends object, Item> = UnpackList<Item> extends infer FlatItem
? RelationalFields<Schema, FlatItem> extends never
? never
: {
[Field in RelationalFields<Schema, FlatItem> as ExtractCollection<Schema, FlatItem[Field]> extends any[]
? Field
: never]?: ExtractCollection<Schema, FlatItem[Field]> extends infer CollectionItem
? Query<Schema, CollectionItem> extends infer TQuery
? MergeObjects<
QueryDeep<Schema, CollectionItem>,
{
[Key in keyof Omit<TQuery, 'deep' | 'alias' | 'fields'> as `_${string & Key}`]: TQuery[Key];
}
>
: never
: never;
}
: never;
type ExtractCollection<Schema extends object, Item> = Extract<Item, ItemType<Schema>>;

79
sdk/src/types/filters.ts Normal file
View File

@@ -0,0 +1,79 @@
import type { RelationalFields } from './schema.js';
import type { UnpackList } from './utils.js';
/**
* Filters
*/
export type QueryFilter<Schema extends object, Item> = WrapLogicalFilters<NestedQueryFilter<Schema, Item>>;
/**
* Query filters without logical filters
*/
export type NestedQueryFilter<Schema extends object, Item> = UnpackList<Item> extends infer FlatItem
? {
[Field in keyof FlatItem]?:
| (Field extends RelationalFields<Schema, FlatItem>
? WrapRelationalFilters<NestedQueryFilter<Schema, FlatItem[Field]>>
: never)
| FilterOperators<FlatItem[Field]>;
}
: never;
/**
* All regular filter operators
*
* TODO would love to filter this based on field type but thats not accurate enough in the schema atm
*/
export type FilterOperators<T> = {
_eq?: T;
_neq?: T;
_gt?: T;
_gte?: T;
_lt?: T;
_lte?: T;
_in?: T[];
_nin?: T[];
_between?: [T, T];
_nbetween?: [T, T];
_contains?: T;
_ncontains?: T;
_starts_with?: T;
_istarts_with?: T;
_nstarts_with?: T;
_nistarts_with?: T;
_ends_with?: T;
_iends_with?: T;
_nends_with?: T;
_niends_with?: T;
_empty?: boolean;
_nempty?: boolean;
_nnull?: boolean;
_null?: boolean;
_intersects?: T;
_nintersects?: T;
_intersects_bbox?: T;
_nintersects_bbox?: T;
_regex?: T;
};
/**
* Relational filter operators
*/
export type RelationalFilterOperators = '_some' | '_none';
export type WrapRelationalFilters<Filters> =
| {
[Operator in RelationalFilterOperators]?: Filters;
}
| Filters;
/**
* Logical filter operations
*/
export type LogicalFilterOperators = '_or' | '_and';
export type WrapLogicalFilters<Filters> =
| {
[Operator in LogicalFilterOperators]?: WrapLogicalFilters<Filters>[];
}
| Filters;

View File

@@ -1,6 +1,8 @@
export * from './aggregate.js';
export * from './client.js';
export * from './deep.js';
export * from './fields.js';
export * from './filters.js';
export * from './output.js';
export * from './query.js';
export * from './request.js';

View File

@@ -1,5 +1,7 @@
import type { QueryDeep } from './deep.js';
import type { HasNestedFields, QueryFields } from './fields.js';
import type { ItemType, RelationalFields } from './schema.js';
import type { QueryFilter } from './filters.js';
import type { ItemType } from './schema.js';
import type { IfAny, UnpackList } from './utils.js';
/**
@@ -45,52 +47,6 @@ export type MergeFields<FieldList> = HasNestedFields<FieldList> extends never
? Extract<UnpackList<FieldList>, string>
: Extract<UnpackList<FieldList>, string> | MergeRelationalFields<FieldList>;
/**
* Filters
*/
export type QueryFilter<Schema extends object, Item> = UnpackList<Item> extends infer FlatItem
? {
[Field in keyof FlatItem]?:
| (Field extends RelationalFields<Schema, FlatItem> ? QueryFilter<Schema, FlatItem[Field]> : never)
| FilterOperatorsByType<FlatItem[Field]>;
}
: never;
/**
* All available filter operators
* TODO would love to filter this based on field type but thats not accurate enough in the schema atm
*/
export type FilterOperatorsByType<T> = {
_eq?: T;
_neq?: T;
_gt?: T;
_gte?: T;
_lt?: T;
_lte?: T;
_in?: T[];
_nin?: T[];
_between?: [T, T];
_nbetween?: [T, T];
_contains?: T;
_ncontains?: T;
_starts_with?: T;
_istarts_with?: T;
_nstarts_with?: T;
_nistarts_with?: T;
_ends_with?: T;
_iends_with?: T;
_nends_with?: T;
_niends_with?: T;
_empty?: boolean;
_nempty?: boolean;
_nnull?: boolean;
_null?: boolean;
_intersects?: T;
_nintersects?: T;
_intersects_bbox?: T;
_nintersects_bbox?: T;
};
/**
* Query sort
* TODO expand to relational sorting (same object notation as fields i guess)
@@ -101,24 +57,6 @@ export type QuerySort<_Schema extends object, Item> = UnpackList<Item> extends i
}[keyof FlatItem]
: never;
/**
* Deep filter object
*/
export type QueryDeep<Schema extends object, Item> = UnpackList<Item> extends infer FlatItem
? RelationalFields<Schema, FlatItem> extends never
? never
: {
[Field in RelationalFields<Schema, FlatItem>]?: Query<Schema, FlatItem[Field]> extends infer TQuery
? MergeObjects<
QueryDeep<Schema, FlatItem[Field]>,
{
[Key in keyof Omit<TQuery, 'deep' | 'alias'> as `_${string & Key}`]: TQuery[Key];
}
>
: never;
}
: never;
export type MergeObjects<A, B extends object> = A extends object ? A & B : never;
/**