2022-12-29 09:58:06 -08:00
|
|
|
/*
|
|
|
|
* Copyright 2021 Collate
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package org.openmetadata.csv;
|
|
|
|
|
|
|
|
import static org.openmetadata.common.utils.CommonUtil.listOf;
|
|
|
|
import static org.openmetadata.common.utils.CommonUtil.listOrEmpty;
|
|
|
|
import static org.openmetadata.common.utils.CommonUtil.nullOrEmpty;
|
|
|
|
import static org.openmetadata.csv.CsvUtil.FIELD_SEPARATOR;
|
|
|
|
import static org.openmetadata.csv.CsvUtil.recordToString;
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.Reader;
|
|
|
|
import java.io.StringReader;
|
|
|
|
import java.io.StringWriter;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.Iterator;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.UUID;
|
|
|
|
import javax.ws.rs.core.Response;
|
2023-01-29 13:47:02 -08:00
|
|
|
import lombok.extern.slf4j.Slf4j;
|
2022-12-29 09:58:06 -08:00
|
|
|
import org.apache.commons.csv.CSVFormat;
|
|
|
|
import org.apache.commons.csv.CSVFormat.Builder;
|
|
|
|
import org.apache.commons.csv.CSVPrinter;
|
|
|
|
import org.apache.commons.csv.CSVRecord;
|
2023-01-29 13:47:02 -08:00
|
|
|
import org.openmetadata.common.utils.CommonUtil;
|
2022-12-29 09:58:06 -08:00
|
|
|
import org.openmetadata.schema.EntityInterface;
|
|
|
|
import org.openmetadata.schema.type.EntityReference;
|
|
|
|
import org.openmetadata.schema.type.Include;
|
|
|
|
import org.openmetadata.schema.type.TagLabel;
|
|
|
|
import org.openmetadata.schema.type.TagLabel.TagSource;
|
2023-01-29 13:47:02 -08:00
|
|
|
import org.openmetadata.schema.type.csv.CsvDocumentation;
|
2022-12-29 09:58:06 -08:00
|
|
|
import org.openmetadata.schema.type.csv.CsvErrorType;
|
|
|
|
import org.openmetadata.schema.type.csv.CsvFile;
|
|
|
|
import org.openmetadata.schema.type.csv.CsvHeader;
|
|
|
|
import org.openmetadata.schema.type.csv.CsvImportResult;
|
|
|
|
import org.openmetadata.schema.type.csv.CsvImportResult.Status;
|
|
|
|
import org.openmetadata.service.Entity;
|
|
|
|
import org.openmetadata.service.jdbi3.EntityRepository;
|
2023-01-29 13:47:02 -08:00
|
|
|
import org.openmetadata.service.util.EntityUtil;
|
|
|
|
import org.openmetadata.service.util.JsonUtils;
|
2022-12-29 09:58:06 -08:00
|
|
|
import org.openmetadata.service.util.RestUtil.PutResponse;
|
2023-07-15 15:02:17 -07:00
|
|
|
import org.openmetadata.service.util.ValidatorUtil;
|
2022-12-29 09:58:06 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* EntityCsv provides export and import capabilities for an entity. Each entity must implement the abstract methods to
|
|
|
|
* provide entity specific processing functionality to export an entity to a CSV record, and import an entity from a CSV
|
|
|
|
* record.
|
|
|
|
*/
|
2023-01-29 13:47:02 -08:00
|
|
|
@Slf4j
|
2022-12-29 09:58:06 -08:00
|
|
|
public abstract class EntityCsv<T extends EntityInterface> {
|
2023-05-10 20:30:17 +05:30
|
|
|
public static final String FIELD_ERROR_MSG = "#%s: Field %d error - %s";
|
2022-12-29 09:58:06 -08:00
|
|
|
public static final String IMPORT_STATUS_HEADER = "status";
|
|
|
|
public static final String IMPORT_STATUS_DETAILS = "details";
|
|
|
|
public static final String IMPORT_STATUS_SUCCESS = "success";
|
|
|
|
public static final String IMPORT_STATUS_FAILED = "failure";
|
2023-01-29 16:56:53 -08:00
|
|
|
public static final String ENTITY_CREATED = "Entity created";
|
|
|
|
public static final String ENTITY_UPDATED = "Entity updated";
|
2022-12-29 09:58:06 -08:00
|
|
|
private final String entityType;
|
|
|
|
private final List<CsvHeader> csvHeaders;
|
|
|
|
private final CsvImportResult importResult = new CsvImportResult();
|
|
|
|
protected boolean processRecord; // When set to false record processing is discontinued
|
2023-01-29 16:56:53 -08:00
|
|
|
protected final Map<String, T> dryRunCreatedEntities = new HashMap<>();
|
|
|
|
private final String importedBy;
|
2022-12-29 09:58:06 -08:00
|
|
|
|
2023-01-29 16:56:53 -08:00
|
|
|
protected EntityCsv(String entityType, List<CsvHeader> csvHeaders, String importedBy) {
|
2022-12-29 09:58:06 -08:00
|
|
|
this.entityType = entityType;
|
|
|
|
this.csvHeaders = csvHeaders;
|
2023-01-29 16:56:53 -08:00
|
|
|
this.importedBy = importedBy;
|
2022-12-29 09:58:06 -08:00
|
|
|
}
|
|
|
|
|
2023-01-29 16:56:53 -08:00
|
|
|
/** Import entities from a CSV file */
|
2022-12-29 09:58:06 -08:00
|
|
|
public final CsvImportResult importCsv(String csv, boolean dryRun) throws IOException {
|
|
|
|
importResult.withDryRun(dryRun);
|
|
|
|
StringWriter writer = new StringWriter();
|
|
|
|
CSVPrinter resultsPrinter = getResultsCsv(csvHeaders, writer);
|
|
|
|
if (resultsPrinter == null) {
|
|
|
|
return importResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse CSV
|
|
|
|
Iterator<CSVRecord> records = parse(csv);
|
|
|
|
if (records == null) {
|
|
|
|
return importResult; // Error during parsing
|
|
|
|
}
|
|
|
|
|
2023-01-29 16:56:53 -08:00
|
|
|
// First record is CSV header - Validate headers
|
2022-12-29 09:58:06 -08:00
|
|
|
List<String> expectedHeaders = CsvUtil.getHeaders(csvHeaders);
|
|
|
|
if (!validateHeaders(expectedHeaders, records.next())) {
|
|
|
|
return importResult;
|
|
|
|
}
|
|
|
|
importResult.withNumberOfRowsPassed(importResult.getNumberOfRowsPassed() + 1);
|
|
|
|
|
|
|
|
// Validate and load each record
|
|
|
|
while (records.hasNext()) {
|
2023-05-10 20:30:17 +05:30
|
|
|
CSVRecord csvRecord = records.next();
|
|
|
|
processRecord(resultsPrinter, expectedHeaders, csvRecord);
|
2022-12-29 09:58:06 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Finally, create the entities parsed from the record
|
|
|
|
setFinalStatus();
|
|
|
|
importResult.withImportResultsCsv(writer.toString());
|
|
|
|
return importResult;
|
|
|
|
}
|
|
|
|
|
2023-01-29 16:56:53 -08:00
|
|
|
/** Implement this method to a CSV record and turn it into an entity */
|
2023-05-10 20:30:17 +05:30
|
|
|
protected abstract T toEntity(CSVPrinter resultsPrinter, CSVRecord csvRecord) throws IOException;
|
2022-12-29 09:58:06 -08:00
|
|
|
|
|
|
|
public final String exportCsv(List<T> entities) throws IOException {
|
|
|
|
CsvFile csvFile = new CsvFile().withHeaders(csvHeaders);
|
|
|
|
List<List<String>> records = new ArrayList<>();
|
|
|
|
for (T entity : entities) {
|
|
|
|
records.add(toRecord(entity));
|
|
|
|
}
|
|
|
|
csvFile.withRecords(records);
|
|
|
|
return CsvUtil.formatCsv(csvFile);
|
|
|
|
}
|
|
|
|
|
2023-01-29 13:47:02 -08:00
|
|
|
public static CsvDocumentation getCsvDocumentation(String entityType) {
|
|
|
|
LOG.info("Initializing CSV documentation for entity {}", entityType);
|
|
|
|
String path = String.format(".*json/data/%s/%sCsvDocumentation.json$", entityType, entityType);
|
|
|
|
try {
|
|
|
|
List<String> jsonDataFiles = EntityUtil.getJsonDataResources(path);
|
|
|
|
String json = CommonUtil.getResourceAsStream(EntityRepository.class.getClassLoader(), jsonDataFiles.get(0));
|
|
|
|
return JsonUtils.readValue(json, CsvDocumentation.class);
|
|
|
|
} catch (IOException e) {
|
|
|
|
LOG.error("FATAL - Failed to load CSV documentation for entity {} from the path {}", entityType, path);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2023-01-29 16:56:53 -08:00
|
|
|
/** Implement this method to export an entity into a list of fields to create a CSV record */
|
2022-12-29 09:58:06 -08:00
|
|
|
protected abstract List<String> toRecord(T entity);
|
|
|
|
|
2023-01-29 16:56:53 -08:00
|
|
|
/** Owner field is in entityType;entityName format */
|
2023-05-10 20:30:17 +05:30
|
|
|
public EntityReference getOwner(CSVPrinter printer, CSVRecord csvRecord, int fieldNumber) throws IOException {
|
|
|
|
String owner = csvRecord.get(fieldNumber);
|
2023-04-02 15:04:00 -07:00
|
|
|
if (nullOrEmpty(owner)) {
|
2023-01-29 16:56:53 -08:00
|
|
|
return null;
|
|
|
|
}
|
2023-04-02 15:04:00 -07:00
|
|
|
|
|
|
|
List<String> list = CsvUtil.fieldToStrings(owner);
|
2023-01-29 16:56:53 -08:00
|
|
|
if (list.size() != 2) {
|
2023-05-10 20:30:17 +05:30
|
|
|
importFailure(printer, invalidOwner(fieldNumber), csvRecord);
|
2023-06-19 03:13:05 -07:00
|
|
|
return null;
|
|
|
|
}
|
2023-08-08 16:42:54 -07:00
|
|
|
return getEntityReference(printer, csvRecord, fieldNumber, list.get(0), list.get(1));
|
2023-06-19 03:13:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Owner field is in entityName format */
|
|
|
|
public EntityReference getOwnerAsUser(CSVPrinter printer, CSVRecord csvRecord, int fieldNumber) throws IOException {
|
|
|
|
String owner = csvRecord.get(fieldNumber);
|
|
|
|
if (nullOrEmpty(owner)) {
|
|
|
|
return null;
|
2023-01-29 16:56:53 -08:00
|
|
|
}
|
2023-08-08 16:42:54 -07:00
|
|
|
return getEntityReference(printer, csvRecord, fieldNumber, Entity.USER, owner);
|
2023-01-29 16:56:53 -08:00
|
|
|
}
|
|
|
|
|
2023-05-10 20:30:17 +05:30
|
|
|
protected final Boolean getBoolean(CSVPrinter printer, CSVRecord csvRecord, int fieldNumber) throws IOException {
|
|
|
|
String field = csvRecord.get(fieldNumber);
|
2023-01-29 16:56:53 -08:00
|
|
|
if (nullOrEmpty(field)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
if (field.equals(Boolean.TRUE.toString())) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (field.equals(Boolean.FALSE.toString())) {
|
|
|
|
return false;
|
|
|
|
}
|
2023-05-10 20:30:17 +05:30
|
|
|
importFailure(printer, invalidBoolean(fieldNumber, field), csvRecord);
|
2023-01-29 16:56:53 -08:00
|
|
|
processRecord = false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-12-29 09:58:06 -08:00
|
|
|
protected final EntityReference getEntityReference(
|
2023-05-10 20:30:17 +05:30
|
|
|
CSVPrinter printer, CSVRecord csvRecord, int fieldNumber, String entityType) throws IOException {
|
|
|
|
String fqn = csvRecord.get(fieldNumber);
|
|
|
|
return getEntityReference(printer, csvRecord, fieldNumber, entityType, fqn);
|
2022-12-29 09:58:06 -08:00
|
|
|
}
|
|
|
|
|
2023-01-29 16:56:53 -08:00
|
|
|
protected EntityInterface getEntityByName(String entityType, String fqn) {
|
2022-12-29 09:58:06 -08:00
|
|
|
EntityInterface entity = entityType.equals(this.entityType) ? dryRunCreatedEntities.get(fqn) : null;
|
|
|
|
if (entity == null) {
|
|
|
|
EntityRepository<?> entityRepository = Entity.getEntityRepository(entityType);
|
2023-08-08 16:42:54 -07:00
|
|
|
entity = entityRepository.findByNameOrNull(fqn, Include.NON_DELETED);
|
2022-12-29 09:58:06 -08:00
|
|
|
}
|
|
|
|
return entity;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected final EntityReference getEntityReference(
|
2023-05-10 20:30:17 +05:30
|
|
|
CSVPrinter printer, CSVRecord csvRecord, int fieldNumber, String entityType, String fqn) throws IOException {
|
2022-12-29 09:58:06 -08:00
|
|
|
if (nullOrEmpty(fqn)) {
|
|
|
|
return null;
|
|
|
|
}
|
2023-01-29 16:56:53 -08:00
|
|
|
EntityInterface entity = getEntityByName(entityType, fqn);
|
2022-12-29 09:58:06 -08:00
|
|
|
if (entity == null) {
|
2023-05-10 20:30:17 +05:30
|
|
|
importFailure(printer, entityNotFound(fieldNumber, fqn), csvRecord);
|
2022-12-29 09:58:06 -08:00
|
|
|
processRecord = false;
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return entity.getEntityReference();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected final List<EntityReference> getEntityReferences(
|
2023-05-10 20:30:17 +05:30
|
|
|
CSVPrinter printer, CSVRecord csvRecord, int fieldNumber, String entityType) throws IOException {
|
|
|
|
String fqns = csvRecord.get(fieldNumber);
|
2022-12-29 09:58:06 -08:00
|
|
|
if (nullOrEmpty(fqns)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
List<String> fqnList = listOrEmpty(CsvUtil.fieldToStrings(fqns));
|
|
|
|
List<EntityReference> refs = new ArrayList<>();
|
|
|
|
for (String fqn : fqnList) {
|
2023-05-10 20:30:17 +05:30
|
|
|
EntityReference ref = getEntityReference(printer, csvRecord, fieldNumber, entityType, fqn);
|
2022-12-29 09:58:06 -08:00
|
|
|
if (!processRecord) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
if (ref != null) {
|
|
|
|
refs.add(ref);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return refs.isEmpty() ? null : refs;
|
|
|
|
}
|
|
|
|
|
2023-06-19 03:13:05 -07:00
|
|
|
protected final List<EntityReference> getUserOrTeamEntityReferences(
|
|
|
|
CSVPrinter printer, CSVRecord csvRecord, int fieldNumber, String entityType) throws IOException {
|
|
|
|
String fqns = csvRecord.get(fieldNumber);
|
|
|
|
if (nullOrEmpty(fqns)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
List<String> fqnList = listOrEmpty(CsvUtil.fieldToStrings(fqns));
|
|
|
|
List<EntityReference> refs = new ArrayList<>();
|
|
|
|
for (String fqn : fqnList) {
|
2023-08-08 16:42:54 -07:00
|
|
|
EntityReference ref = getEntityReference(printer, csvRecord, fieldNumber, entityType, fqn);
|
2023-06-19 03:13:05 -07:00
|
|
|
if (!processRecord) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
if (ref != null) {
|
|
|
|
refs.add(ref);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return refs.isEmpty() ? null : refs;
|
|
|
|
}
|
|
|
|
|
2023-05-10 20:30:17 +05:30
|
|
|
protected final List<TagLabel> getTagLabels(CSVPrinter printer, CSVRecord csvRecord, int fieldNumber)
|
2022-12-29 09:58:06 -08:00
|
|
|
throws IOException {
|
2023-05-10 20:30:17 +05:30
|
|
|
List<EntityReference> refs = getEntityReferences(printer, csvRecord, fieldNumber, Entity.TAG);
|
2022-12-29 09:58:06 -08:00
|
|
|
if (!processRecord || nullOrEmpty(refs)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
List<TagLabel> tagLabels = new ArrayList<>();
|
|
|
|
for (EntityReference ref : refs) {
|
2023-03-09 00:30:36 -08:00
|
|
|
tagLabels.add(new TagLabel().withSource(TagSource.CLASSIFICATION).withTagFQN(ref.getFullyQualifiedName()));
|
2022-12-29 09:58:06 -08:00
|
|
|
}
|
|
|
|
return tagLabels;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static String[] getResultHeaders(List<CsvHeader> csvHeaders) {
|
|
|
|
List<String> importResultsCsvHeader = listOf(IMPORT_STATUS_HEADER, IMPORT_STATUS_DETAILS);
|
|
|
|
importResultsCsvHeader.addAll(CsvUtil.getHeaders(csvHeaders));
|
|
|
|
return importResultsCsvHeader.toArray(new String[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a CSVPrinter to capture the import results
|
|
|
|
private CSVPrinter getResultsCsv(List<CsvHeader> csvHeaders, StringWriter writer) {
|
|
|
|
CSVFormat format = Builder.create(CSVFormat.DEFAULT).setHeader(getResultHeaders(csvHeaders)).build();
|
|
|
|
try {
|
|
|
|
return new CSVPrinter(writer, format);
|
|
|
|
} catch (IOException e) {
|
|
|
|
documentFailure(failed(e.getMessage(), CsvErrorType.UNKNOWN));
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
private Iterator<CSVRecord> parse(String csv) {
|
|
|
|
Reader in = new StringReader(csv);
|
|
|
|
try {
|
|
|
|
return CSVFormat.DEFAULT.parse(in).iterator();
|
|
|
|
} catch (IOException e) {
|
|
|
|
documentFailure(failed(e.getMessage(), CsvErrorType.PARSER_FAILURE));
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2023-05-10 20:30:17 +05:30
|
|
|
private boolean validateHeaders(List<String> expectedHeaders, CSVRecord csvRecord) {
|
|
|
|
importResult.withNumberOfRowsProcessed((int) csvRecord.getRecordNumber());
|
|
|
|
if (expectedHeaders.equals(csvRecord.toList())) {
|
2022-12-29 09:58:06 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
importResult.withNumberOfRowsFailed(1);
|
2023-05-10 20:30:17 +05:30
|
|
|
documentFailure(invalidHeader(recordToString(expectedHeaders), recordToString(csvRecord)));
|
2022-12-29 09:58:06 -08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-05-10 20:30:17 +05:30
|
|
|
private void processRecord(CSVPrinter resultsPrinter, List<String> expectedHeader, CSVRecord csvRecord)
|
2022-12-29 09:58:06 -08:00
|
|
|
throws IOException {
|
|
|
|
processRecord = true;
|
|
|
|
// Every row must have total fields corresponding to the number of headers
|
2023-05-10 20:30:17 +05:30
|
|
|
if (csvHeaders.size() != csvRecord.size()) {
|
|
|
|
importFailure(resultsPrinter, invalidFieldCount(expectedHeader.size(), csvRecord.size()), csvRecord);
|
2022-12-29 09:58:06 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if required values are present
|
|
|
|
List<String> errors = new ArrayList<>();
|
|
|
|
for (int i = 0; i < csvHeaders.size(); i++) {
|
2023-05-10 20:30:17 +05:30
|
|
|
String field = csvRecord.get(i);
|
2022-12-29 09:58:06 -08:00
|
|
|
boolean fieldRequired = Boolean.TRUE.equals(csvHeaders.get(i).getRequired());
|
|
|
|
if (fieldRequired && nullOrEmpty(field)) {
|
|
|
|
errors.add(fieldRequired(i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!errors.isEmpty()) {
|
2023-05-10 20:30:17 +05:30
|
|
|
importFailure(resultsPrinter, String.join(FIELD_SEPARATOR, errors), csvRecord);
|
2022-12-29 09:58:06 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finally, convert record into entity for importing
|
2023-05-10 20:30:17 +05:30
|
|
|
T entity = toEntity(resultsPrinter, csvRecord);
|
2022-12-29 09:58:06 -08:00
|
|
|
if (entity != null) {
|
|
|
|
// Finally, create entities
|
2023-05-10 20:30:17 +05:30
|
|
|
createEntity(resultsPrinter, csvRecord, entity);
|
2022-12-29 09:58:06 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-10 20:30:17 +05:30
|
|
|
private void createEntity(CSVPrinter resultsPrinter, CSVRecord csvRecord, T entity) throws IOException {
|
2022-12-29 09:58:06 -08:00
|
|
|
entity.setId(UUID.randomUUID());
|
2023-01-29 16:56:53 -08:00
|
|
|
entity.setUpdatedBy(importedBy);
|
2022-12-29 09:58:06 -08:00
|
|
|
entity.setUpdatedAt(System.currentTimeMillis());
|
2023-01-30 21:34:34 -08:00
|
|
|
EntityRepository<T> repository = (EntityRepository<T>) Entity.getEntityRepository(entityType);
|
2022-12-29 09:58:06 -08:00
|
|
|
Response.Status responseStatus;
|
2023-08-09 18:50:50 -07:00
|
|
|
String violations = ValidatorUtil.validate(entity);
|
|
|
|
if (violations != null) {
|
|
|
|
// JSON schema based validation failed for the entity
|
|
|
|
importFailure(resultsPrinter, violations, csvRecord);
|
|
|
|
return;
|
|
|
|
}
|
2023-05-10 20:30:17 +05:30
|
|
|
if (Boolean.FALSE.equals(importResult.getDryRun())) {
|
2022-12-29 09:58:06 -08:00
|
|
|
try {
|
|
|
|
repository.prepareInternal(entity);
|
2023-01-30 21:34:34 -08:00
|
|
|
PutResponse<T> response = repository.createOrUpdate(null, entity);
|
2022-12-29 09:58:06 -08:00
|
|
|
responseStatus = response.getStatus();
|
|
|
|
} catch (Exception ex) {
|
2023-05-10 20:30:17 +05:30
|
|
|
importFailure(resultsPrinter, ex.getMessage(), csvRecord);
|
2022-12-29 09:58:06 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
repository.setFullyQualifiedName(entity);
|
|
|
|
responseStatus =
|
2023-08-08 16:42:54 -07:00
|
|
|
repository.findByNameOrNull(entity.getFullyQualifiedName(), Include.NON_DELETED) == null
|
2022-12-29 09:58:06 -08:00
|
|
|
? Response.Status.CREATED
|
|
|
|
: Response.Status.OK;
|
|
|
|
// Track the dryRun created entities, as they may be referred by other entities being created during import
|
|
|
|
dryRunCreatedEntities.put(entity.getFullyQualifiedName(), entity);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Response.Status.CREATED.equals(responseStatus)) {
|
2023-05-10 20:30:17 +05:30
|
|
|
importSuccess(resultsPrinter, csvRecord, ENTITY_CREATED);
|
2022-12-29 09:58:06 -08:00
|
|
|
} else {
|
2023-05-10 20:30:17 +05:30
|
|
|
importSuccess(resultsPrinter, csvRecord, ENTITY_UPDATED);
|
2022-12-29 09:58:06 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public String failed(String exception, CsvErrorType errorType) {
|
|
|
|
return String.format("#%s: Failed to parse the CSV filed - reason %s", errorType, exception);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static String invalidHeader(String expected, String actual) {
|
|
|
|
return String.format("#%s: Headers [%s] doesn't match [%s]", CsvErrorType.INVALID_HEADER, actual, expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static String invalidFieldCount(int expectedFieldCount, int actualFieldCount) {
|
|
|
|
return String.format(
|
|
|
|
"#%s: Field count %d does not match the expected field count of %d",
|
|
|
|
CsvErrorType.INVALID_FIELD_COUNT, actualFieldCount, expectedFieldCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static String fieldRequired(int field) {
|
|
|
|
return String.format("#%s: Field %d is required", CsvErrorType.FIELD_REQUIRED, field + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static String invalidField(int field, String error) {
|
2023-05-10 20:30:17 +05:30
|
|
|
return String.format(FIELD_ERROR_MSG, CsvErrorType.INVALID_FIELD, field + 1, error);
|
2022-12-29 09:58:06 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
public static String entityNotFound(int field, String fqn) {
|
|
|
|
String error = String.format("Entity %s not found", fqn);
|
2023-05-10 20:30:17 +05:30
|
|
|
return String.format(FIELD_ERROR_MSG, CsvErrorType.INVALID_FIELD, field + 1, error);
|
2022-12-29 09:58:06 -08:00
|
|
|
}
|
|
|
|
|
2023-01-29 16:56:53 -08:00
|
|
|
public static String invalidOwner(int field) {
|
|
|
|
String error = "Owner should be of format user;userName or team;teamName";
|
2023-05-10 20:30:17 +05:30
|
|
|
return String.format(FIELD_ERROR_MSG, CsvErrorType.INVALID_FIELD, field + 1, error);
|
2023-01-29 16:56:53 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
public static String invalidBoolean(int field, String fieldValue) {
|
|
|
|
String error = String.format("Field %s should be either 'true' of 'false'", fieldValue);
|
2023-05-10 20:30:17 +05:30
|
|
|
return String.format(FIELD_ERROR_MSG, CsvErrorType.INVALID_FIELD, field + 1, error);
|
2023-01-29 16:56:53 -08:00
|
|
|
}
|
|
|
|
|
2022-12-29 09:58:06 -08:00
|
|
|
private void documentFailure(String error) {
|
|
|
|
importResult.withStatus(Status.ABORTED);
|
|
|
|
importResult.withAbortReason(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void importSuccess(CSVPrinter printer, CSVRecord inputRecord, String successDetails) throws IOException {
|
2023-05-10 20:30:17 +05:30
|
|
|
List<String> recordList = listOf(IMPORT_STATUS_SUCCESS, successDetails);
|
|
|
|
recordList.addAll(inputRecord.toList());
|
|
|
|
printer.printRecord(recordList);
|
2022-12-29 09:58:06 -08:00
|
|
|
importResult.withNumberOfRowsProcessed((int) inputRecord.getRecordNumber());
|
|
|
|
importResult.withNumberOfRowsPassed(importResult.getNumberOfRowsPassed() + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void importFailure(CSVPrinter printer, String failedReason, CSVRecord inputRecord) throws IOException {
|
2023-05-10 20:30:17 +05:30
|
|
|
List<String> recordList = listOf(IMPORT_STATUS_FAILED, failedReason);
|
|
|
|
recordList.addAll(inputRecord.toList());
|
|
|
|
printer.printRecord(recordList);
|
2022-12-29 09:58:06 -08:00
|
|
|
importResult.withNumberOfRowsProcessed((int) inputRecord.getRecordNumber());
|
|
|
|
importResult.withNumberOfRowsFailed(importResult.getNumberOfRowsFailed() + 1);
|
|
|
|
processRecord = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void setFinalStatus() {
|
2023-05-10 20:30:17 +05:30
|
|
|
Status status = Status.FAILURE;
|
|
|
|
if (importResult.getNumberOfRowsPassed().equals(importResult.getNumberOfRowsProcessed())) {
|
|
|
|
status = Status.SUCCESS;
|
|
|
|
} else if (importResult.getNumberOfRowsPassed() > 1) {
|
|
|
|
status = Status.PARTIAL_SUCCESS;
|
|
|
|
}
|
2022-12-29 09:58:06 -08:00
|
|
|
importResult.setStatus(status);
|
|
|
|
}
|
|
|
|
}
|