Skip to content

Commit be97100

Browse files
committed
feat: schedule.getEvents
1 parent 4322ca9 commit be97100

File tree

7 files changed

+312
-15
lines changed

7 files changed

+312
-15
lines changed

docs/schedule.md

+29
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,32 @@ See the example response in the `Reference`.
3838
#### Reference
3939

4040
- https://developer.cybozu.io/hc/ja/articles/360000440583#step1
41+
42+
### getEvents
43+
44+
Get events by specifying conditions.
45+
46+
#### Parameters
47+
48+
| Name | Type | Required | Description |
49+
| ----------------- | :--------------: | :-------------------------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
50+
| limit | Number | | The number of events to retrieve.<br />Must be between `1` and `1000`.<br />If nothing is specified, it will default to `100`. |
51+
| offset | Number | | The number of retrievals that will be skipped.<br />Must be between `0` and `2147483647`. If nothing is specified, it will default to `0`. |
52+
| fields | Array\<String\> | | The response properties to get. |
53+
| orderBy | Object | | An object containing data of sort settings. |
54+
| orderBy.property | String | Yes | The property name. Possible values are: `createdAt`, `updatedAt`, `start`. |
55+
| orderBy.order | String | Yes | The sort order. Possible values are: `asc`, `desc`. |
56+
| rangeStart | String | | The start datetime for the search. The format is RFC3339. (e.g. `2020-01-01T00:00:00Z`) |
57+
| rangeEnd | String | | The end datetime for the search. The format is RFC3339. (e.g. `2020-01-01T00:00:00Z`) |
58+
| target | Number or String | Conditionally<br />Required | The ID of an user or an organization or a facility. Required if `targetType` is specified. If nothing is specified, it will default to the login user ID. |
59+
| targetType | String | Conditionally<br />Required | The target type. Possible values are: `user`, `organization`, `facility`. Required if `target` is specified. If nothing is specified, it will default to `user`. |
60+
| keyword | String | Conditionally<br />Required | The search keyword. The keyword is searched for subject, company information, notes and comments. Required if `excludeFromSearch` is specified. |
61+
| excludeFromSearch | Array\<String\> | | The specified elements are excluded from the keyword search. Possible values are: `subject`, `company`, `notes`, `comments`. |
62+
63+
#### Returns
64+
65+
See the example response in the `Reference`.
66+
67+
#### Reference
68+
69+
- https://developer.cybozu.io/hc/ja/articles/360000440583#step2

src/client/ScheduleClient.ts

+36-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { HttpClient } from "../http";
2-
import { Event, EventID } from "./types";
2+
import { Event, EventID, ExcludeFromSearchElement, TargetID } from "./types";
33
import { buildPath } from "../url";
44

