2022-09-12
(C) Questetra, Inc. (MIT License)
2
https://support.questetra.com/bpmn-icons/service-task-stripe-customer-search/
https://support.questetra.com/ja/bpmn-icons/service-task-stripe-customer-search/
This item searches for customer objects on Stripe using a query.
この工程は、検索クエリに合致する Stripe 上の顧客オブジェクトを検索します。
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAvJJREFUWEfF
l19I01EUxz/XKZqWNbVS6cH+UfSUGQlCD0EQRCkoFDkDUzENi6IgkB4sKEgjDStFnVlzalC9+BBR
IEFBEeVbFP3R/lhRpLRpKHO78dtv021u+pszfnv87Zzv+dxzzj33XoHOP6E1fvleGe9KJF/CDiSZ
CDIAo8d/BMkggn4BfVE27rX0ir9atOcEKC6WqdEOTkuoAOK0iALjAponY7jY0SF+zOYzK0CJSVYI
uAQkaAwcaDYm4VS7VTSH8g8JUFokryOpnGdgfzdBk7lTHAmmFRSgxCS7BBxYkOAeEQnd7VZRGKg5
A2BBVz4z2oxM+AF4at60kCsP1JJQ6dsTUwBKtxscvI+g4bRyjzljWOfdHVMAZSZZL+G4VpVI7AQ0
tFnFCUXDDaAMGWciv8PY55HEV3zHDTaSlWHlBigzySIJlvmoxsbBxHj4ngIOtllFpxug1CTNQMls
MjnbIS0d7t6etmpshe9DcKEmfACg3WwVpSpAoXyFIDNQZuMm2J2rfk1Ng6XL4NlTSE4Blwt6LOB0
QtEhkBKGvsCdHli9FnLzoe8h9L8MASfpN3eJLd4MDPscLFMeTTcgOhocDjXNi5fAr5+wYqUa+NOA
ClF9FiYnVdtHD2DnLpiYgNhYqKmGr5+DQoyYrSLJCyCDmdQ1gtEIdpv6r7LKbgscroKr9ZC/Dzpa
VIBr9VBeBYYoiDKotkLA/V7/svnGMVuFYuLugaAAx05C+ipIWa4KShe8eA7ZOdB4GQr2TwMMfoSM
NfDuLazfALY/YLdD9y148zp4GXwBgpag9goYk2BkGJ48hj15MDoK8QnQUAtZ26DrJrRaVMBRO9Sd
h7wC2JylAp87A9+G5ipBiCbU0ttbs6HiqFqCkA0XTCigCefchqFgFsWrO2TggxZcP5vpbRjJIAo7
rMfBbxDpPoo941i/w0gB0P04ViB0vZB4m0nXK5kXQtdL6X/JRLjXcp9M6Pcw8ULo+jTznXK6PU7n
O2q1+v0D2nRCMMki7aoAAAAASUVORK5CYII=
{
configs.put('conf_Auth', 'Stripe');
configs.put('conf_Query', query);
// 顧客の ID 一覧を保存する文字型データ項目(複数行)を準備し、設定
const idDef = engine.createDataDefinition('顧客 ID の一覧', 1, 'q_customerIds', 'STRING_TEXTAREA');
engine.setData(idDef, '事前文字列');
configs.putObject('conf_CustomerIds', idDef);
// 顧客詳細ページの URL 一覧を保存する文字型データ項目(複数行)を準備し、設定
const urlDef = engine.createDataDefinition('顧客 URL の一覧', 2, 'q_customerUrls', 'STRING_TEXTAREA');
engine.setData(urlDef, '事前文字列');
configs.putObject('conf_CustomerUrls', urlDef);
// 顧客名の一覧を保存する文字型データ項目(複数行)を準備し、設定
const nameDef = engine.createDataDefinition('顧客名の一覧', 3, 'q_customerNames', 'STRING_TEXTAREA');
engine.setData(nameDef, '事前文字列');
configs.putObject('conf_CustomerNames', nameDef);
// 顧客メールアドレスの一覧を保存する文字型データ項目(複数行)を準備し、設定
const emailDef = engine.createDataDefinition('顧客メールアドレスの一覧', 4, 'q_customerEmails', 'STRING_TEXTAREA');
engine.setData(emailDef, '事前文字列');
configs.putObject('conf_CustomerEmails', emailDef);
return {idDef, urlDef, nameDef, emailDef};
}
const SAMPLE_QUERY_1 = 'name~"テスト顧客"';
const SAMPLE_QUERY_2 = 'created>946652400\nAND metadata["key"]:"value"'; // 複数行
/**
* 保存先データ項目がひとつも設定されていない
*/
test('Search query is blank', () => {
prepareConfigs(SAMPLE_QUERY_1);
configs.put('conf_CustomerIds', '');
configs.put('conf_CustomerUrls', '');
configs.put('conf_CustomerNames', '');
configs.put('conf_CustomerEmails', '');
expect(execute).toThrow('No data item to save the search result is set.');
});
/**
* 保存先データ項目が重複 - ID と URL
*/
test('Same data item is set multiple times - ID and URL', () => {
const {idDef} = prepareConfigs(SAMPLE_QUERY_1);
configs.putObject('conf_CustomerUrls', idDef);
expect(execute).toThrow('Same data item is set multiple times.');
});
/**
* 保存先データ項目が重複 - URL と名前
*/
test('Same data item is set multiple times - URL and Name', () => {
const {urlDef} = prepareConfigs(SAMPLE_QUERY_1);
configs.putObject('conf_CustomerNames', urlDef);
expect(execute).toThrow('Same data item is set multiple times.');
});
/**
* 保存先データ項目が重複 - 名前とメールアドレス
*/
test('Same data item is set multiple times - Name and Email', () => {
const {nameDef} = prepareConfigs(SAMPLE_QUERY_1);
configs.putObject('conf_CustomerEmails', nameDef);
expect(execute).toThrow('Same data item is set multiple times.');
});
/**
* 保存先データ項目が重複 - メールアドレスと ID
*/
test('Same data item is set multiple times - Email and ID', () => {
const {emailDef} = prepareConfigs(SAMPLE_QUERY_1);
configs.putObject('conf_CustomerIds', emailDef);
expect(execute).toThrow('Same data item is set multiple times.');
});
/** 1 リクエストで取得できる顧客数 */
const MAX_CUSTOMERS_PER_REQ = 100;
/**
* 顧客を検索する API リクエストのテスト
* @param {Object} request
* @param request.url
* @param request.method
* @param request.headers
* @param query
* @param page
*/
const assertRequest = ({url, method, headers}, query, page) => {
const encodedQuery = encodeURIComponent(query).replace(/~/g, '%7E').replace(/%20/g, '+');
let expectedQueryStr = `query=${encodedQuery}&limit=${MAX_CUSTOMERS_PER_REQ}`;
if (page !== null) {
expectedQueryStr += `&page=${page}`;
}
expect(url).toEqual(`https://api.stripe.com/v1/customers/search?${expectedQueryStr}`);
expect(method).toEqual('GET');
expect(headers.get('Stripe-Version')).toEqual(STRIPE_VERSION);
};
/**
* 初回の顧客を検索する HTTP リクエストで失敗
*/
test('Fail to search customers', () => {
prepareConfigs(SAMPLE_QUERY_1);
httpClient.setRequestHandler((request) => {
assertRequest(request, SAMPLE_QUERY_1, null);
return httpClient.createHttpResponse(400, 'application/json', '{}');
});
expect(execute).toThrow('Failed to search customers. status: 400');
});
/**
* 検索結果が 0 件
*/
test('No customers found', () => {
prepareConfigs(SAMPLE_QUERY_1);
httpClient.setRequestHandler((request) => {
assertRequest(request, SAMPLE_QUERY_1, null);
return httpClient.createHttpResponse(200, 'application/json', '{"data": []}');
});
expect(execute).toThrow('No customers found.');
});
/**
* あたえられた数の顧客オブジェクトの配列を返す
* @param number
* @return customers
*/
const prepareCustomers = (number) => {
const customers = [];
for (let i = 0; i < number; i++) {
const customer = {
'id': `cus_${i+1}`,
'name': `テスト顧客 ${i+1}`,
'email': `test${i+1}@example.com`
};
customers.push(customer);
}
return customers;
};
/**
* 顧客を検索する API リクエストのレスポンスボディを返す
* @param hasMore
* @param customers
* @param nextPage
* @return response
*/
const searchResponse = (hasMore, customers, nextPage) => {
const responseObj = {
'has_more': hasMore,
'data': customers
};
if (nextPage !== null) {
responseObj['next_page'] = nextPage;
}
return JSON.stringify(responseObj);
};
/**
* 成功 - 検索結果が 1 件
*/
test('Success - 1 customer found', () => {
const {idDef, urlDef, nameDef, emailDef} = prepareConfigs(SAMPLE_QUERY_1);
const customers = prepareCustomers(1);
httpClient.setRequestHandler((request) => {
assertRequest(request, SAMPLE_QUERY_1, null);
return httpClient.createHttpResponse(200, 'application/json', searchResponse(false, customers, null));
});
execute();
// 文字型データ項目の値をチェック
expect(engine.findData(idDef)).toEqual('cus_1');
expect(engine.findData(urlDef)).toEqual('https://dashboard.stripe.com/customers/cus_1');
expect(engine.findData(nameDef)).toEqual('テスト顧客 1');
expect(engine.findData(emailDef)).toEqual('test1@example.com');
});
/**
* 成功 - 検索結果が 1 件、単一行データ項目に保存
*/
test('Success - 1 customer found, saved to single-line data items', () => {
prepareConfigs(SAMPLE_QUERY_2);
// 顧客の ID 一覧を保存する文字型データ項目(単一行)を準備し、設定
const idDef = engine.createDataDefinition('顧客 ID の一覧', 5, 'q_customerId', 'STRING_TEXTFIELD');
engine.setData(idDef, '事前文字列');
configs.putObject('conf_CustomerIds', idDef);
// 顧客詳細ページの URL 一覧を保存する文字型データ項目(単一行)を準備し、設定
const urlDef = engine.createDataDefinition('顧客 URL の一覧', 6, 'q_customerUrl', 'STRING_TEXTFIELD');
engine.setData(urlDef, '事前文字列');
configs.putObject('conf_CustomerUrls', urlDef);
// 顧客名の一覧を保存する文字型データ項目(単一行)を準備し、設定
const nameDef = engine.createDataDefinition('顧客名の一覧', 7, 'q_customerName', 'STRING_TEXTFIELD');
engine.setData(nameDef, '事前文字列');
configs.putObject('conf_CustomerNames', nameDef);
// 顧客メールアドレスの一覧を保存する文字型データ項目(単一行)を準備し、設定
const emailDef = engine.createDataDefinition('顧客メールアドレスの一覧', 8, 'q_customerEmail', 'STRING_TEXTFIELD');
engine.setData(emailDef, '事前文字列');
configs.putObject('conf_CustomerEmails', emailDef);
const customers = prepareCustomers(1);
httpClient.setRequestHandler((request) => {
assertRequest(request, SAMPLE_QUERY_2, null);
return httpClient.createHttpResponse(200, 'application/json', searchResponse(false, customers, null));
});
execute();
// 文字型データ項目の値をチェック
expect(engine.findData(idDef)).toEqual('cus_1');
expect(engine.findData(urlDef)).toEqual('https://dashboard.stripe.com/customers/cus_1');
expect(engine.findData(nameDef)).toEqual('テスト顧客 1');
expect(engine.findData(emailDef)).toEqual('test1@example.com');
});
const REQ_LIMIT = httpClient.getRequestingLimit();
/**
* 成功 - 検索結果が上限件
*/
test('Success - max number of customers found', () => {
const {idDef, urlDef, nameDef, emailDef} = prepareConfigs(SAMPLE_QUERY_2);
const customerNum = MAX_CUSTOMERS_PER_REQ * REQ_LIMIT;
const customers = prepareCustomers(customerNum);
let reqCount = 0;
httpClient.setRequestHandler((request) => {
const start = MAX_CUSTOMERS_PER_REQ * reqCount;
const end = start + MAX_CUSTOMERS_PER_REQ;
if (reqCount === 0) {
assertRequest(request, SAMPLE_QUERY_2, null);
reqCount++;
const response = searchResponse(true, customers.slice(start, end), `page_${reqCount}`);
return httpClient.createHttpResponse(200, 'application/json', response);
}
assertRequest(request, SAMPLE_QUERY_2, `page_${reqCount}`);
if (reqCount < REQ_LIMIT - 1) {
reqCount++;
const response = searchResponse(true, customers.slice(start, end), `page_${reqCount}`);
return httpClient.createHttpResponse(200, 'application/json', response);
}
const response = searchResponse(false, customers.slice(start, end), null);
return httpClient.createHttpResponse(200, 'application/json', response);
});
execute();
// 文字型データ項目の値をチェック
const ids = engine.findData(idDef).split('\n');
const urls = engine.findData(urlDef).split('\n');
const names = engine.findData(nameDef).split('\n');
const emails = engine.findData(emailDef).split('\n');
expect(ids.length).toEqual(customerNum);
expect(urls.length).toEqual(customerNum);
expect(names.length).toEqual(customerNum);
expect(emails.length).toEqual(customerNum);
for (let i = 0; i < customerNum; i++) {
expect(ids[i]).toEqual(`cus_${i+1}`);
expect(urls[i]).toEqual(`https://dashboard.stripe.com/customers/cus_${i+1}`);
expect(names[i]).toEqual(`テスト顧客 ${i+1}`);
expect(emails[i]).toEqual(`test${i+1}@example.com`);
}
});
/**
* 検索結果が多すぎて HTTP リクエスト数の上限を超える
*/
test('Number of HTTP requests exceeds the limit', () => {
const {idDef, urlDef, nameDef, emailDef} = prepareConfigs(SAMPLE_QUERY_1);
const customerNum = MAX_CUSTOMERS_PER_REQ * REQ_LIMIT;
const customers = prepareCustomers(customerNum);
let reqCount = 0;
httpClient.setRequestHandler((request) => {
const start = MAX_CUSTOMERS_PER_REQ * reqCount;
const end = start + MAX_CUSTOMERS_PER_REQ;
if (reqCount === 0) {
assertRequest(request, SAMPLE_QUERY_1, null);
reqCount++;
const response = searchResponse(true, customers.slice(start, end), `page_${reqCount}`);
return httpClient.createHttpResponse(200, 'application/json', response);
}
assertRequest(request, SAMPLE_QUERY_1, `page_${reqCount}`);
reqCount++;
// 最後のリクエストでも hasMore が true
const response = searchResponse(true, customers.slice(start, end), `page_${reqCount}`);
return httpClient.createHttpResponse(200, 'application/json', response);
});
expect(execute).toThrow(`HTTP requests must be up to ${REQ_LIMIT}.`); // スクリプトエンジンのエラー
});
/**
* 検索結果が複数件で、保存先データ項目が単一行 - ID
*/
test('Cannot save multiple results to single-line data item - ID', () => {
prepareConfigs(SAMPLE_QUERY_1);
// 顧客の ID 一覧を保存する文字型データ項目(単一行)を準備し、設定
const idDef = engine.createDataDefinition('顧客 ID の一覧', 5, 'q_customerId', 'STRING_TEXTFIELD');
configs.putObject('conf_CustomerIds', idDef);
const customers = prepareCustomers(2);
httpClient.setRequestHandler((request) => {
assertRequest(request, SAMPLE_QUERY_1, null);
return httpClient.createHttpResponse(200, 'application/json', searchResponse(false, customers, null));
});
expect(execute).toThrow('More than one customers were found while the data item to save the result is Single-Line.');
});
/**
* 検索結果が複数件で、保存先データ項目が単一行 - URL
*/
test('Cannot save multiple results to single-line data item - URL', () => {
prepareConfigs(SAMPLE_QUERY_2);
// 顧客詳細ページの URL 一覧を保存する文字型データ項目(単一行)を準備し、設定
const urlDef = engine.createDataDefinition('顧客 URL の一覧', 6, 'q_customerUrl', 'STRING_TEXTFIELD');
configs.putObject('conf_CustomerUrls', urlDef);
const customersNum = MAX_CUSTOMERS_PER_REQ + 1;
const customers = prepareCustomers(customersNum);
httpClient.setRequestHandler((request) => { // リクエストは 1 回のみ
assertRequest(request, SAMPLE_QUERY_2, null);
const response = searchResponse(true, customers.slice(0, MAX_CUSTOMERS_PER_REQ), 'next_page')
return httpClient.createHttpResponse(200, 'application/json', response);
});
expect(execute).toThrow('More than one customers were found while the data item to save the result is Single-Line.');
});
/**
* 検索結果が複数件で、保存先データ項目が単一行 - 名前
*/
test('Cannot save multiple results to single-line data item - Name', () => {
prepareConfigs(SAMPLE_QUERY_1);
// 顧客名の一覧を保存する文字型データ項目(単一行)を準備し、設定
const nameDef = engine.createDataDefinition('顧客名の一覧', 7, 'q_customerName', 'STRING_TEXTFIELD');
configs.putObject('conf_CustomerNames', nameDef);
const customers = prepareCustomers(2);
httpClient.setRequestHandler((request) => {
assertRequest(request, SAMPLE_QUERY_1, null);
return httpClient.createHttpResponse(200, 'application/json', searchResponse(false, customers, null));
});
expect(execute).toThrow('More than one customers were found while the data item to save the result is Single-Line.');
});
/**
* 検索結果が複数件で、保存先データ項目が単一行 - メールアドレス
*/
test('Cannot save multiple results to single-line data item - Email', () => {
prepareConfigs(SAMPLE_QUERY_2);
// 顧客メールアドレスの一覧を保存する文字型データ項目(単一行)を準備し、設定
const emailDef = engine.createDataDefinition('顧客メールアドレスの一覧', 8, 'q_customerEmail', 'STRING_TEXTFIELD');
configs.putObject('conf_CustomerEmails', emailDef);
const customers = prepareCustomers(2);
httpClient.setRequestHandler((request) => {
assertRequest(request, SAMPLE_QUERY_2, null);
return httpClient.createHttpResponse(200, 'application/json', searchResponse(false, customers, null));
});
expect(execute).toThrow('More than one customers were found while the data item to save the result is Single-Line.');
});
/**
* 成功 - ID とメールアドレスの保存先データ項目が未設定
*/
test('Success - Data item to save IDs and Emails are not set', () => {
const {idDef, urlDef, nameDef, emailDef} = prepareConfigs(SAMPLE_QUERY_1);
configs.put('conf_CustomerIds', '');
configs.put('conf_CustomerEmails', '');
const customerNum = MAX_CUSTOMERS_PER_REQ;
const customers = prepareCustomers(customerNum);
httpClient.setRequestHandler((request) => {
assertRequest(request, SAMPLE_QUERY_1, null);
return httpClient.createHttpResponse(200, 'application/json', searchResponse(false, customers, null));
});
execute();
// 文字型データ項目の値をチェック
expect(engine.findData(idDef)).toEqual('事前文字列');
expect(engine.findData(emailDef)).toEqual('事前文字列');
const urls = engine.findData(urlDef).split('\n');
const names = engine.findData(nameDef).split('\n');
expect(urls.length).toEqual(customerNum);
expect(names.length).toEqual(customerNum);
for (let i = 0; i < customerNum; i++) {
expect(urls[i]).toEqual(`https://dashboard.stripe.com/customers/cus_${i+1}`);
expect(names[i]).toEqual(`テスト顧客 ${i+1}`);
}
});
/**
* 成功 - URL と名前の保存先データ項目が未設定
*/
test('Success - Data item to save URLs and Names are not set', () => {
const {idDef, urlDef, nameDef, emailDef} = prepareConfigs(SAMPLE_QUERY_2);
configs.put('conf_CustomerUrls', '');
configs.put('conf_CustomerNames', '');
const customerNum = 10;
const customers = prepareCustomers(customerNum);
httpClient.setRequestHandler((request) => {
assertRequest(request, SAMPLE_QUERY_2, null);
return httpClient.createHttpResponse(200, 'application/json', searchResponse(false, customers, null));
});
execute();
// 文字型データ項目の値をチェック
expect(engine.findData(urlDef)).toEqual('事前文字列');
expect(engine.findData(nameDef)).toEqual('事前文字列');
const ids = engine.findData(idDef).split('\n');
const emails = engine.findData(emailDef).split('\n');
expect(ids.length).toEqual(customerNum);
expect(emails.length).toEqual(customerNum);
for (let i = 0; i < customerNum; i++) {
expect(ids[i]).toEqual(`cus_${i+1}`);
expect(emails[i]).toEqual(`test${i+1}@example.com`);
}
});
/**
* 成功 - 名前やメールアドレスが未設定の顧客がいる
*/
test('Success - Some customers do not have their name or email address', () => {
const {idDef, urlDef, nameDef, emailDef} = prepareConfigs(SAMPLE_QUERY_1);
const customerNum = 5;
const customers = prepareCustomers(customerNum);
customers[2].name = null;
customers[3].email = null;
httpClient.setRequestHandler((request) => {
assertRequest(request, SAMPLE_QUERY_1, null);
return httpClient.createHttpResponse(200, 'application/json', searchResponse(false, customers, null));
});
execute();
// 文字型データ項目の値をチェック
const ids = engine.findData(idDef).split('\n');
const urls = engine.findData(urlDef).split('\n');
const names = engine.findData(nameDef).split('\n');
const emails = engine.findData(emailDef).split('\n');
expect(ids.length).toEqual(customerNum);
expect(urls.length).toEqual(customerNum);
expect(names.length).toEqual(customerNum);
expect(emails.length).toEqual(customerNum);
for (let i = 0; i < customerNum; i++) {
expect(ids[i]).toEqual(`cus_${i+1}`);
expect(urls[i]).toEqual(`https://dashboard.stripe.com/customers/cus_${i+1}`);
if (i === 2) {
expect(names[i]).toEqual(''); // 空行
} else {
expect(names[i]).toEqual(`テスト顧客 ${i+1}`);
}
if (i === 3) {
expect(emails[i]).toEqual(''); // 空行
} else {
expect(emails[i]).toEqual(`test${i+1}@example.com`);
}
}
});
/**
* 検索クエリが空 - 全件取得
*/
test('Search query is blank - get all customers', () => {
const {idDef, urlDef, nameDef, emailDef} = prepareConfigs('');
const customerNum = MAX_CUSTOMERS_PER_REQ * REQ_LIMIT;
const customers = prepareCustomers(customerNum);
let reqCount = 0;
const query = 'created>0';
httpClient.setRequestHandler((request) => {
const start = MAX_CUSTOMERS_PER_REQ * reqCount;
const end = start + MAX_CUSTOMERS_PER_REQ;
if (reqCount === 0) {
assertRequest(request, query, null);
reqCount++;
const response = searchResponse(true, customers.slice(start, end), `page_${reqCount}`);
return httpClient.createHttpResponse(200, 'application/json', response);
}
assertRequest(request, query, `page_${reqCount}`);
if (reqCount < REQ_LIMIT - 1) {
reqCount++;
const response = searchResponse(true, customers.slice(start, end), `page_${reqCount}`);
return httpClient.createHttpResponse(200, 'application/json', response);
}
const response = searchResponse(false, customers.slice(start, end), null);
return httpClient.createHttpResponse(200, 'application/json', response);
});
execute();
// 文字型データ項目の値をチェック
const ids = engine.findData(idDef).split('\n');
const urls = engine.findData(urlDef).split('\n');
const names = engine.findData(nameDef).split('\n');
const emails = engine.findData(emailDef).split('\n');
expect(ids.length).toEqual(customerNum);
expect(urls.length).toEqual(customerNum);
expect(names.length).toEqual(customerNum);
expect(emails.length).toEqual(customerNum);
for (let i = 0; i < customerNum; i++) {
expect(ids[i]).toEqual(`cus_${i+1}`);
expect(urls[i]).toEqual(`https://dashboard.stripe.com/customers/cus_${i+1}`);
expect(names[i]).toEqual(`テスト顧客 ${i+1}`);
expect(emails[i]).toEqual(`test${i+1}@example.com`);
}
});
]]>