/*
* Copyright 2015 herd contributors
*
* 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.finra.herd.tools.uploader;
import java.io.IOException;
import java.io.Reader;
import java.util.HashSet;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.finra.herd.model.api.xml.BusinessObjectDataKey;
import org.finra.herd.model.dto.ManifestFile;
import org.finra.herd.model.dto.UploaderInputManifestDto;
import org.finra.herd.service.helper.BusinessObjectDataHelper;
import org.finra.herd.tools.common.databridge.DataBridgeManifestReader;
/**
* Manifest reader that reads and validates an uploader JSON input manifest file.
*/
@Component
public class UploaderManifestReader extends DataBridgeManifestReader<UploaderInputManifestDto>
{
public static final int MAX_ATTRIBUTE_NAME_LENGTH = 100;
public static final int MAX_ATTRIBUTE_VALUE_LENGTH = 4000;
@Autowired
private BusinessObjectDataHelper businessObjectDataHelper;
@Override
protected UploaderInputManifestDto getManifestFromReader(Reader reader, ObjectMapper objectMapper) throws IOException
{
return objectMapper.readValue(reader, UploaderInputManifestDto.class);
}
/**
* Validates the manifest file.
*
* @param manifest the manifest to validate.
*
* @throws IllegalArgumentException if the manifest is not valid.
*/
@Override
protected void validateManifest(UploaderInputManifestDto manifest) throws IllegalArgumentException
{
// Perform the base validation.
super.validateManifest(manifest);
Assert.hasText(manifest.getBusinessObjectFormatVersion(), "Manifest business object format version must be specified.");
Assert.notEmpty(manifest.getManifestFiles(), "Manifest must contain at least 1 file.");
// Ensure row counts are all positive.
for (ManifestFile manifestFile : manifest.getManifestFiles())
{
Assert.hasText(manifestFile.getFileName(), "Manifest file can not have a blank filename.");
// Trim to ensure we don't get errors with leading or trailing spaces causing "path" errors when validating that files exist.
manifestFile.setFileName(manifestFile.getFileName().trim());
if (manifestFile.getRowCount() != null)
{
Assert.isTrue(manifestFile.getRowCount() >= 0, "Manifest file \"" + manifestFile.getFileName() + "\" can not have a negative row count.");
}
}
if (manifest.getAttributes() != null)
{
HashSet<String> attributeNameValidationSet = new HashSet<>();
for (Map.Entry<String, String> entry : manifest.getAttributes().entrySet())
{
String attributeName = entry.getKey().trim();
Assert.hasText(attributeName, "Manifest attribute name must be specified.");
// Validate attribute key length.
Assert.isTrue(attributeName.length() <= MAX_ATTRIBUTE_NAME_LENGTH,
String.format("Manifest attribute name is longer than %d characters.", MAX_ATTRIBUTE_NAME_LENGTH));
// Ensure the attribute name isn't a duplicate by using a map with a "lowercase" name as the key for case insensitivity.
String lowercaseAttributeName = attributeName.toLowerCase();
Assert.isTrue(!attributeNameValidationSet.contains(lowercaseAttributeName),
String.format("Duplicate manifest attribute name found: %s", attributeName));
// Validate attribute value length.
if (entry.getValue() != null)
{
Assert.isTrue(entry.getValue().length() <= MAX_ATTRIBUTE_VALUE_LENGTH,
String.format("Manifest attribute value is longer than %d characters.", MAX_ATTRIBUTE_VALUE_LENGTH));
}
attributeNameValidationSet.add(lowercaseAttributeName);
}
}
if (manifest.getBusinessObjectDataParents() != null)
{
HashSet<BusinessObjectDataKey> parentValidationSet = new HashSet<>();
for (BusinessObjectDataKey businessObjectDataKey : manifest.getBusinessObjectDataParents())
{
// Perform validation.
Assert.hasText(businessObjectDataKey.getBusinessObjectDefinitionName(), "Manifest parent business object definition name must be specified.");
Assert.hasText(businessObjectDataKey.getBusinessObjectFormatUsage(), "Manifest parent business object format usage must be specified.");
Assert.hasText(businessObjectDataKey.getBusinessObjectFormatFileType(), "Manifest parent business object format file type must be specified.");
Assert.hasText(businessObjectDataKey.getPartitionValue(), "Manifest parent business object data partition value must be specified.");
// Remove leading and trailing spaces.
businessObjectDataKey.setBusinessObjectDefinitionName(businessObjectDataKey.getBusinessObjectDefinitionName().trim());
businessObjectDataKey.setBusinessObjectFormatUsage(businessObjectDataKey.getBusinessObjectFormatUsage().trim());
businessObjectDataKey.setBusinessObjectFormatFileType(businessObjectDataKey.getBusinessObjectFormatFileType().trim());
businessObjectDataKey.setPartitionValue(businessObjectDataKey.getPartitionValue().trim());
// Ensure the business object data parent isn't a duplicate by using a unique set
// with "lowercase" parent alternate key fields that are case insensitive.
BusinessObjectDataKey lowercaseKey = new BusinessObjectDataKey();
lowercaseKey.setBusinessObjectDefinitionName(businessObjectDataKey.getBusinessObjectDefinitionName().toLowerCase());
lowercaseKey.setBusinessObjectFormatUsage(businessObjectDataKey.getBusinessObjectFormatUsage().toLowerCase());
lowercaseKey.setBusinessObjectFormatFileType(businessObjectDataKey.getBusinessObjectFormatFileType().toLowerCase());
lowercaseKey.setBusinessObjectFormatVersion(businessObjectDataKey.getBusinessObjectFormatVersion());
lowercaseKey.setPartitionValue(businessObjectDataKey.getPartitionValue());
lowercaseKey.setBusinessObjectDataVersion(businessObjectDataKey.getBusinessObjectDataVersion());
Assert.isTrue(!parentValidationSet.contains(lowercaseKey), String.format("Duplicate manifest business object data parent found: {%s}",
businessObjectDataHelper.businessObjectDataKeyToString(businessObjectDataKey)));
parentValidationSet.add(lowercaseKey);
}
}
}
}