From 1784bb6677fc25b5fd265ab4475d51c59bf77ad8 Mon Sep 17 00:00:00 2001 From: Derek Sifford Date: Mon, 22 Jul 2019 15:38:30 -0400 Subject: [PATCH] [@wordpress/api-fetch] add API schema types for consumption by other packages (#37026) --- types/wordpress__api-fetch/index.d.ts | 705 +++++++++++++++++- .../wordpress__api-fetch-tests.ts | 17 +- 2 files changed, 701 insertions(+), 21 deletions(-) diff --git a/types/wordpress__api-fetch/index.d.ts b/types/wordpress__api-fetch/index.d.ts index e696d8d36c..3bd59b6a43 100644 --- a/types/wordpress__api-fetch/index.d.ts +++ b/types/wordpress__api-fetch/index.d.ts @@ -2,7 +2,7 @@ // Project: https://github.com/WordPress/gutenberg/tree/master/packages/api-fetch/README.md // Definitions by: Derek Sifford // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 3.4 +// TypeScript Version: 3.5 export interface APIFetchOptions extends RequestInit { /** @@ -28,10 +28,7 @@ export interface APIFetchOptions extends RequestInit { data?: object; } -export type Middleware = ( - options: APIFetchOptions, - next: (options: APIFetchOptions) => Promise -) => Promise; +export type Middleware = (options: APIFetchOptions, next: (options: APIFetchOptions) => Promise) => Promise; declare namespace apiFetch { function use(middleware: Middleware): void; @@ -41,9 +38,7 @@ declare namespace apiFetch { * custom fetch handler will receive the `options` passed to the `apiFetch` * calls. */ - function setFetchHandler( - handler: (options: APIFetchOptions) => PromiseLike - ): void; + function setFetchHandler(handler: (options: APIFetchOptions) => PromiseLike): void; // // Built-in middleware @@ -60,7 +55,697 @@ declare namespace apiFetch { const fetchAllMiddleware: Middleware; } -declare function apiFetch(options: APIFetchOptions & { parse: false }): Promise; // prettier-ignore +declare function apiFetch(options: APIFetchOptions & { parse: false }): Promise; declare function apiFetch(options: APIFetchOptions): Promise; // tslint:disable-line no-unnecessary-generics - export default apiFetch; + +export namespace Schema { + type Context = 'edit' | 'embed' | 'view'; + type Contextual = T extends 'edit' + ? { raw: string; rendered: string } & TAdditional & TEditAdditional + : { rendered: string } & TAdditional; + type OpenOrClosed = 'open' | 'closed'; + type PostFormat = + | 'aside' + | 'audio' + | 'chat' + | 'gallery' + | 'image' + | 'link' + | 'quote' + | 'standard' + | 'status' + | 'video'; + type PostStatus = 'draft' | 'future' | 'pending' | 'private' | 'publish'; + type TaxonomyKind = 'category' | 'link_category' | 'nav_menu' | 'post_format' | 'post_tag'; + + interface BaseResponse { + _embedded?: Record; + _links: Record< + string, + Array<{ + href: string; + [k: string]: any; + }> + >; + } + + interface BasePost extends BaseResponse { + author: number; + categories: number[]; + comment_status: OpenOrClosed; + content: Contextual; + date: string; + date_gmt: string; + excerpt: Contextual; + featured_media: number; + format: PostFormat; + generated_slug: string; + guid: Contextual; + id: number; + link: string; + meta: any[]; + modified: string; + modified_gmt: string; + password: string; + permalink_template: string; + ping_status: OpenOrClosed; + slug: string; + status: PostStatus; + sticky: boolean; + tags: string[]; + template: string; + title: Contextual; + type: string; + } + + interface BasePostRevision extends BaseResponse { + author: number; + content: Contextual; + date: string; + date_gmt: string; + excerpt: Contextual; + guid: Contextual; + id: number; + modified: string; + modified_gmt: string; + parent: number; + slug: string; + title: Contextual; + } + + interface BaseCategory extends BaseResponse { + count: number; + description: string; + id: number; + link: string; + meta: any[]; + name: string; + parent: number; + slug: string; + taxonomy: TaxonomyKind; + } + + interface BaseTag extends BaseResponse { + count: number; + description: string; + id: number; + link: string; + meta: any[]; + name: string; + slug: string; + taxonomy: TaxonomyKind; + } + + interface BasePage extends Omit, 'categories' | 'format' | 'sticky' | 'tags'> { + menu_order: number; + parent: number; + } + + interface BaseComment extends BaseResponse { + author: number; + author_avatar_urls: Record; + author_email: string; + author_ip: string; + author_name: string; + author_url: string; + author_user_agent: string; + content: Contextual; + date: string; + date_gmt: string; + id: number; + link: string; + meta: any[]; + parent: number; + post: number; + status: 'approved' | 'hold' | 'spam' | 'trash'; + type: string; + } + + interface BaseTaxonomy extends BaseResponse { + capabilities: { + assign_terms: string; + delete_terms: string; + edit_terms: string; + manage_terms: string; + }; + description: string; + hierarchical: boolean; + labels: { + add_new_item: string; + add_or_remove_items: string | null; + all_items: string; + back_to_items: string; + choose_from_most_used: string | null; + edit_item: string; + items_list: string; + items_list_navigation: string; + menu_name: string; + most_used: string; + name: string; + name_admin_bar: string; + new_item_name: string; + no_terms: string; + not_found: string; + parent_item: string | null; + parent_item_colon: string | null; + popular_items: string | null; + search_items: string; + separate_items_with_commas: string | null; + singular_name: string; + update_item: string; + view_item: string; + }; + name: string; + rest_base: string; + show_cloud: boolean; + slug: string; + types: string[]; + visibility: { + public: boolean; + publicly_queryable: boolean; + show_admin_column: boolean; + show_in_nav_menus: boolean; + show_in_quick_edit: boolean; + show_ui: boolean; + }; + } + + interface BaseMedia extends BaseResponse { + alt_text: string; + author: number; + caption: Contextual; + comment_status: OpenOrClosed; + date: string; + date_gmt: string; + description: Contextual; + generated_slug: string; + guid: Contextual; + id: number; + link: string; + media_type: 'file' | 'image'; + media_details: { + sizes: Record< + string, + { + file: string; + height: number; + mime_type: string; + source_url: string; + width: number; + } + >; + file?: string; + height?: number; + image_meta?: { + aperture: string; + camera: string; + caption: string; + copyright: string; + created_timestamp: string; + credit: string; + focal_length: string; + iso: string; + keywords: string[]; + orientation: string; + shutter_speed: string; + title: string; + }; + width?: number; + }; + meta: any[]; + mime_type: string; + modified: string; + modified_gmt: string; + permalink_template: string; + ping_status: OpenOrClosed; + post: number | null; + slug: string; + source_url: string; + status: PostStatus | 'inherit'; + template: string; + title: Contextual; + type: string; + } + + interface BaseUser extends BaseResponse { + avatar_urls: Record; + capabilities: Record; + description: string; + email: string; + extra_capabilities: Record; + first_name: string; + id: number; + last_name: string; + link: string; + meta: any[]; + name: string; + nickname: string; + registered_date: string; + roles: string[]; + slug: string; + url: string; + username: string; + } + + interface BaseType extends BaseResponse { + capabilities: { + create_posts: string; + delete_others_posts: string; + delete_post: string; + delete_posts: string; + delete_private_posts: string; + delete_published_posts: string; + edit_others_posts: string; + edit_post: string; + edit_posts: string; + edit_private_posts: string; + edit_published_posts: string; + publish_posts: string; + read: string; + read_post: string; + read_private_posts: string; + }; + description: string; + hierarchical: boolean; + labels: { + add_new: string; + add_new_item: string; + all_items: string; + archives: string; + attributes: string; + edit_item: string; + featured_image: string; + filter_items_list: string; + insert_into_item: string; + item_published: string; + item_published_privately: string; + item_reverted_to_draft: string; + item_scheduled: string; + item_updated: string; + items_list: string; + items_list_navigation: string; + menu_name: string; + name: string; + name_admin_bar: string; + new_item: string; + not_found: string; + not_found_in_trash: string; + parent_item_colon: string; + remove_featured_image: string; + search_items: string; + set_featured_image: string; + singular_name: string; + uploaded_to_this_item: string; + use_featured_image: string; + view_item: string; + view_items: string; + }; + name: string; + rest_base: string; + slug: string; + supports: { + author?: true; + comments?: true; + 'custom-fields'?: true; + editor?: true; + excerpt?: true; + 'page-attributes'?: true; + 'post-formats'?: true; + revisions?: true; + thumbnail?: true; + title?: true; + trackbacks?: true; + }; + taxonomies: TaxonomyKind[]; + viewable: boolean; + } + + interface BaseStatus extends BaseResponse { + name: string; + private: boolean; + protected: boolean; + public: boolean; + queryable: boolean; + show_in_list: boolean; + slug: string; + } + + interface BaseBlock extends BaseResponse { + content: { + protected: boolean; + raw: string; + } & (T extends 'edit' ? { block_version: number } : {}); + date: string; + date_gmt: string; + guid: Contextual; + id: number; + link: string; + modified: string; + modified_gmt: string; + password: string; + slug: string; + status: PostStatus; + template: string; + title: T extends 'embed' ? [] : { raw: string }; + type: string; + } + + // prettier-ignore + type Block = + T extends 'embed' ? Pick, EmbedKeys.Block> : + T extends 'view' ? Pick, ViewKeys.Block> : + BaseBlock; + + // prettier-ignore + type Category = + T extends 'embed' ? Pick : + T extends 'view' ? Pick : + BaseCategory; + + // prettier-ignore + type Comment = + T extends 'embed' ? Pick, EmbedKeys.Comment> : + T extends 'view' ? Pick, ViewKeys.Comment> : + BaseComment; + + // prettier-ignore + type Media = + T extends 'embed' ? Pick, EmbedKeys.Media> : + T extends 'view' ? Pick, ViewKeys.Media> : + BaseMedia; + + // prettier-ignore + type Page = + T extends 'embed' ? Pick, EmbedKeys.Page> : + T extends 'view' ? Pick, ViewKeys.Page> : + BasePage; + + // prettier-ignore + type Post = + T extends 'embed' ? Pick, EmbedKeys.Post> : + T extends 'view' ? Pick, ViewKeys.Post> : + BasePost; + + // prettier-ignore + type PostRevision = + T extends 'embed' ? Pick, EmbedKeys.PostRevision> : + T extends 'view' ? Pick, ViewKeys.PostRevision> : + BasePostRevision; + + interface SearchResult extends BaseResponse { + id: number; + subtype: string; + title: string; + type: string; + url: string; + } + + interface Settings { + date_format: string; + default_category: number; + default_comment_status: OpenOrClosed; + default_ping_status: OpenOrClosed; + default_post_format: string; + description: string; + email: string; + language: string; + posts_per_page: number; + start_of_week: number; + time_format: string; + timezone: string; + title: string; + url: string; + use_smilies: boolean; + } + + // prettier-ignore + type Status = + T extends 'embed' ? Pick : + T extends 'view' ? Pick : + BaseStatus; + + // prettier-ignore + type Tag = + T extends 'embed' ? Pick : + BaseTag; + + // prettier-ignore + type Taxonomy = + T extends 'embed' ? Pick, EmbedKeys.Taxonomy> : + T extends 'view' ? Pick, ViewKeys.Taxonomy> : + BaseTaxonomy; + + interface Theme { + theme_supports: { + formats: PostFormat[]; + 'post-thumbnails': boolean; + 'responsive-embeds': boolean; + }; + } + + // prettier-ignore + type Type = + T extends 'embed' ? Pick, EmbedKeys.Type> : + T extends 'view' ? Pick, ViewKeys.Type> : + BaseType; + + // prettier-ignore + type User = + T extends 'embed' ? Pick, EmbedKeys.User> : + T extends 'view' ? Pick, ViewKeys.User> : + BaseUser; + + namespace EmbedKeys { + // prettier-ignore + type Block = + | keyof BaseResponse + | 'date' + | 'id' + | 'link' + | 'slug' + | 'title' + | 'type'; + + // prettier-ignore + type Category = + | keyof BaseResponse + | 'id' + | 'link' + | 'name' + | 'slug' + | 'taxonomy'; + + type Comment = + | keyof BaseResponse + | 'author' + | 'author_avatar_urls' + | 'author_name' + | 'author_url' + | 'content' + | 'date' + | 'id' + | 'link' + | 'parent' + | 'type'; + + type Media = + | keyof BaseResponse + | 'alt_text' + | 'author' + | 'caption' + | 'date' + | 'id' + | 'link' + | 'media_details' + | 'media_type' + | 'mime_type' + | 'slug' + | 'source_url' + | 'title' + | 'type'; + + type Page = + | keyof BaseResponse + | 'author' + | 'date' + | 'excerpt' + | 'featured_media' + | 'id' + | 'link' + | 'slug' + | 'title' + | 'type'; + + type Post = + | keyof BaseResponse + | 'author' + | 'date' + | 'excerpt' + | 'featured_media' + | 'id' + | 'link' + | 'slug' + | 'title' + | 'type'; + + // prettier-ignore + type PostRevision = + | keyof BaseResponse + | 'author' + | 'date' + | 'excerpt' + | 'id' + | 'parent' + | 'slug' + | 'title'; + + // prettier-ignore + type Status = + | keyof BaseResponse + | 'name' + | 'slug'; + + // prettier-ignore + type Tag = + | keyof BaseResponse + | 'id' + | 'link' + | 'name' + | 'slug' + | 'taxonomy'; + + // prettier-ignore + type Taxonomy = + | keyof BaseResponse + | 'name' + | 'rest_base' + | 'slug'; + + // prettier-ignore + type Type = + | keyof BaseResponse + | 'name' + | 'rest_base' + | 'slug'; + + // prettier-ignore + type User = + | keyof BaseResponse + | 'avatar_urls' + | 'description' + | 'id' + | 'link' + | 'name' + | 'slug' + | 'url'; + } + + namespace ViewKeys { + type Block = + | EmbedKeys.Block + | 'content' + | 'date_gmt' + | 'guid' + | 'modified' + | 'modified_gmt' + | 'status' + | 'template'; + + // prettier-ignore + type Category = + | EmbedKeys.Category + | 'count' + | 'description' + | 'meta' + | 'parent'; + + // prettier-ignore + type Comment = + | EmbedKeys.Comment + | 'date_gmt' + | 'meta' + | 'post' + | 'status'; + + type Media = + | EmbedKeys.Media + | 'comment_status' + | 'date_gmt' + | 'description' + | 'guid' + | 'meta' + | 'modified' + | 'modified_gmt' + | 'ping_status' + | 'post' + | 'status' + | 'template'; + + type Page = + | EmbedKeys.Page + | 'comment_status' + | 'content' + | 'date_gmt' + | 'guid' + | 'menu_order' + | 'meta' + | 'modified' + | 'modified_gmt' + | 'parent' + | 'ping_status' + | 'status' + | 'template'; + + type Post = + | EmbedKeys.Post + | 'categories' + | 'comment_status' + | 'content' + | 'date_gmt' + | 'format' + | 'guid' + | 'meta' + | 'modified' + | 'modified_gmt' + | 'ping_status' + | 'status' + | 'sticky' + | 'tags' + | 'template'; + + // prettier-ignore + type PostRevision = + | EmbedKeys.PostRevision + | 'content' + | 'date_gmt' + | 'guid' + | 'modified' + | 'modified_gmt'; + + // prettier-ignore + type Status = + | EmbedKeys.Status + | 'public' + | 'queryable'; + + // prettier-ignore + type Taxonomy = + | EmbedKeys.Taxonomy + | 'description' + | 'hierarchical' + | 'types'; + + // prettier-ignore + type Type = + | EmbedKeys.Type + | 'description' + | 'hierarchical' + | 'taxonomies'; + + // prettier-ignore + type User = + | EmbedKeys.User + | 'meta'; + } +} diff --git a/types/wordpress__api-fetch/wordpress__api-fetch-tests.ts b/types/wordpress__api-fetch/wordpress__api-fetch-tests.ts index 1892621ab4..aeb7241e9f 100644 --- a/types/wordpress__api-fetch/wordpress__api-fetch-tests.ts +++ b/types/wordpress__api-fetch/wordpress__api-fetch-tests.ts @@ -1,13 +1,8 @@ -import apiFetch, { Middleware } from "@wordpress/api-fetch"; - -interface Post { - title: string; - timestamp: number; -} +import apiFetch, { Middleware, Schema } from '@wordpress/api-fetch'; async function foo() { - apiFetch({ path: "/wp/v2/posts" }).then(posts => - posts.map(({ title, timestamp }) => `Post "${title}" at ${timestamp}`) + apiFetch({ path: '/wp/v2/posts' }).then(posts => + posts.map(({ date, title }) => `Post "${title.rendered}" at ${date}`) ); const response = await apiFetch({ parse: false }); if (response.ok) { @@ -21,13 +16,13 @@ const x: Middleware = async (undefined, next) => { }; apiFetch.use(apiFetch.fetchAllMiddleware); -apiFetch.use(apiFetch.createRootURLMiddleware("https://foo.bar/wp-json")); +apiFetch.use(apiFetch.createRootURLMiddleware('https://foo.bar/wp-json')); apiFetch.setFetchHandler(options => { const { url, path, data, method } = options; - return fetch(url || path || "", { + return fetch(url || path || '', { method, - body: JSON.stringify(data) + body: JSON.stringify(data), }); });