Connect the website to the API

This commit is contained in:
Gender Shrapnel 2019-10-27 13:41:16 +01:00
parent ab791efe42
commit 32e8dfa4bb
8 changed files with 66 additions and 38 deletions

View File

@ -1,34 +1,38 @@
import { DateTime } from "luxon";
import { APIError, Task } from "./types";
export interface Task {
id: number;
name: string;
due: DateTime;
export const getTasks = async (
limit: number = 10,
offset: number = 0
): Promise<{ items: Task[]; count: number }> => {
const url = new URL("tasks", API_URL);
url.searchParams.set("limit", limit.toString());
url.searchParams.set("offset", offset.toString());
const response = await fetch(url.toString());
if (!response.ok) {
throw new APIError("Task fetch failed");
}
const tasks: Task[] = [...Array(5).keys()].map(n => ({
id: n,
name: `Task ${n}`,
due: DateTime.local().plus({ weeks: n })
}));
const json = await response.json();
if (!Number.isSafeInteger(json.count) || !Array.isArray(json.tasks)) {
throw new APIError("Invalid response from server.");
}
const count: number = json.count;
const items: Task[] = (json.tasks as any[]).map(t => {
if (
typeof t !== "object" ||
!Number.isSafeInteger(t.id) ||
typeof t.name !== "string"
) {
throw new APIError("Invalid response from server.");
}
export const getTasks = async (): Promise<Task[]> =>
new Promise<Task[]>((resolve, reject) =>
setTimeout(() => resolve(tasks), 3000)
);
return {
id: t.id,
name: t.name,
schedule: { type: "Plain" }
};
});
export const schedule = async (task: Omit<Task, "id">): Promise<Task> =>
new Promise<Task>((resolve, reject) =>
setTimeout(() => {
const id = Math.max(...tasks.map(t => t.id)) + 1;
tasks[id] = { ...task, id };
resolve(tasks[id]);
}, 3000)
);
export const unschedule = async (taskId: number): Promise<void> =>
new Promise<void>((resolve, reject) =>
setTimeout(() => {
delete tasks[taskId];
}, 3000)
);
return { items, count };
};

View File

@ -0,0 +1,8 @@
export class APIError extends Error {}
export interface Task {
id: number;
name: string;
schedule: {
type: "Plain";
};
}

1
src/frontend/globals.d.ts vendored Normal file
View File

@ -0,0 +1 @@
declare const API_URL: string;

View File

@ -1,5 +1,6 @@
import { getTasks } from "@kredens/frontend/api/tasks";
import { Task as APITask } from "@kredens/frontend/api/types";
import { all, call, put, takeEvery } from "redux-saga/effects";
import { getTasks, Task as APITask } from "../api/tasks";
import { taskFetchError, taskFetchOk, taskFetchStart } from "./tasks/actions";
import { Task, TaskQuery, TaskScheduleType } from "./tasks/types";
@ -7,22 +8,26 @@ export function* fetchTasksSaga(query: TaskQuery = { limit: 10 }) {
yield put(taskFetchStart(query));
try {
const tasks: APITask[] = yield call(getTasks);
const tasks: { items: APITask[]; count: number } = yield call(
getTasks,
query.limit,
query.offset
);
yield put(
taskFetchOk(
query,
tasks
tasks.items
.map<[string, Task]>(t => [
t.id.toString(),
{
name: t.name,
schedule: {
type: TaskScheduleType.Once,
due: t.due.toISO()
type: TaskScheduleType[t.schedule.type]
}
}
])
.reduce((res, [id, task]) => ({ ...res, [id]: task }), {})
.reduce((res, [id, task]) => ({ ...res, [id]: task }), {}),
tasks.count
)
);
} catch (error) {

View File

@ -26,12 +26,14 @@ export function taskFetchStart(query: TaskQuery): TasksActionType {
export function taskFetchOk(
query: TaskQuery,
results: { [key: string]: Task }
results: { [key: string]: Task },
count: number
): TasksActionType {
return {
type: TasksAction.TASKS_FETCH_OK,
query,
results,
count,
fetched: DateTime.utc().toISO()
};
}

View File

@ -3,6 +3,7 @@ import { TasksAction, TasksActionType, TasksState } from "./types";
const initialState: TasksState = {
items: {},
count: 0,
queries: {}
};

View File

@ -21,8 +21,8 @@ export interface Task {
export interface TaskQuery {
query?: string;
after?: string;
limit: number;
offset?: number;
}
interface TaskQueryOk {
@ -48,6 +48,7 @@ export interface TasksState {
items: {
[key: string]: Task;
};
count: number;
queries: {
[key: string]: TaskQueryResult;
};
@ -82,6 +83,7 @@ interface TaskFetchOkAction {
type: TasksAction.TASKS_FETCH_OK;
query: TaskQuery;
results: { [key: string]: Task };
count: number;
fetched: string;
}

View File

@ -24,7 +24,12 @@ const config: webpack.Configuration = {
alias: {
"@kredens": path.resolve(__dirname, "src/")
}
}
},
plugins: [
new webpack.DefinePlugin({
API_URL: JSON.stringify("http://localhost:3000/api/")
})
]
};
export default config;