🥴
APIのレスポンスを変換する
はじめに
ワークフローを実現するApache Airflowを使用しており、使用している技術スタックなどを隠蔽するためにAirflowのAPIを薄くラップして独自のマイクロサービスを実装する必要がありました。実装している他のマイクロサービスはNode.jsで実装しており、主に返却するレスポンスのキーはキャメルケースでAPIのレスポンスを実装されています。AirflowのAPIのレスポンスとしてはスネークケースで返却されます。そのためlodashを用いてAPIのレスポンスをスネークケースからキャメルケースに変換してみたので、紹介しようと思います。
環境
- Node.js: v12.18.3
- lodash: 4.17.21
- Airflow: 2.0.1
Airflow API
今回は例として、AirflowのDAGを取得する/dags
を例に実装したいと思います。
{
"dags": [
{
"dag_id": "string",
"root_dag_id": "string",
"is_paused": true,
"is_subdag": true,
"fileloc": "string",
"file_token": "string",
"owners": [
"string"
],
"description": "string",
"schedule_interval": {
"__type": "string",
"days": 0,
"seconds": 0,
"microseconds": 0
},
"tags": [
{
"name": "string"
}
]
}
],
"total_entries": 0
}
期待するAPIの形としては不要な項目を削除して、スネークケースの部分をキャメルケースに変換していきます。また、dag_id
はworkflowId
などに変換しています。
{
[
{
"jobId": "string",
"rootJobId": "string",
"isPaused": true,
"isSubdag": true,
"fileloc": "string",
"fileToken": "string",
"owners": [
"string"
],
"description": "string",
"scheduleInterval": {
"__type": "string",
"days": 0,
"seconds": 0,
"microseconds": 0
},
"tags": [
{
"name": "string"
}
]
}
]
}
lodashを用いて変換するコード
utilities.js
'use strict';
cosnt _ = require('lodash');
module.exports = {
isPrimitive: val => _.includes(['string', 'number', 'boolean'], typeof val),
changeKeyFormat: (object, toStyle = _.snakeCase) => {
function _localize(obj) {
if (module.exports.isPrimitive(obj) || obj === null) return obj;
if (Array.isArray(obj)) return obj.map(item => _localize(item));
if (typeof obj === 'object') {
const convertedObj = {};
Object.keys(obj).forEach(key => {
if (module.exports.isPrimitivve(obj[key]) || obj[key] === null) {
if (key === '__type') return (convertedObj[key] = obj[key]);
else return (convertedObj[toStyle(key)] = obj[key]);
}
if (typeof obj[key] === 'object')
return (convertedObj[toStyle(key)] = _localize(obj[key]));
});
return convertedObj;
}
}
return _localize(object);
},
replaceKeys: object =>
_.mapKeys(object, (value, key) => {
if (key === 'dag_id') return 'workflowId';
else if (key === 'dag_run_id') return 'jobId';
else return key;
}),
formatResponse: object => {
function createChildren(listObject) {
return listObject.map(eachItem => module.exports.replaceKeys(eachItem));
}
let formatedObj;
if ('dags' in object) formatedObj = createChildren(object['dags']);
else if ('dag_runs' in object) formatedObj = createChildren(object['dag_runs']);
else if ('task_instances' in object) formatedObj = createChildren(object['task_instances']);
else formatedObj = module.exports.replaceKeys(object);
return module.exports.changeKeyFormat(formatedObj, _.camelCase);
},
formatRequest: object => {
const replaced = module.exports.replaceKeys(object);
return module.exports.changeKeyFormat(replaced);
}
}
- isPrimitive: keyに対応する値が、"string", "number", "boolean"であるかを判定する関数
- changeKeyFormat: オブジェクトのキー名を変換するための関数で、値が配列なのか、オブジェクトなのか等で処理を分けています。
- "string", "number", "boolean"の場合: そのまま値を返却する
- 配列の場合: 各要素ごとに、内部関数の
_localize
を呼び出す - オブジェクトの場合: キーを変換して別のオブジェクトに格納し、返却する
- replaceKeys: AirflowのAPIに存在する
dag_id
,dag_run_id
をworkflowId
,jobId
に変換するための関数 - formatResponse: AirflowのAPIレスポンスをキャメルケースに変換するための関数
- formatRequest: Airflowの値を更新するために、リクエストをスネークケースに変換するための関数
以上のコードをutilitis.js
として作成しておき、マイクロサービスの実装している部分で、使用します。
レスポンスの変換
sampleService.js
const utils = require('somewhere/utilities);
const convertedResponse = utils.formatResponse(data);
リクエストの変換
マイクロサービスから、Airflowのdag_runを更新して、有効・無効化する、ジョブ実行周期の変更などを実行したかったので、リクエストの形式をこちらはキャメルケースからスネークケースに変換します。
sample2Service.js
cosnt utils = require('somewhere/utilities');
const convertedRequest = utils.formatRequest(data);
おわりに
lodashを用いて、Javascriptのオブジェクトのキーの変換を実装できました。
今回使用しているのはlodashの実装されているメソッドの中で、ごくわずかなので便利なものがあれば、また実装して紹介しようと思います。
もし、コードの実装が綺麗でない部分であったり、もう少しいいやり方や違う方法があれば、ご指摘ください。
Discussion