Skip to content

Commit

Permalink
[Files] Refactor how types are shared between server and client (#141580
Browse files Browse the repository at this point in the history
)

* migrated uploads component and added new way of typing and creating an endpoint

* updated create endpoint types

* convert delete endpoint

* convert download endpoint

* convert get by id endpoint

* convert list endpoint

* convert update endpoint

* convert public facing download

* convert share get

* convert list shares

* missed a spot

* remove unused import

* convert share endpoint

* convert unshare endpoint

* convert find find endpoint, fix types and fix use of empty value check

* convert metrics
  • Loading branch information
jloleysens authored Sep 23, 2022
1 parent 1114d09 commit 6429468
Show file tree
Hide file tree
Showing 19 changed files with 284 additions and 483 deletions.
208 changes: 36 additions & 172 deletions x-pack/plugins/files/common/api_routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,8 @@
* 2.0.
*/

import type { TypeOf, Type } from '@kbn/config-schema';
import { PLUGIN_ID } from './constants';
import type {
FileJSON,
Pagination,
FilesMetrics,
FileShareJSON,
FileShareJSONWithToken,
} from './types';

export const API_BASE_PATH = `/api/${PLUGIN_ID}`;

Expand All @@ -22,6 +16,27 @@ export const FILES_SHARE_API_BASE_PATH = `${API_BASE_PATH}/shares`;

export const FILES_PUBLIC_API_BASE_PATH = `${API_BASE_PATH}/public`;

export interface EndpointInputs<
P extends Type<unknown> = Type<unknown>,
Q extends Type<unknown> = Type<unknown>,
B extends Type<unknown> = Type<unknown>
> {
params?: P;
query?: Q;
body?: B;
}

export interface CreateRouteDefinition<Inputs extends EndpointInputs, R> {
inputs: {
params: TypeOf<NonNullable<Inputs['params']>>;
query: TypeOf<NonNullable<Inputs['query']>>;
body: TypeOf<NonNullable<Inputs['body']>>;
};
output: R;
}

export type AnyEndpoint = CreateRouteDefinition<EndpointInputs, unknown>;

/**
* Abstract type definition for API route inputs and outputs.
*
Expand All @@ -42,168 +57,17 @@ export interface HttpApiInterfaceEntryDefinition<
output: R;
}

export type CreateFileKindHttpEndpoint = HttpApiInterfaceEntryDefinition<
unknown,
unknown,
{
name: string;
alt?: string;
meta?: Record<string, unknown>;
mimeType?: string;
},
{ file: FileJSON }
>;

export type DeleteFileKindHttpEndpoint = HttpApiInterfaceEntryDefinition<
{
id: string;
},
unknown,
unknown,
{ ok: true }
>;

export type DownloadFileKindHttpEndpoint = HttpApiInterfaceEntryDefinition<
{
id: string;
fileName?: string;
},
unknown,
unknown,
any
>;

export type GetByIdFileKindHttpEndpoint = HttpApiInterfaceEntryDefinition<
{
id: string;
},
unknown,
unknown,
{ file: FileJSON }
>;

export type ListFileKindHttpEndpoint = HttpApiInterfaceEntryDefinition<
unknown,
Pagination,
unknown,
{ files: FileJSON[] }
>;

export type UpdateFileKindHttpEndpoint = HttpApiInterfaceEntryDefinition<
{ id: string },
unknown,
{ name?: string; alt?: string; meta?: Record<string, unknown> },
{ file: FileJSON }
>;

export type UploadFileKindHttpEndpoint = HttpApiInterfaceEntryDefinition<
{ id: string },
{ selfDestructOnAbort?: boolean },
{ body: unknown },
{
ok: true;
size: number;
}
>;

export type FindFilesHttpEndpoint = HttpApiInterfaceEntryDefinition<
unknown,
Pagination,
{
/**
* Filter for set of file-kinds
*/
kind?: string[];

/**
* Filter for match on names
*/
name?: string[];

/**
* Filter for set of meta attributes matching this object
*/
meta?: {};

/**
* Filter for match on extensions
*/
extension?: string[];

