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

Make model and UI support any kind of post #65

Open
wants to merge 23 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions src/api/ApiClient.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
/* eslint-disable react/no-is-mounted */
import { PlaygroundClient } from '@wp-playground/client';

Check warning on line 1 in src/api/ApiClient.ts

View workflow job for this annotation

GitHub Actions / lint

PlaygroundClient not found in '@wp-playground/client'
import { PostsApi } from '@/api/Posts';
import { BlogPostsApi } from '@/api/BlogPosts';
import { SettingsApi } from '@/api/Settings';
import { UsersApi } from '@/api/Users';
import { PostsApi } from '@/api/Posts';

export class ApiClient {
private readonly playgroundClient: PlaygroundClient;
private readonly _siteUrl: string;
private readonly _posts: PostsApi;
private readonly _blogPosts: BlogPostsApi;
private readonly _settings: SettingsApi;
private readonly _users: UsersApi;
private readonly _posts: PostsApi;

constructor( playgroundClient: PlaygroundClient, siteUrl: string ) {
this.playgroundClient = playgroundClient;
this._siteUrl = siteUrl;
this._posts = new PostsApi( this );
this._blogPosts = new BlogPostsApi( this );
this._settings = new SettingsApi( this );
this._users = new UsersApi( this );
}
Expand All @@ -27,6 +29,10 @@
return this._posts;
}

get blogPosts(): BlogPostsApi {
return this._blogPosts;
}

get settings(): SettingsApi {
return this._settings;
}
Expand Down Expand Up @@ -72,5 +78,3 @@
return response.json;
}
}

/* eslint-enable react/no-is-mounted */
121 changes: 121 additions & 0 deletions src/api/BlogPosts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/* eslint-disable camelcase */
import { WP_REST_API_Post } from 'wp-types';
type ApiPost = WP_REST_API_Post;
/* eslint-enable camelcase */

import { BlogPost } from '@/model/content/BlogPost';
import { ApiClient } from '@/api/ApiClient';
import {
DateField,
HtmlField,
newDateField,
newHtmlField,
newTextField,
PostType,
TextField,
} from '@/model/content/Post';

interface CreateBody {
guid: string;
}

interface UpdateBody {
date?: DateField;
title?: TextField;
content?: HtmlField;
}

interface PostMeta {
guid: string;
raw_title: string;
raw_date: string;
raw_content: string;
}

export class BlogPostsApi {
// eslint-disable-next-line no-useless-constructor
constructor( private readonly client: ApiClient ) {}

async create( body: CreateBody ): Promise< BlogPost > {
const response = ( await this.client.post( '/liberated_posts', {
meta: {
guid: body.guid,
},
} ) ) as ApiPost;
return fromApiResponse( response );
}

async update( id: number, body: UpdateBody ): Promise< BlogPost > {
const actualBody: any = {};
if ( body.date || body.title || body.content ) {
actualBody.meta = {};
}
if ( body.date ) {
actualBody.date = body.date.value.toISOString();
actualBody.meta.raw_date = body.date.original;
}
if ( body.title ) {
actualBody.title = body.title.parsed;
actualBody.meta.raw_title = body.title.original;
}
if ( body.content ) {
actualBody.content = body.content.parsed;
actualBody.meta.raw_content = body.content.original;
}
if ( Object.keys( actualBody ).length === 0 ) {
throw Error( 'attempting to update zero fields' );
}
const response = ( await this.client.post(
`/liberated_posts/${ id }`,
actualBody
) ) as ApiPost;
return fromApiResponse( response );
}

async findById( id: string ): Promise< BlogPost | null > {
// eslint-disable-next-line react/no-is-mounted
const posts = await this.find( { id } );
return posts.length === 0 ? null : fromApiResponse( posts[ 0 ] );
}

async findByGuid( guid: string ): Promise< BlogPost | null > {
// eslint-disable-next-line react/no-is-mounted
const posts = await this.find( { guid } );
return posts.length === 0 ? null : fromApiResponse( posts[ 0 ] );
}

private async find(
params: Record< string, string >
): Promise< ApiPost[] > {
// A liberated_post is always draft.
params.status = 'draft';
// Must set context to 'edit' to have all fields in the response.
params.context = 'edit';
return ( await this.client.get(
`/liberated_posts`,
params
) ) as ApiPost[];
}
}