55
export class ScheduleClient {
@@ -16,4 +16,39 @@ export class ScheduleClient {
1616
});
1717
return this.client.get(path, {});
1818
}
19+
20+
public getEvents(params?: {
21+
limit?: number;
22+
offset?: number;
23+
fields?: string[];
24+
orderBy?: {
25+
property: "createdAt" | "updatedAt" | "start";
26+
order: "asc" | "desc";
27+
};
28+
rangeStart?: string;
29+
rangeEnd?: string;
30+
target?: TargetID;
31+
targetType?: "user" | "organization" | "facility";
32+
keyword?: string;
33+
excludeFromSearch?: ExcludeFromSearchElement[];
34+
}): Promise<{ events: Event[] }> {
35+
const path = buildPath({ endpointName: "schedule/events" });
36+
37+
if (!params) {
38+
return this.client.get(path, {});
39+
}
40+
41+
const { fields, orderBy, excludeFromSearch, ...rest } = params;
42+
const data: any = rest;
43+
if (fields) {
44+
data.fields = fields.join(",");
45+
}
46+
if (orderBy) {
47+
data.orderBy = `${orderBy.property} ${orderBy.order}`;
48+
}
49+
if (excludeFromSearch) {
50+
data.excludeFromSearch = excludeFromSearch.join(",");
51+
}
52+
return this.client.get(path, data);
53+
}
1954
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { MockClient } from "../../http/MockClient";
2+
import { ScheduleClient } from "../ScheduleClient";
3+
import { GaroonRequestConfigBuilder } from "../../GaroonRequestConfigBuilder";
4+
import { errorResponseHandler } from "../../GaroonRestAPIClient";
5+
6+
describe("ScheduleClient", () => {
7+
let mockClient: MockClient;
8+
let scheduleClient: ScheduleClient;
9+
10+
beforeEach(() => {
11+
const requestConfigBuilder = new GaroonRequestConfigBuilder({
12+
baseUrl: "https://example.cybozu.com/g",
13+
auth: {
14+
type: "session",
15+
},
16+
});
17+
mockClient = new MockClient({ requestConfigBuilder, errorResponseHandler });
18+
scheduleClient = new ScheduleClient(mockClient);
19+
});
20+
21+
describe("getEvent", () => {
22+
beforeEach(async () => {
23+
await scheduleClient.getEvent({ id: 1 });
24+
});
25+
it("should pass the path to the http client", () => {
26+
expect(mockClient.getLogs()[0].path).toBe("/api/v1/schedule/events/1");
27+
});
28+
it("should send a get request", () => {
29+
expect(mockClient.getLogs()[0].method).toBe("get");
30+
});
31+
it("should pass an empty object as a param to the http client", () => {
32+
expect(mockClient.getLogs()[0].params).toEqual({});
33+
});
34+
});
35+
36+
describe("getEvents", () => {
37+
describe("without parameter", () => {
38+
beforeEach(async () => {
39+
await scheduleClient.getEvents();
40+
});
41+
it("should pass the path to the http client", () => {
42+
expect(mockClient.getLogs()[0].path).toBe("/api/v1/schedule/events");
43+
});
44+
it("should send a get request", () => {
45+
expect(mockClient.getLogs()[0].method).toBe("get");
46+
});
47+
it("should pass an empty object as a param to the http client", () => {
48+
expect(mockClient.getLogs()[0].params).toEqual({});
49+
});
50+
});
51+
describe("with parameter", () => {
52+
beforeEach(async () => {
53+
await scheduleClient.getEvents({
54+
limit: 100,
55+
offset: 0,
56+
fields: ["id", "creator"],
57+
orderBy: {
58+
property: "createdAt",
59+
order: "asc",
60+
},
61+
rangeStart: "2017-10-19T00:10:30Z",
62+
rangeEnd: "2017-10-19T01:10:30Z",
63+
target: 1,
64+
targetType: "user",
65+
keyword: "test",
66+
excludeFromSearch: ["subject", "company"],
67+
});
68+
});
69+
it("should pass the path to the http client", () => {
70+
expect(mockClient.getLogs()[0].path).toBe("/api/v1/schedule/events");
71+
});
72+
it("should send a get request", () => {
73+
expect(mockClient.getLogs()[0].method).toBe("get");
74+
});
75+
it("should pass limit, offset, fields, orderBy, rangeStart, rangeEnd, target, targetType, keyword and excludeFromSearch as a param to the http client", () => {
76+
expect(mockClient.getLogs()[0].params).toEqual({
77+
limit: 100,
78+
offset: 0,
79+
fields: "id,creator",
80+
orderBy: "createdAt asc",
81+
rangeStart: "2017-10-19T00:10:30Z",
82+
rangeEnd: "2017-10-19T01:10:30Z",
83+
target: 1,
84+
targetType: "user",
85+
keyword: "test",
86+
excludeFromSearch: "subject,company",
87+
});
88+
});
89+
});
90+
});
91+
});

src/client/types/schedule/index.ts

+6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
export type EventID = string | number;
2+
export type TargetID = string | number;
3+
export type ExcludeFromSearchElement =
4+
| "subject"
5+
| "company"
6+
| "notes"
7+
| "comments";
28