/**
* Filter for match on extensions
*/
status?: string[];
},
{ files: FileJSON[] }
>;

export type FilesMetricsHttpEndpoint = HttpApiInterfaceEntryDefinition<
unknown,
unknown,
unknown,
FilesMetrics
>;

export type FileShareHttpEndpoint = HttpApiInterfaceEntryDefinition<
{
fileId: string;
},
unknown,
{
/**
* Unix timestamp of when the share will expire.
*/
validUntil?: number;
/**
* Optional name to uniquely identify this share instance.
*/
name?: string;
},
FileShareJSONWithToken
>;

export type FileUnshareHttpEndpoint = HttpApiInterfaceEntryDefinition<
{
/**
* Share token id
*/
id: string;
},
unknown,
unknown,
{
ok: true;
}
>;

export type FileGetShareHttpEndpoint = HttpApiInterfaceEntryDefinition<
{
/**
* ID of the share object
*/
id: string;
},
unknown,
unknown,
{
share: FileShareJSON;
}
>;

export type FileListSharesHttpEndpoint = HttpApiInterfaceEntryDefinition<
unknown,
Pagination & { forFileId?: string },
unknown,
{
shares: FileShareJSON[];
}
>;

export type FilePublicDownloadHttpEndpoint = HttpApiInterfaceEntryDefinition<
{ fileName?: string },
{ token: string },
unknown,
// Should be a readable stream
any
>;
export type { Endpoint as CreateFileKindHttpEndpoint } from '../server/routes/file_kind/create';
export type { Endpoint as DeleteFileKindHttpEndpoint } from '../server/routes/file_kind/delete';
export type { Endpoint as DownloadFileKindHttpEndpoint } from '../server/routes/file_kind/download';
export type { Endpoint as GetByIdFileKindHttpEndpoint } from '../server/routes/file_kind/get_by_id';
export type { Endpoint as ListFileKindHttpEndpoint } from '../server/routes/file_kind/list';
export type { Endpoint as UpdateFileKindHttpEndpoint } from '../server/routes/file_kind/update';
export type { Endpoint as UploadFileKindHttpEndpoint } from '../server/routes/file_kind/upload';
export type { Endpoint as FindFilesHttpEndpoint } from '../server/routes/find';
export type { Endpoint as FilesMetricsHttpEndpoint } from '../server/routes/metrics';
export type { Endpoint as FileShareHttpEndpoint } from '../server/routes/file_kind/share/share';
export type { Endpoint as FileUnshareHttpEndpoint } from '../server/routes/file_kind/share/unshare';
export type { Endpoint as FileGetShareHttpEndpoint } from '../server/routes/file_kind/share/get';
export type { Endpoint as FileListSharesHttpEndpoint } from '../server/routes/file_kind/share/list';
export type { Endpoint as FilePublicDownloadHttpEndpoint } from '../server/routes/public_facing/download';
16 changes: 12 additions & 4 deletions x-pack/plugins/files/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,18 @@ export interface FilesClient extends GlobalEndpoints {
*
* @param args - upload file args
*/
upload: ClientMethodFrom<
UploadFileKindHttpEndpoint,
{ abortSignal?: AbortSignal; contentType?: string }
>;
upload: (
args: UploadFileKindHttpEndpoint['inputs']['params'] &
UploadFileKindHttpEndpoint['inputs']['query'] & {
/**
* Should be blob or ReadableStream of some kind.
*/
body: unknown;
kind: string;
abortSignal?: AbortSignal;
contentType?: string;
}
) => Promise<UploadFileKindHttpEndpoint['output']>;
/**
* Stream a download of the file object's content.
*
Expand Down
39 changes: 17 additions & 22 deletions x-pack/plugins/files/server/routes/file_kind/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,35 @@
* 2.0.
*/

import { schema, TypeOf } from '@kbn/config-schema';
import { Ensure } from '@kbn/utility-types';
import type { CreateFileKindHttpEndpoint } from '../../../common/api_routes';
import type { FileKind } from '../../../common/types';
import { FILES_API_ROUTES } from '../api_routes';
import type { FileKindRouter, FileKindsRequestHandler } from './types';
import { schema } from '@kbn/config-schema';
import type { FileJSON, FileKind } from '../../../common/types';
import { CreateRouteDefinition, FILES_API_ROUTES } from '../api_routes';
import type { FileKindRouter } from './types';
import * as commonSchemas from '../common_schemas';
import { CreateHandler } from './types';

export const method = 'post' as const;

export const bodySchema = schema.object({
name: commonSchemas.fileName,
alt: commonSchemas.fileAlt,
meta: commonSchemas.fileMeta,
mimeType: schema.maybe(schema.string()),
});

type Body = Ensure<CreateFileKindHttpEndpoint['inputs']['body'], TypeOf<typeof bodySchema>>;
const rt = {
body: schema.object({
name: commonSchemas.fileName,
alt: commonSchemas.fileAlt,
meta: commonSchemas.fileMeta,
mimeType: schema.maybe(schema.string()),
}),
};

type Response = CreateFileKindHttpEndpoint['output'];
export type Endpoint = CreateRouteDefinition<typeof rt, { file: FileJSON }>;

export const handler: FileKindsRequestHandler<unknown, unknown, Body> = async (
{ fileKind, files },
req,
res
) => {
export const handler: CreateHandler<Endpoint> = async ({ fileKind, files }, req, res) => {
const { fileService } = await files;
const {
body: { name, alt, meta, mimeType },
} = req;
const file = await fileService
.asCurrentUser()
.create({ fileKind, name, alt, meta, mime: mimeType });
const body: Response = {
const body: Endpoint['output'] = {
file: file.toJSON(),
};
return res.ok({ body });
Expand All @@ -50,7 +45,7 @@ export function register(fileKindRouter: FileKindRouter, fileKind: FileKind) {
{
path: FILES_API_ROUTES.fileKind.getCreateFileRoute(fileKind.id),
validate: {
body: bodySchema,
...rt,
},
options: {
tags: fileKind.http.create.tags,
Expand Down
28 changes: 12 additions & 16 deletions x-pack/plugins/files/server/routes/file_kind/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,25 @@
* 2.0.
*/

import { schema, TypeOf } from '@kbn/config-schema';
import type { Ensure } from '@kbn/utility-types';
import type { DeleteFileKindHttpEndpoint } from '../../../common/api_routes';
import { schema } from '@kbn/config-schema';
import type { FileKind } from '../../../common/types';
import { fileErrors } from '../../file';
import { FILES_API_ROUTES } from '../api_routes';
import type { FileKindRouter, FileKindsRequestHandler } from './types';
import { CreateRouteDefinition, FILES_API_ROUTES } from '../api_routes';
import type { CreateHandler, FileKindRouter } from './types';

import { getById } from './helpers';

export const method = 'delete' as const;

export const paramsSchema = schema.object({
id: schema.string(),
});

type Params = Ensure<DeleteFileKindHttpEndpoint['inputs']['params'], TypeOf<typeof paramsSchema>>;
const rt = {
params: schema.object({
id: schema.string(),
}),
};

type Response = DeleteFileKindHttpEndpoint['output'];
export type Endpoint = CreateRouteDefinition<typeof rt, { ok: true }>;

export const handler: FileKindsRequestHandler<Params> = async ({ files, fileKind }, req, res) => {
export const handler: CreateHandler<Endpoint> = async ({ files, fileKind }, req, res) => {
const {
params: { id },
} = req;
Expand All @@ -43,7 +41,7 @@ export const handler: FileKindsRequestHandler<Params> = async ({ files, fileKind
}
throw e;
}
const body: Response = {
const body: Endpoint['output'] = {
ok: true,
};
return res.ok({ body });
Expand All @@ -54,9 +52,7 @@ export function register(fileKindRouter: FileKindRouter, fileKind: FileKind) {
fileKindRouter[method](
{
path: FILES_API_ROUTES.fileKind.getDeleteRoute(fileKind.id),
validate: {
params: paramsSchema,
},
validate: { ...rt },
options: {
tags: fileKind.http.delete.tags,
},
Expand Down
Loading

0 comments on commit 6429468

Please sign in to comment.