前置き
Pleasanterには、さまざまAPIが用意されており、単純なAPIだけではなく、内部スクリプトとして$p.apiGetや、items.Getなど様々なメソッドが用意されています。
ただ、公式マニュアルを読んだだけで、実装するのはなかなかハードルが高いと感じます。
そこで、マニュアルを見ずとも各種APIを利用できるよう、独自にスクリプトを組んでみました。
実装の前提
typescriptで実装することでパラメータの型を管理し、扱いやすく・誤りが少なくなるように。また、クラスを活用して、使いまわすことも想定します。(サーバスクリプト等)
実装するテーブルイメージ
AテーブルからBテーブルの情報を、$p.apiGetで取得することを考えます。
Aテーブル/スクリプトでデータを取得するテーブル
特に項目は設定せず、プロセスでスクリプトを実行できるようにします。(「実行」ボタン)
Onclickに、実装するスクリプトの関数を指定します。
Bテーブル/データ取得対象のテーブル
確認用にいろいろ項目を並べます。
結果イメージ
実装スクリプト
ApiGetを実装。
スクリプト-①
リクエストパラメータをenum、interfaceで管理します。
enum ApiDataType {
KeyValues = "KeyValues",
}
enum ApiColumnKeyDisplayType {
LabelText = "LabelText",
ColumnName = "ColumnName",
}
enum ApiColumnValueDisplayType {
DisplayValue = "DisplayValue",
Value = "Value",
Text = "Text",
}
interface ViewOptions {
Incomplete?: boolean | null;
Own?: boolean | null;
NearCompletionTime?: boolean | null;
Delay?: boolean | null;
Overdue?: boolean | null;
Search?: string | null;
ColumnFilterHash?: Map<string, string> | null;
ColumnFilterSearchTypes?: Map<string, string> | null;
ColumnFilterNegatives?: string[] | null;
ColumnSorterHash?: Map<string, string> | null;
ApiDataType?: ApiDataType | null;
ApiColumnKeyDisplayType?: ApiColumnKeyDisplayType | null;
ApiColumnValueDisplayType?: ApiColumnValueDisplayType | null;
ApiColumnHash?: Map<string, object> | null;
GridColumns?: string[] | null;
MergeSessionViewFilters?: boolean | null;
MergeSessionViewSorters?: boolean | null;
}
スクリプト-②
リクエストを生成するための、Api・Viewクラス。
パラメータは仕様に従い型を定義。特にKeyValueで指定するものは、Map型を使用。パラメータ設定用のメソッドも用意します。
インスタンス生成時はNullで生成し必要なパラメータを適宜設定します。
class Api {
constructor(
public ApiVersion: number | null = 1.1,
public ApiKey: string | null = null
) {}
}
class View extends Api {
Incomplete: boolean | null;
Own: boolean | null;
NearCompletionTime: boolean | null;
Delay: boolean | null;
Overdue: boolean | null;
Search: string | null;
ColumnFilterHash: Map<string, string> | null;
ColumnFilterSearchTypes: Map<string, string> | null;
ColumnFilterNegatives: string[] | null;
ColumnSorterHash: Map<string, string> | null;
ApiDataType: ApiDataType | null;
ApiColumnKeyDisplayType: ApiColumnKeyDisplayType | null;
ApiColumnValueDisplayType: ApiColumnValueDisplayType | null;
ApiColumnHash: Map<string, object> | null;
GridColumns: string[] | null;
MergeSessionViewFilters: boolean | null;
MergeSessionViewSorters: boolean | null;
constructor(options: ViewOptions) {
super();
this.Incomplete = options.Incomplete || null;
this.Own = options.Own || null;
this.NearCompletionTime = options.NearCompletionTime || null;
this.Delay = options.Delay || null;
this.Overdue = options.Overdue || null;
this.Search = options.Search || null;
this.ColumnFilterHash = options.ColumnFilterHash || null;
this.ColumnFilterSearchTypes = options.ColumnFilterSearchTypes || null;
this.ColumnFilterNegatives = options.ColumnFilterNegatives || null;
this.ColumnSorterHash = options.ColumnSorterHash || null;
this.ApiDataType = options.ApiDataType || null;
this.ApiColumnKeyDisplayType = options.ApiColumnKeyDisplayType || null;
this.ApiColumnValueDisplayType = options.ApiColumnValueDisplayType || null;
this.ApiColumnHash = options.ApiColumnHash || null;
this.GridColumns = options.GridColumns || null;
this.MergeSessionViewFilters = options.MergeSessionViewFilters || null;
this.MergeSessionViewSorters = options.MergeSessionViewSorters || null;
}
setColumnFilterHash({ key, value }: { key: string; value: string }) {
if (this.ColumnFilterHash === null) {
this.ColumnFilterHash = new Map();
}
this.ColumnFilterHash.set(key, value);
}
setColumnFilterSearchTypes({ key, value }: { key: string; value: string }) {
if (this.ColumnFilterSearchTypes === null) {
this.ColumnFilterSearchTypes = new Map();
}
this.ColumnFilterSearchTypes.set(key, value);
}
setColumnSorterHash({ key, value }: { key: string; value: string }) {
if (this.ColumnSorterHash === null) {
this.ColumnSorterHash = new Map();
}
this.ColumnSorterHash.set(key, value);
}
setColumnFilterNegatives({ value }: { value: string }) {
if (this.ColumnFilterNegatives === null) {
this.ColumnFilterNegatives = new Array();
}
this.ColumnFilterNegatives.push(value);
}
setApiColumnHash({ key, value }: { key: string; value: object }) {
if (this.ApiColumnHash === null) {
this.ApiColumnHash = new Map();
}
this.ApiColumnHash.set(key, value);
}
setGridColumns({ value }: { value: string }) {
if (this.GridColumns === null) {
this.GridColumns = new Array();
}
this.GridColumns.push(value);
}
setGridColumnsByArray({ value }: { value: string[] }) {
if (this.GridColumns === null) {
this.GridColumns = new Array();
}
this.GridColumns.push(...value);
}
}
スクリプト-③
Apiリクエストを発行するクラス。まずは、Viewクラスを受け取り、Null以外のパラメータでリクエストを生成し、$p.apiGetを実行。結果を返却します。
class PleasanterApiClient {
static async apiGetByScript({ id, view }: { id: number; view: View }) {
let res: any;
//Pleasnaterのメソッドをtypescriptが解釈できずエラーとなるため
//エラーチェックをスキップする
/*@ts-ignore*/
await $p.apiGet({
id: id,
data: PleasanterApiClient.setGetRequest({ view: view }),
done: function (data: any) {
res = data;
},
});
return res;
}
static setGetRequest({ view }: { view: View }) {
let data: any = {};
data.View = {};
if (view.Incomplete != null) {
data.View.Incomplete = view.Incomplete;
}
if (view.Own != null) {
data.View.Own = view.Own;
}
if (view.NearCompletionTime != null) {
data.View.NearCompletionTime = view.NearCompletionTime;
}
if (view.Delay != null) {
data.View.Delay = view.Delay;
}
if (view.Overdue != null) {
data.View.Overdue = view.Overdue;
}
if (view.Search != null) {
data.View.Search = view.Search;
}
if (view.ColumnFilterHash != null) {
let columnFilterHash: any = {};
view.ColumnFilterHash.forEach((value, key) => {
columnFilterHash[key] = value;
});
data.View.ColumnFilterHash = columnFilterHash;
}
if (view.ColumnFilterSearchTypes != null) {
let columnFilterSearchTypes: any = {};
view.ColumnFilterSearchTypes.forEach((value, key) => {
columnFilterSearchTypes[key] = value;
});
data.View.ColumnFilterHash = columnFilterSearchTypes;
}
if (view.ColumnFilterNegatives != null) {
data.View.ColumnFilterNegatives = view.ColumnFilterNegatives;
}
if (view.ColumnSorterHash != null) {
let columnSorterHash: any = {};
view.ColumnSorterHash.forEach((value, key) => {
columnSorterHash[key] = value;
});
data.View.ColumnSorterHash = columnSorterHash;
}
if (view.ApiDataType != null) {
data.View.ApiDataType = view.ApiDataType;
}
if (view.ApiColumnKeyDisplayType != null) {
data.View.ApiColumnKeyDisplayType = view.ApiColumnKeyDisplayType;
}
if (view.ApiColumnValueDisplayType != null) {
data.View.ApiColumnValueDisplayType =
view.ApiColumnValueDisplayType;
}
if (view.ApiColumnHash != null) {
data.View.ApiColumnHash = view.ApiColumnHash;
}
if (view.GridColumns != null) {
data.View.GridColumns = view.GridColumns;
}
if (view.MergeSessionViewFilters != null) {
data.View.MergeSessionViewFilters = view.MergeSessionViewFilters;
}
if (view.MergeSessionViewSorters != null) {
data.View.MergeSessionViewSorters = view.MergeSessionViewSorters;
}
return data;
}
}
スクリプト-④
Pleasanterのプロセスから呼び出される関数。
Viewクラスを生成し、必要なパラメータを設定。その後Apiを実行し結果をコンソールに表示します。
const getRecordsScript = async () => {
let siteId: number = 11114111;
let view = new View({});
let gridColumnHash = [
"IssueId",
"ClassA",
"ClassB",
"DateA",
"DateC",
"NumB",
"Status",
];
view.setGridColumnsByArray({ value: gridColumnHash });
let filterStatus = ["300"];
view.setColumnFilterHash({
key: "Status",
value: JSON.stringify(filterStatus),
});
view.ApiDataType = ApiDataType.KeyValues;
view.ApiColumnKeyDisplayType = ApiColumnKeyDisplayType.ColumnName;
let res = await PleasanterApiClient.apiGetByScript({
id: siteId,
view: view,
});
console.log(res);
};
補足
コンパイル後のjavascriptをPleasanterに設定します。
実装してみて
VSCodeで入力候補が出てくるのが、とてもコーディングしやすい。バグも見つけやすい、はず。
課題など
- この方式で他に使いまわせるか、いろいろ検証してみたい。(Update、Create、サーバスクリプト、外部からのAPIなどなど)
- リクエストパラメータなどは、とりあえずpublicとしたが、制限をかけたほうが良いかもしれない(整理できていない)。
- コンストラクタ回り、Apiリクエスト生成回りの処理をもっと簡略化できないか?