function fromApiResponse( response: ApiPost ): BlogPost {
const meta = response.meta as unknown as PostMeta;
const date = newDateField( meta.raw_date, response.date_gmt );
const title = newTextField( meta.raw_title, response.title.raw ?? '' );
const content = newHtmlField(
meta.raw_content,
response.content.raw ?? ''
);

return {
type: PostType.BlogPost,
guid: meta.guid,
id: response.id,
url: response.link,
fields: {
date,
content,
title,
},
};
}
82 changes: 8 additions & 74 deletions src/api/Posts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,111 +3,45 @@ import { WP_REST_API_Post } from 'wp-types';
type ApiPost = WP_REST_API_Post;
/* eslint-enable camelcase */

import { Post, PostContent, PostDate, PostTitle } from '@/model/Post';
import { ApiClient } from '@/api/ApiClient';

interface CreateBody {
guid: string;
}

interface UpdateBody {
date?: PostDate;
title?: PostTitle;
content?: PostContent;
}
import { Post } from '@/model/content/Post';

interface PostMeta {
guid: string;
raw_title: string;
raw_date: string;
raw_content: string;
}

export class PostsApi {
// eslint-disable-next-line no-useless-constructor
constructor( private readonly client: ApiClient ) {}

async create( body: CreateBody ): Promise< Post > {
const response = ( await this.client.post( '/liberated_posts', {
meta: {
guid: body.guid,
},
} ) ) as ApiPost;
return makePostFromApiResponse( response );
}

async update( id: number, body: UpdateBody ): Promise< Post > {
const actualBody: any = {};
if ( body.date || body.title || body.content ) {
actualBody.meta = {};
}
if ( body.date ) {
actualBody.date = body.date.utcString;
actualBody.meta.raw_date = body.date.original;
}
if ( body.title ) {
actualBody.title = body.title.parsed;
actualBody.meta.raw_title = body.title.original;
}
if ( body.content ) {
actualBody.content = body.content.parsed;
actualBody.meta.raw_content = body.content.original;
}
if ( Object.keys( actualBody ).length === 0 ) {
throw Error( 'attempting to update zero fields' );
}
const response = ( await this.client.post(
`/liberated_posts/${ id }`,
actualBody
) ) as ApiPost;
return makePostFromApiResponse( response );
}

async findById( id: string ): Promise< Post | null > {
// eslint-disable-next-line react/no-is-mounted
const posts = await this.find( { id } );
return posts.length === 0
? null
: makePostFromApiResponse( posts[ 0 ] );
}

async findByGuid( guid: string ): Promise< Post | null > {
// eslint-disable-next-line react/no-is-mounted
const posts = await this.find( { guid } );
return posts.length === 0
? null
: makePostFromApiResponse( posts[ 0 ] );
return posts.length === 0 ? null : fromApiResponse( posts[ 0 ] );
}

private async find(
params: Record< string, string >
): Promise< ApiPost[] > {
// A liberated_post is always draft.
params.status = 'draft';
// Must set context to 'edit' to have all fields in the response.
params.context = 'edit';

// TODO: For now we're querying liberated_posts but this must be changed as this call needs to retrieve
// any kind of post.
return ( await this.client.get(
`/liberated_posts`,
params
) ) as ApiPost[];
}
}

function makePostFromApiResponse( response: ApiPost ): Post {
function fromApiResponse( response: ApiPost ): Post {
const meta = response.meta as unknown as PostMeta;
const date = new PostDate( meta.raw_date, response.date_gmt );
const title = new PostTitle( meta.raw_title, response.title.raw ?? '' );
const content = new PostContent(
meta.raw_content,
response.content.raw ?? ''
);

return {
type: response.type,
guid: meta.guid,
id: response.id,
url: response.link,
date,
content,
title,
fields: {},
};
}
44 changes: 0 additions & 44 deletions src/model/Post.ts

This file was deleted.

16 changes: 16 additions & 0 deletions src/model/content/BlogPost.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {
DateField,
GenericPost,
HtmlField,
PostType,
TextField,
} from '@/model/content/Post';

type BlogPostFields = {
date: DateField;
title: TextField;
content: HtmlField;
};

export interface BlogPost
extends GenericPost< PostType.BlogPost, BlogPostFields > {}
Loading
Loading