「DataMapping」コンポーネントとプラグイン 新機能「更新対象」
This commit is contained in:
@@ -96,7 +96,7 @@ import FieldSelect from '../FieldSelect.vue';
|
|||||||
import IAppFields from './AppFieldSelect.vue';
|
import IAppFields from './AppFieldSelect.vue';
|
||||||
import { api } from 'boot/axios';
|
import { api } from 'boot/axios';
|
||||||
|
|
||||||
type Props = {
|
type ContextProps = {
|
||||||
props?: {
|
props?: {
|
||||||
name: string;
|
name: string;
|
||||||
modelValue?: {
|
modelValue?: {
|
||||||
@@ -108,12 +108,12 @@ type Props = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
type Value = {
|
type CurrentModelValueType = {
|
||||||
data: ValueType[];
|
data: MappingValueType[];
|
||||||
createWithNull: boolean;
|
createWithNull: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ValueType = {
|
type MappingValueType = {
|
||||||
id: string;
|
id: string;
|
||||||
from: object;
|
from: object;
|
||||||
to: typeof IAppFields & {
|
to: typeof IAppFields & {
|
||||||
@@ -122,6 +122,7 @@ type ValueType = {
|
|||||||
isKey: boolean;
|
isKey: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const blackListLabelName = ['レコード番号','作業者','更新者','更新日時','作成日時','作成者']
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'DataMapping',
|
name: 'DataMapping',
|
||||||
@@ -134,7 +135,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
context: {
|
context: {
|
||||||
type: Array<Props>,
|
type: Array<ContextProps>,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
displayName: {
|
displayName: {
|
||||||
@@ -146,7 +147,7 @@ export default defineComponent({
|
|||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: Object as () => Value,
|
type: Object as () => CurrentModelValueType,
|
||||||
},
|
},
|
||||||
placeholder: {
|
placeholder: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -166,27 +167,26 @@ export default defineComponent({
|
|||||||
const sourceAppId = computed(() => sourceApp.value?.id);
|
const sourceAppId = computed(() => sourceApp.value?.id);
|
||||||
|
|
||||||
const closeDg = () => {
|
const closeDg = () => {
|
||||||
emit('update:modelValue', mappingProps.value
|
emit('update:modelValue', { data: mappingProps.value, createWithNull: createWithNull.value });
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const closeToDg = () => {
|
const closeToDg = () => {
|
||||||
emit('update:modelValue', mappingProps.value
|
emit('update:modelValue', { data: mappingProps.value, createWithNull: createWithNull.value });
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mappingProps = computed(() => props.modelValue?.data ?? []);
|
const mappingProps = computed(() => props.modelValue?.data ?? []);
|
||||||
|
|
||||||
const createWithNull = computed(() => props.modelValue?.createWithNull)
|
const createWithNull = ref(props.modelValue?.createWithNull ?? false)
|
||||||
|
|
||||||
watch(() => sourceAppId.value, async (newId, oldId) => {
|
watch(() => sourceAppId.value, async (newId, oldId) => {
|
||||||
if (!newId) return;
|
if (!newId) return;
|
||||||
const a = await api.get('api/v1/appfields', {
|
const ktAppFields = await api.get('api/v1/appfields', {
|
||||||
params: {
|
params: {
|
||||||
app: newId
|
app: newId
|
||||||
}
|
}
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
return Object.values(res.data.properties)
|
return Object.values(res.data.properties)
|
||||||
|
.filter(f => !blackListLabelName.find(label => f.label === label))
|
||||||
.map(f => ({ name: f.label, objectType: 'field', ...f }))
|
.map(f => ({ name: f.label, objectType: 'field', ...f }))
|
||||||
.map(f => {
|
.map(f => {
|
||||||
return {
|
return {
|
||||||
@@ -197,7 +197,7 @@ export default defineComponent({
|
|||||||
fields: [f],
|
fields: [f],
|
||||||
isDialogVisible: false
|
isDialogVisible: false
|
||||||
},
|
},
|
||||||
isKey: true
|
isKey: false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -206,21 +206,18 @@ export default defineComponent({
|
|||||||
const createWithNull = props.modelValue?.createWithNull ?? false
|
const createWithNull = props.modelValue?.createWithNull ?? false
|
||||||
|
|
||||||
if (modelValueData.length === 0 || newId !== oldId) {
|
if (modelValueData.length === 0 || newId !== oldId) {
|
||||||
// emit('update:modelValue', a);
|
emit('update:modelValue', { data: ktAppFields, createWithNull: createWithNull });
|
||||||
emit('update:modelValue', { data: a, createWithNull: createWithNull });
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const modelValueFieldNames = modelValueData.map(item => item.to.fields[0].name);
|
const modelValueFieldNames = modelValueData.map(item => item.to.fields[0].name);
|
||||||
|
|
||||||
const newFields = a.filter(field => !modelValueFieldNames.includes(field.to.fields[0].name));
|
const newFields = ktAppFields.filter(field => !modelValueFieldNames.includes(field.to.fields[0].name));
|
||||||
|
|
||||||
const updatedModelValue = [...modelValueData, ...newFields];
|
const updatedModelValueData = [...modelValueData, ...newFields];
|
||||||
|
emit('update:modelValue', { data: updatedModelValueData, createWithNull: createWithNull });
|
||||||
emit('update:modelValue', { data: updatedModelValue, createWithNull: createWithNull });
|
|
||||||
// emit('update:modelValue', updatedModelValue);
|
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log(mappingProps.value);
|
console.log(createWithNull.value);
|
||||||
|
|
||||||
// const deleteMappingObject = (index: number) => mappingProps.length === 1
|
// const deleteMappingObject = (index: number) => mappingProps.length === 1
|
||||||
// ? mappingProps.splice(0, mappingProps.length, defaultMappingProp())
|
// ? mappingProps.splice(0, mappingProps.length, defaultMappingProp())
|
||||||
@@ -243,7 +240,7 @@ export default defineComponent({
|
|||||||
//集計処理方法
|
//集計処理方法
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
emit('update:modelValue', mappingProps.value);
|
emit('update:modelValue', { data: mappingProps.value, createWithNull: createWithNull.value });
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
uuidv4,
|
uuidv4,
|
||||||
|
|||||||
@@ -6,54 +6,57 @@ import {
|
|||||||
IContext,
|
IContext,
|
||||||
} from "../types/ActionTypes";
|
} from "../types/ActionTypes";
|
||||||
import { actionAddins } from ".";
|
import { actionAddins } from ".";
|
||||||
|
import { KintoneRestAPIClient } from "@kintone/rest-api-client";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
displayName: string;
|
displayName: string;
|
||||||
sources: Sources;
|
sources: Sources;
|
||||||
dataMapping: DataMapping[];
|
dataMapping: DataMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DataMapping {
|
interface DataMapping {
|
||||||
|
data: Mapping[];
|
||||||
|
createWithNull: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Mapping {
|
||||||
id: string;
|
id: string;
|
||||||
from: From;
|
from: From;
|
||||||
to: To;
|
to: To;
|
||||||
|
isKey: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface To {
|
interface To {
|
||||||
app: App;
|
app: App;
|
||||||
fields: Field[];
|
fields: {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
code: string;
|
||||||
|
label: string;
|
||||||
|
noLabel: boolean;
|
||||||
|
required: boolean;
|
||||||
|
minLength: string;
|
||||||
|
maxLength: string;
|
||||||
|
expression: string;
|
||||||
|
hideExpression: boolean;
|
||||||
|
unique: boolean;
|
||||||
|
defaultValue: string;
|
||||||
|
}[];
|
||||||
isDialogVisible: boolean;
|
isDialogVisible: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Field {
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
code: string;
|
|
||||||
label: string;
|
|
||||||
noLabel: boolean;
|
|
||||||
required: boolean;
|
|
||||||
minLength: string;
|
|
||||||
maxLength: string;
|
|
||||||
expression: string;
|
|
||||||
hideExpression: boolean;
|
|
||||||
unique: boolean;
|
|
||||||
defaultValue: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface From {
|
interface From {
|
||||||
sharedText: string;
|
sharedText: string;
|
||||||
_t: string;
|
_t: string;
|
||||||
id: string;
|
id: string;
|
||||||
objectType: string;
|
objectType: string;
|
||||||
name: Name;
|
name: {
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
actionName: string;
|
actionName: string;
|
||||||
displayName: string;
|
displayName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Name {
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Sources {
|
interface Sources {
|
||||||
app: App;
|
app: App;
|
||||||
}
|
}
|
||||||
@@ -84,32 +87,37 @@ export class DataMappingAction implements IAction {
|
|||||||
this.actionProps = prop.actionProps;
|
this.actionProps = prop.actionProps;
|
||||||
this.dataMappingProps = prop.ActionValue as Props;
|
this.dataMappingProps = prop.ActionValue as Props;
|
||||||
console.log(prop.ActionValue);
|
console.log(prop.ActionValue);
|
||||||
|
|
||||||
// this.initTypedActionProps();
|
|
||||||
let result = {
|
let result = {
|
||||||
canNext: true,
|
canNext: true,
|
||||||
result: "",
|
result: "",
|
||||||
} as IActionResult;
|
} as IActionResult;
|
||||||
try {
|
try {
|
||||||
const record = this.dataMappingProps.dataMapping
|
// createWithNull が有効な場合は、4 番目のパラメーターを true にして doUpdate 関数ブランチを実行します。
|
||||||
.filter(
|
if (this.dataMappingProps.dataMapping.createWithNull === true) {
|
||||||
(item) =>
|
await doUpdate(
|
||||||
item.from.objectType === "variable" &&
|
this.dataMappingProps.dataMapping.data,
|
||||||
item.from.name.name &&
|
this.dataMappingProps.sources.app.id,
|
||||||
item.to.app &&
|
context,
|
||||||
item.to.fields &&
|
true // キーがない場合、またはキーでターゲットが見つからない場合に、マッピング条件によって新しいレコードを作成するかどうかを決定するために使用されます。
|
||||||
item.to.fields.length > 0
|
);
|
||||||
)
|
} else if (
|
||||||
.reduce((accumulator, item) => {
|
// キーがないと更新対象を取得できないため、この時点でのみ更新が行われます。 doUpdate 関数の 4 番目のパラメーターは false です。
|
||||||
return {...accumulator, [item.to.fields[0].code]: {
|
this.dataMappingProps.dataMapping.data
|
||||||
value: getValueByPath(context.variables, item.from.name.name),
|
.map((m) => m.isKey)
|
||||||
}};
|
.find((isKey) => isKey === true)
|
||||||
}, {});
|
) {
|
||||||
if (record && Object.keys(record).length > 0) {
|
await doUpdate(
|
||||||
await kintone.api(kintone.api.url("/k/v1/record.json", true), "POST", {
|
this.dataMappingProps.dataMapping.data,
|
||||||
app: this.dataMappingProps.sources.app.id,
|
this.dataMappingProps.sources.app.id,
|
||||||
record: record,
|
context,
|
||||||
});
|
false
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await doCreate(
|
||||||
|
this.dataMappingProps.dataMapping.data,
|
||||||
|
this.dataMappingProps.sources.app.id,
|
||||||
|
context
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("DataMappingAction error", error);
|
console.error("DataMappingAction error", error);
|
||||||
@@ -127,40 +135,136 @@ export class DataMappingAction implements IAction {
|
|||||||
|
|
||||||
new DataMappingAction();
|
new DataMappingAction();
|
||||||
|
|
||||||
const getValueByPath = (obj: any, path: string) => {
|
const getContextVarByPath = (obj: any, path: string) => {
|
||||||
return path.split(".").reduce((o, k) => (o || {})[k], obj);
|
return path.split(".").reduce((o, k) => (o || {})[k], obj);
|
||||||
};
|
};
|
||||||
|
|
||||||
type Resp = { records: RespRecordType[] };
|
interface UpdateRecord {
|
||||||
|
id: string;
|
||||||
type RespRecordType = {
|
record: {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
type: string;
|
value: any;
|
||||||
value: any;
|
};
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
type Result = {
|
const client = new KintoneRestAPIClient();
|
||||||
type: string;
|
|
||||||
value: any[];
|
|
||||||
};
|
|
||||||
|
|
||||||
const selectData = async (appid: string, field: string): Promise<Result> => {
|
const doUpdate = async (
|
||||||
return kintone
|
mappingData: Mapping[],
|
||||||
.api(kintone.api.url("/k/v1/records", true), "GET", {
|
appId: string,
|
||||||
app: appid ?? kintone.app.getId(),
|
context: any,
|
||||||
fields: [field],
|
needCreate: boolean
|
||||||
})
|
) => {
|
||||||
.then((resp: Resp) => {
|
const targetField = await findUpdateField(mappingData, appId, context);
|
||||||
const result: Result = { type: "", value: [] };
|
console.log(targetField);
|
||||||
resp.records.forEach((element) => {
|
if (targetField.records.length === 0 && needCreate) {
|
||||||
for (const [key, value] of Object.entries(element)) {
|
await doCreate(mappingData, appId, context);
|
||||||
if (result.type === "") {
|
} else {
|
||||||
result.type = value.type;
|
// マッピングデータを単純なオブジェクトに処理し、ソース値が変数の場合は変数を置き換えます。
|
||||||
}
|
const mappingRules = mappingData
|
||||||
result.value.push(value.value);
|
.filter((m) => Object.keys(m.from).length > 0)
|
||||||
|
.map((m) => {
|
||||||
|
if (m.from.objectType === "variable") {
|
||||||
|
return {
|
||||||
|
value: getContextVarByPath(context.variables, m.from.name.name),
|
||||||
|
code: m.to.fields[0].code,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
value: m.from.sharedText,
|
||||||
|
code: m.to.fields[0].code,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return result;
|
|
||||||
|
const updateRecords: UpdateRecord[] = targetField.records.map(
|
||||||
|
(targetRecord) => {
|
||||||
|
const updateRecord: UpdateRecord["record"] = {};
|
||||||
|
|
||||||
|
// マッピング内のルールにヒットしたフィールドのみが更新されます。
|
||||||
|
for (const mapping of mappingRules) {
|
||||||
|
if (targetRecord[mapping.code]) {
|
||||||
|
updateRecord[mapping.code] = {
|
||||||
|
value: mapping.value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: targetRecord.$id.value as string,
|
||||||
|
record: updateRecord,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(updateRecords);
|
||||||
|
|
||||||
|
await client.record.updateRecords({
|
||||||
|
app: appId,
|
||||||
|
records: updateRecords,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const findUpdateField = async (
|
||||||
|
mappingData: Mapping[],
|
||||||
|
appId: string,
|
||||||
|
context: any
|
||||||
|
) => {
|
||||||
|
const queryStr = mappingData
|
||||||
|
.filter((m) => m.to.app && m.to.fields && m.to.fields.length > 0 && m.isKey)
|
||||||
|
.map((m) => {
|
||||||
|
if (m.from.objectType === "variable") {
|
||||||
|
return `${m.to.fields[0].code} = "${getContextVarByPath(
|
||||||
|
context.variables,
|
||||||
|
m.from.name.name
|
||||||
|
)}"`;
|
||||||
|
} else {
|
||||||
|
return `${m.to.fields[0].code}=${m.from.sharedText}`;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.join("&");
|
||||||
|
// 検索条件が空の場合は全レコードを返すため、検索対象が見つからない場合は検索は行われません。
|
||||||
|
if (queryStr.length === 0) {
|
||||||
|
return {
|
||||||
|
records: [],
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return await client.record.getRecords({
|
||||||
|
app: appId,
|
||||||
|
// query: undefined
|
||||||
|
query: queryStr,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const doCreate = async (
|
||||||
|
mappingData: Mapping[],
|
||||||
|
appId: string,
|
||||||
|
context: any
|
||||||
|
) => {
|
||||||
|
const record = mappingData
|
||||||
|
.filter(
|
||||||
|
(item) =>
|
||||||
|
item.from.objectType === "variable" &&
|
||||||
|
item.from.name.name &&
|
||||||
|
item.to.app &&
|
||||||
|
item.to.fields &&
|
||||||
|
item.to.fields.length > 0
|
||||||
|
)
|
||||||
|
.reduce((accumulator, item) => {
|
||||||
|
return {
|
||||||
|
...accumulator,
|
||||||
|
[item.to.fields[0].code]: {
|
||||||
|
value: getContextVarByPath(context.variables, item.from.name.name),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}, {});
|
||||||
|
if (record && Object.keys(record).length > 0) {
|
||||||
|
await kintone.api(kintone.api.url("/k/v1/record.json", true), "POST", {
|
||||||
|
app: appId,
|
||||||
|
record: record,
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user