39
export type Event = {
410
id: string;

src/http/HttpClientInterface.ts

+24-12
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,29 @@
11
import FormData from "form-data";
22
export interface HttpClient {
3-
// eslint-disable-next-line @typescript-eslint/ban-types
4-
get: <T extends object>(path: string, params: object) => Promise<T>;
5-
// eslint-disable-next-line @typescript-eslint/ban-types
6-
getData: (path: string, params: object) => Promise<ArrayBuffer>;
7-
// eslint-disable-next-line @typescript-eslint/ban-types
8-
post: <T extends object>(path: string, params: object) => Promise<T>;
9-
// eslint-disable-next-line @typescript-eslint/ban-types
10-
postData: <T extends object>(path: string, params: FormData) => Promise<T>;
11-
// eslint-disable-next-line @typescript-eslint/ban-types
12-
put: <T extends object>(path: string, params: object) => Promise<T>;
13-
// eslint-disable-next-line @typescript-eslint/ban-types
14-
delete: <T extends object>(path: string, params: object) => Promise<T>;
3+
get: <T extends Record<string, unknown>>(
4+
path: string,
5+
params: Record<string, unknown>
6+
) => Promise<T>;
7+
getData: (
8+
path: string,
9+
params: Record<string, unknown>
10+
) => Promise<ArrayBuffer>;
11+
post: <T extends Record<string, unknown>>(
12+
path: string,
13+
params: Record<string, unknown>
14+
) => Promise<T>;
15+
postData: <T extends Record<string, unknown>>(
16+
path: string,
17+
params: FormData
18+
) => Promise<T>;
19+
put: <T extends Record<string, unknown>>(
20+
path: string,
21+
params: Record<string, unknown>
22+
) => Promise<T>;
23+
delete: <T extends Record<string, unknown>>(
24+
path: string,
25+
params: Record<string, unknown>
26+
) => Promise<T>;
1527
}
1628

1729
export type ErrorResponse<T = any> = {

src/http/MockClient.ts

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import {
2+
HttpClient,
3+
RequestConfigBuilder,
4+
ErrorResponseHandler,
5+
} from "./HttpClientInterface";
6+
import FormData from "form-data";
7+
8+
type Log = {
9+
method: "get" | "post" | "put" | "delete";
10+
path: string;
11+
params: {
12+
[key: string]: any;
13+
};
14+
};
15+
16+
export class MockClient implements HttpClient {
17+
private readonly errorResponseHandler: ErrorResponseHandler;
18+
private readonly requestConfigBuilder: RequestConfigBuilder;
19+
logs: Log[];
20+
responses: Array<Record<string, unknown>>;
21+
22+
constructor({
23+
errorResponseHandler,
24+
requestConfigBuilder,
25+
}: {
26+
errorResponseHandler: ErrorResponseHandler;
27+
requestConfigBuilder: RequestConfigBuilder;
28+
}) {
29+
this.errorResponseHandler = errorResponseHandler;
30+
this.requestConfigBuilder = requestConfigBuilder;
31+
this.logs = [];
32+
this.responses = [];
33+
}
34+
35+
public mockResponse(mock: Record<string, unknown>) {
36+
this.responses.push(mock);
37+
}
38+
// eslint-disable-next-line @typescript-eslint/ban-types
39+
private createResponse<T extends object>(): T {
40+
const response = this.responses.shift() || {};
41+
if (response instanceof Error) {
42+
this.errorResponseHandler(response);
43+
}
44+
return response as T;
45+
}
46+
47+
public async get<T extends Record<string, unknown>>(
48+
path: string,
49+
params: any
50+
): Promise<T> {
51+
const requestConfig = await this.requestConfigBuilder.build(
52+
"get",
53+
path,
54+
params
55+
);
56+
this.logs.push({ method: requestConfig.method, path, params });
57+
return this.createResponse<T>();
58+
}
59+
public async getData(path: string, params: any): Promise<ArrayBuffer> {
60+
const requestConfig = await this.requestConfigBuilder.build(
61+
"get",
62+
path,
63+
params
64+
);
65+
this.logs.push({ method: requestConfig.method, path, params });
66+
return this.createResponse<ArrayBuffer>();
67+
}
68+
public async post<T extends Record<string, unknown>>(
69+
path: string,
70+
params: any
71+
): Promise<T> {
72+
const requestConfig = await this.requestConfigBuilder.build(
73+
"post",
74+
path,
75+
params
76+
);
77+
this.logs.push({ method: requestConfig.method, path, params });
78+
return this.createResponse<T>();
79+
}
80+
public async postData<T extends Record<string, unknown>>(
81+
path: string,
82+
formData: FormData
83+
): Promise<T> {
84+
const requestConfig = await this.requestConfigBuilder.build(
85+
"post",
86+
path,
87+
formData
88+
);
89+
this.logs.push({
90+
method: requestConfig.method,
91+
path,
92+
params: { formData },
93+
});
94+
return this.createResponse<T>();
95+
}
96+
public async put<T extends Record<string, unknown>>(
97+
path: string,
98+
params: any
99+
): Promise<T> {
100+
const requestConfig = await this.requestConfigBuilder.build(
101+
"put",
102+
path,
103+
params
104+
);
105+
this.logs.push({ method: requestConfig.method, path, params });
106+
return this.createResponse<T>();
107+
}
108+
public async delete<T extends Record<string, unknown>>(
109+
path: string,
110+
params: any
111+
): Promise<T> {
112+
const requestConfig = await this.requestConfigBuilder.build(
113+
"delete",
114+
path,
115+
params
116+
);
117+
this.logs.push({ method: requestConfig.method, path, params });
118+
return this.createResponse<T>();
119+
}
120+
public getLogs(): Log[] {
121+
return this.logs;
122+
}
123+
}

src/platform/index.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ type PlatformDeps = {
55
) => Promise<{ name: string; data: unknown }>;
66
getRequestToken: () => Promise<string>;
77
getDefaultAuth: () => DiscriminatedAuth;
8-
// eslint-disable-next-line @typescript-eslint/ban-types
9-
buildPlatformDependentConfig: (params: object) => object;
8+
buildPlatformDependentConfig: (
9+
params: Record<string, unknown>
10+
) => Record<string, unknown>;
1011
buildHeaders: () => Record<string, string>;
1112
buildFormDataValue: (data: unknown) => unknown;
1213
buildBaseUrl: (baseUrl?: string) => string;

0 commit comments

Comments
 (0)