-
Notifications
You must be signed in to change notification settings - Fork 255
/
postType.ts
155 lines (139 loc) · 4.55 KB
/
postType.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import { Handler } from "../../../types";
import capitalize from "./utils/capitalize";
import { ServerError } from "@frontity/source";
import { PostTypeData } from "@frontity/source/types/data";
/**
* The parameters for {@link postTypeHandler}.
*/
interface PostTypeHandlerParams {
/**
* The list of [WP REST API endpoints](https://developer.wordpress.org/rest-api/reference/)
* from which the generated handler is going to fetch the data.
*/
endpoints: string[];
}
/**
* A {@link Handler} function generator for WordPress Post Types.
*
* This function will generate a handler function for specific
* [WP REST API endpoints](https://developer.wordpress.org/rest-api/reference/)
* from which the data is going to be fetched. The generated handler will fetch
* data from all specified endpoints.
*
* @param options - Options for the handler generator: {@link PostTypeHandlerParams}.
*
* @example
* ```js
* const postTypeHandlerFunc = postTypeHandler({ endpoints: ['post']});
* libraries.source.handlers.push({
* name: "post type",
* priority: 30,
* pattern: "/(.*)?/:slug",
* func: postTypeHandlerFunc,
* })
* ```
*
* @returns An async "handler" function that can be passed as an argument to the handler object.
* This function will be invoked by the frontity framework when calling `source.fetch()` for
* a specific entity.
*/
const postTypeHandler = ({
endpoints,
}: PostTypeHandlerParams): Handler => async ({
link,
params,
state,
libraries,
force,
}) => {
// Name of the endpoint that returned an entity.
// Used later in case this fetch is for a preview.
let matchedEndpoint = "";
// 1. search id in state or get the entity from WP REST API
const { route, query } = libraries.source.parse(link);
const routeData: Partial<PostTypeData> = state.source.get(route);
if (!routeData.id || force) {
const { slug } = params;
// 1.1 transform "posts" endpoint to state.source.postEndpoint
const finalEndpoints = endpoints.map((endpoint) =>
endpoint === "posts" ? state.source.postEndpoint : endpoint
);
// 1.2 iterate over finalEndpoints array
let isHandled = false;
let isMismatched = false;
for (const endpoint of finalEndpoints) {
const response = await libraries.source.api.get({
endpoint,
params: { slug, _embed: true, ...state.source.params },
});
const populated = await libraries.source.populate({
response,
state,
force,
});
// exit loop if this endpoint returns an entity!
if (populated.length > 0) {
// We have to check if the link property in the data that we
// populated is the same as the current route.
if (populated[0].link === route) {
isHandled = true;
isMismatched = false;
matchedEndpoint = endpoint;
break;
} else {
isMismatched = true;
}
}
}
if (isMismatched) {
throw new ServerError(
`You have tried to access content at route: ${route} but it does not exist`,
404
);
}
// 1.3 if no entity has found, throw an error
if (!isHandled)
throw new ServerError(
`post type from endpoints "${endpoints}" with slug "${slug}" not found`,
404
);
}
// 2. get `type` and `id` from route data and assign props to data
const { type, id }: Partial<PostTypeData> = state.source.get(route);
const data = state.source.get(link);
Object.assign(data, {
type,
link: link,
query,
id,
isPostType: true,
[`is${capitalize(type)}`]: true,
}) as PostTypeData; // This ensures the resulting type is correct.
// Overwrite properties if the request is a preview.
if (query.preview && state.source.auth) {
// Get entity from the state.
const entity = state.source[type][id];
// Fetch the latest revision using the token.
const response = await libraries.source.api.get({
endpoint: `${matchedEndpoint}/${id}/revisions?per_page=1`,
params: state.source.params,
auth: state.source.auth,
});
// Get modified props from revision.
const revision = await response.json();
if (revision.code) {
console.log(revision);
throw new ServerError(revision.message, revision.data.status);
}
const [json] = revision;
if (json.parent === id) {
const { title, content, excerpt } = json;
// Merge props with entity.
Object.assign(entity, { title, content, excerpt });
} else {
// Error response.
console.warn(json);
}
}
};
export default postTypeHandler;