diff --git a/README.md b/README.md
index f5f6dbd..251708a 100644
--- a/README.md
+++ b/README.md
@@ -121,6 +121,7 @@ See [Error Handling](https://github.com/miyajan/garoon-rest/tree/master/docs/err
## References
- [Schedule](https://github.com/miyajan/garoon-rest/tree/master/docs/schedule.md)
+- [Workflow](https://github.com/miyajan/garoon-rest/tree/master/docs/workflow.md)
## Contribution Guide
diff --git a/docs/workflow.md b/docs/workflow.md
new file mode 100644
index 0000000..d01b961
--- /dev/null
+++ b/docs/workflow.md
@@ -0,0 +1,49 @@
+# Workflow
+
+- [getRequests](#getrequests)
+
+## Overview
+
+```ts
+const client = new GaroonRestAPIClient();
+
+(async () => {
+ try {
+ console.log(await client.workflow.getRequests());
+ } catch (error) {
+ console.log(error);
+ }
+})();
+```
+
+- All methods are defined on the `workflow` property.
+- This method returns a Promise object that is resolved with an object having properties in each `Returns` section.
+
+## Methods
+
+### getRequests
+
+Get the all request data.
+
+#### Parameters
+
+| Name | Type | Required | Description |
+| -------------------- | :--------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| limit | Number | | The number of requests to retrieve.
Must be between `1` and `1000`.
If nothing is specified, it will default to `100`. |
+| offset | Number | | The number of retrievals that will be skipped.
Must be between `0` and `2147483647`. If nothing is specified, it will default to `0`. |
+| fields | Array\ | | The response properties to get. |
+| orderBy | Object | | An object containing data of sort settings. |
+| orderBy.property | String | Yes | The property name. Possible values are: `createdAt`. If nothing is specified, it will default to `createdAt`. |
+| orderBy.order | String | Yes | The sort order. Possible values are: `asc`, `desc`. If nothing is specified, it will default to `asc`. |
+| rangeStartApprovedAt | String | | The start approved datetime for the search. The format is RFC3339. (e.g. `2020-01-01T00:00:00Z`)
If `rangeEndApprovedAt` is specified, `rangeStartApprovedAt` must be before the `rangeEndApprovedAt`. |
+| rangeEndApprovedAt | String | | The end approved datetime for the search. The format is RFC3339. (e.g. `2020-01-01T00:00:00Z`)
If `rangeStartApprovedAt` is specified, `rangeEndApprovedAt` must be later than `rangeStartApprovedAt`. |
+| form | Number or String | | The form ID. |
+| status | Array\ | | The request status. Possible values are: `UNPROCESSING`, `IN_PROGRESS`, `REJECTED`, `WITHDRAWN`, `SENT_BACK`, `CANCELLED`, `APPROVED`, `COMPLETED`. If nothing is specified, it will default to all statuses. |
+
+#### Returns
+
+See the example response in the `Reference`.
+
+#### Reference
+
+- https://developer.cybozu.io/hc/ja/articles/360031071011#step1
diff --git a/src/GaroonRestAPIClient.ts b/src/GaroonRestAPIClient.ts
index 7e0b501..7290792 100644
--- a/src/GaroonRestAPIClient.ts
+++ b/src/GaroonRestAPIClient.ts
@@ -9,6 +9,7 @@ import { GaroonErrorResponse, GaroonRestAPIError } from "./GaroonRestAPIError";
import { platformDeps } from "./platform";
import { UnsupportedPlatformError } from "./platform/UnsupportedPlatformError";
import { GaroonRequestConfigBuilder } from "./GaroonRequestConfigBuilder";
+import { WorkflowClient } from "./client/WorkflowClient";
export type DiscriminatedAuth = PasswordAuth | SessionAuth | OAuthTokenAuth;
@@ -93,6 +94,7 @@ const buildDiscriminatedAuth = (auth: Auth): DiscriminatedAuth => {
export class GaroonRestAPIClient {
readonly schedule: ScheduleClient;
+ readonly workflow: WorkflowClient;
private readonly baseUrl: string;
constructor(options: Options = {}) {
@@ -109,6 +111,7 @@ export class GaroonRestAPIClient {
requestConfigBuilder,
});
this.schedule = new ScheduleClient(httpClient);
+ this.workflow = new WorkflowClient(httpClient);
}
public getBaseUrl(): string {
diff --git a/src/client/ScheduleClient.ts b/src/client/ScheduleClient.ts
index 27591ac..eea5454 100644
--- a/src/client/ScheduleClient.ts
+++ b/src/client/ScheduleClient.ts
@@ -39,7 +39,7 @@ export class ScheduleClient {
}
const { fields, orderBy, excludeFromSearch, ...rest } = params;
- const data: any = rest;
+ const data: Record = rest as Record;
if (fields) {
data.fields = fields.join(",");
}
diff --git a/src/client/WorkflowClient.ts b/src/client/WorkflowClient.ts
new file mode 100644
index 0000000..e1d9bf6
--- /dev/null
+++ b/src/client/WorkflowClient.ts
@@ -0,0 +1,103 @@
+import { HttpClient } from "../http";
+import { Item, Operation, Status } from "./types";
+import { buildPath } from "../url";
+
+export class WorkflowClient {
+ private readonly client: HttpClient;
+
+ constructor(client: HttpClient) {
+ this.client = client;
+ }
+
+ public getRequests(params?: {
+ limit?: number;
+ offset?: number;
+ fields?: string[];
+ orderBy?: {
+ property: "createdAt";
+ order: "asc" | "desc";
+ };
+ rangeStartApprovedAt?: string;
+ rangeEndApprovedAt?: string;
+ form?: string | number;
+ status?: Status[];
+ }): Promise<{
+ requests: Array<{
+ id: string;
+ status: {
+ name: string;
+ type: Status;
+ };
+ createdAt: string;
+ processingStepCode: string;
+ name: string;
+ number: string;
+ isUrgent: boolean;
+ applicant: {
+ id: string;
+ code: string;
+ name: string;
+ proxy?: {
+ id: string;
+ code: string;
+ name: string;
+ };
+ };
+ form: {
+ id: string;
+ name: string;
+ };
+ items: {
+ [itemCode: string]: Item;
+ };
+ steps: {
+ [stepCode: string]: {
+ id: string;
+ name: string;
+ requirement: string;
+ isApprovalStep: 0 | 1;
+ processors: Array<{
+ id: string;
+ code: string;
+ name: string;
+ result: string;
+ date: string;
+ comment: string;
+ proxy: {
+ id: string;
+ code: string;
+ name: string;
+ };
+ }>;
+ };
+ };
+ availableOperations: {
+ list: Operation[];
+ sentBackTargets: string[];
+ };
+ folder: Array<{
+ id: string;
+ type: "UNPROCESSED" | "SENT" | "RECEIVED" | "DRAFT" | "FINISH";
+ }>;
+ }>;
+ }> {
+ const path = buildPath({ endpointName: "workflow/admin/requests" });
+
+ if (!params) {
+ return this.client.get(path, {});
+ }
+
+ const { fields, orderBy, status, ...rest } = params;
+ const data: Record = rest as Record;
+ if (fields) {
+ data.fields = fields.join(",");
+ }
+ if (orderBy) {
+ data.orderBy = `${orderBy.property} ${orderBy.order}`;
+ }
+ if (status) {
+ data.status = status.join(",");
+ }
+ return this.client.get(path, data);
+ }
+}
diff --git a/src/client/__tests__/WorkflowClient.test.ts b/src/client/__tests__/WorkflowClient.test.ts
new file mode 100644
index 0000000..ca2bddc
--- /dev/null
+++ b/src/client/__tests__/WorkflowClient.test.ts
@@ -0,0 +1,62 @@
+import { MockClient } from "../../http/MockClient";
+import { GaroonRequestConfigBuilder } from "../../GaroonRequestConfigBuilder";
+import { errorResponseHandler } from "../../GaroonRestAPIClient";
+import { WorkflowClient } from "../WorkflowClient";
+import { Status } from "../types/workflow";
+
+describe("WorkflowClient", () => {
+ let mockClient: MockClient;
+ let workflowClient: WorkflowClient;
+
+ beforeEach(() => {
+ const requestConfigBuilder = new GaroonRequestConfigBuilder({
+ baseUrl: "https://example.cybozu.com/g",
+ auth: {
+ type: "password",
+ username: "cybozu",
+ password: "cybozu",
+ },
+ });
+ mockClient = new MockClient({ requestConfigBuilder, errorResponseHandler });
+ workflowClient = new WorkflowClient(mockClient);
+ });
+
+ describe("getRequests", () => {
+ const params = {
+ limit: 100,
+ offset: 0,
+ fields: ["id", "number"],
+ orderBy: {
+ property: "createdAt" as const,
+ order: "desc" as const,
+ },
+ rangeStartApprovedAt: "2020-01-01T00:00:00Z",
+ rangeEndApprovedAt: "2020-12-30T00:00:00Z",
+ form: 1,
+ status: ["UNPROCESSING" as const, "IN_PROGRESS" as const],
+ };
+ beforeEach(async () => {
+ await workflowClient.getRequests(params);
+ });
+ it("should pass the path to the http client", () => {
+ expect(mockClient.getLogs()[0].path).toBe(
+ "/api/v1/workflow/admin/requests"
+ );
+ });
+ it("should send a get request", () => {
+ expect(mockClient.getLogs()[0].method).toBe("get");
+ });
+ it("should pass limit, offset, fields, orderBy, rangeStartApprovedAt, rangeEndApprovedAt, form and status as a param to the http client", () => {
+ expect(mockClient.getLogs()[0].params).toEqual({
+ limit: 100,
+ offset: 0,
+ fields: "id,number",
+ orderBy: "createdAt desc",
+ rangeStartApprovedAt: "2020-01-01T00:00:00Z",
+ rangeEndApprovedAt: "2020-12-30T00:00:00Z",
+ form: 1,
+ status: "UNPROCESSING,IN_PROGRESS",
+ });
+ });
+ });
+});
diff --git a/src/client/types/index.ts b/src/client/types/index.ts
index a473adb..386ff83 100644
--- a/src/client/types/index.ts
+++ b/src/client/types/index.ts
@@ -1 +1,2 @@
export * from "./schedule";
+export * from "./workflow";
diff --git a/src/client/types/workflow/index.ts b/src/client/types/workflow/index.ts
new file mode 100644
index 0000000..6013367
--- /dev/null
+++ b/src/client/types/workflow/index.ts
@@ -0,0 +1,95 @@
+export type Status =
+ | "UNPROCESSING"
+ | "IN_PROGRESS"
+ | "REJECTED"
+ | "WITHDRAWN"
+ | "SENT_BACK"
+ | "CANCELLED"
+ | "APPROVED"
+ | "COMPLETED";
+export type Operation =
+ | "SENT_BACK"
+ | "APPROVE"
+ | "REJECT"
+ | "WITHDRAW"
+ | "CANCEL"
+ | "CONFIRM"
+ | "ACKNOWLEDGE";
+export type Item =
+ | SingleLineTextItem
+ | MultiLineTextItem
+ | NumberItem
+ | CalcItem
+ | CheckBoxItem
+ | RadioButtonItem
+ | DropDownItem
+ | FileItem
+ | DateItem
+ | DateTimeItem
+ | RouteNaviItem;
+export type SingleLineTextItem = {
+ name: string;
+ type: "SINGLE_LINE_TEXT";
+ value: string;
+};
+export type MultiLineTextItem = {
+ name: string;
+ type: "MULTI_LINE_TEXT";
+ value: string;
+};
+export type NumberItem = {
+ name: string;
+ type: "NUMBER";
+ value: string;
+};
+export type CalcItem = {
+ name: string;
+ type: "CALC";
+ value: string;
+};
+export type CheckBoxItem = {
+ name: string;
+ type: "CHECK_BOX";
+ value: boolean;
+};
+export type RadioButtonItem = {
+ name: string;
+ type: "RADIO_BUTTON";
+ value: string;
+};
+export type DropDownItem = {
+ name: string;
+ type: "DROP_DOWN";
+ value: string;
+};
+export type FileItem = {
+ name: string;
+ type: "FILE";
+ value: Array<{
+ id: string;
+ contentType: string;
+ name: string;
+ size: string;
+ }>;
+};
+export type DateItem = {
+ name: string;
+ type: "DATE";
+ value: string;
+};
+export type DateTimeItem = {
+ name: string;
+ type: "DATETIME";
+ value: {
+ date: string;
+ time: string;
+ };
+};
+export type RouteNaviItem = {
+ name: string;
+ type: "ROUTE_NAVI";
+ value: {
+ route: string;
+ expense: string;
+ };
+};