Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TACKLE-312: import dependencies #136

Merged
merged 10 commits into from
Sep 30, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ public class ApplicationImport extends AbstractEntity {
@Size(max = 40)
private String tag20;

private String dependency;

private String dependencyDirection;

private String errorMessage;

@Filterable(check = CheckType.EQUAL)
Expand Down Expand Up @@ -597,6 +601,26 @@ public void setFilename(String filename) {
}



public String getDependency() {
return dependency;
}

@JsonSetter("Dependency")
public void setDependency(String dependency) {
this.dependency = dependency != null ? dependency.trim() : null;
}

public String getDependencyDirection() {
return dependencyDirection;
}

@JsonSetter("Dependency Direction")
public void setDependencyDirection(String dependencyDirection) {
this.dependencyDirection = dependencyDirection != null ? dependencyDirection.trim() : null;
}


}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ public abstract class ApplicationImportForCsv {
private String tagType20;
@JsonProperty("Tag 20")
private String tag20;
@JsonProperty("Dependency")
private String dependency;
@JsonProperty("Dependency Direction")
private String dependencyDirection;
}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package io.tackle.applicationinventory.mapper;

import io.tackle.applicationinventory.BusinessService;
import io.tackle.applicationinventory.Tag;
m-brophy marked this conversation as resolved.
Show resolved Hide resolved
import io.tackle.applicationinventory.entities.Application;
import io.tackle.applicationinventory.entities.ApplicationImport;
import io.tackle.applicationinventory.entities.ApplicationsDependency;

import javax.ws.rs.core.Response;
import java.util.Set;
m-brophy marked this conversation as resolved.
Show resolved Hide resolved

public class ApplicationDependencyAPIMapper extends ApplicationMapper {

private static final String FROM_DIRECTION = "SOUTHBOUND";
private static final String TO_DIRECTION = "NORTHBOUND";

public ApplicationDependencyAPIMapper() {
super(null, null);
}

@Override
public Response map(ApplicationImport importApp, Long parentId)
{
Application application = null;
Application applicationDependency = null;
if (importApp.getApplicationName() != null)
{
application = Application.find("name", importApp.getApplicationName()).firstResult();
}

if (application == null)
{
importApp.setErrorMessage("Invalid Application Name");
return Response.serverError().build();
}

if (importApp.getDependency() != null)
{
applicationDependency = Application.find("name", importApp.getDependency()).firstResult();
}

if (applicationDependency == null)
{
importApp.setErrorMessage("Invalid Dependency");
return Response.serverError().build();
}

if (applicationDependency == application)
m-brophy marked this conversation as resolved.
Show resolved Hide resolved
{
importApp.setErrorMessage("Application cannot be a dependency of itself");
return Response.serverError().build();
}




ApplicationsDependency dependency = new ApplicationsDependency();

if (importApp.getDependencyDirection().equalsIgnoreCase(FROM_DIRECTION))
{
dependency.from = application;
dependency.to = applicationDependency;
}
else if (importApp.getDependencyDirection().equalsIgnoreCase(TO_DIRECTION))
{
dependency.from = applicationDependency;
dependency.to = application;
}
else
{
importApp.setErrorMessage("Invalid Dependency Direction");
return Response.serverError().build();
}

ApplicationsDependency found = ApplicationsDependency.find("to_id = ?1 and from_id = ?2", dependency.to.id, dependency.from.id).firstResult();

if(found != null)
{
importApp.setErrorMessage("Dependency already exists");
return Response.serverError().build();
}

dependency.persistAndFlush();
return Response.ok().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.tackle.applicationinventory.Tag;
import io.tackle.applicationinventory.entities.ApplicationImport;
import io.tackle.applicationinventory.entities.ImportSummary;
import io.tackle.applicationinventory.mapper.ApplicationDependencyAPIMapper;
import io.tackle.applicationinventory.mapper.ApplicationInventoryAPIMapper;
import io.tackle.applicationinventory.mapper.ApplicationMapper;
import org.apache.commons.lang3.StringUtils;
Expand Down Expand Up @@ -40,6 +41,8 @@ public class ImportService {
public static final String COMPLETED_STATUS = "Completed";
public static final String IN_PROGRESS_STATUS = "In Progress";
public static final String FAILED_STATUS = "Failed";
public static final String APPLICATION_IMPORT_TYPE = "1";
public static final String DEPENDENCY_IMPORT_TYPE = "2";

@Inject
EntityManager entityManager;
Expand Down Expand Up @@ -69,43 +72,68 @@ public Response importFile(@MultipartForm MultipartImportBody data) {
parentRecord.filename = data.getFileName();
parentRecord.importStatus = IN_PROGRESS_STATUS;
parentRecord.persistAndFlush();
Set<Tag> tags = tagService.getListOfTags(0, 1000);
if (tags == null)
{
String msg = "Unable to retrieve TagTypes from remote resource";
parentRecord.errorMessage = msg;
throw new Exception(msg);

List<ApplicationImport> importList = writeFile(data.getFile(), data.getFileName(), parentRecord);

List<ApplicationImport> applicationTypeImports = importList.stream().filter(anImportRow ->
anImportRow.getRecordType1().equals(APPLICATION_IMPORT_TYPE)).collect(Collectors.toList());

if(!applicationTypeImports.isEmpty()) {

//Only check for tags and business services for Application (Type 1) Imports
Set<Tag> tags = tagService.getListOfTags(0, 1000);
if (tags == null) {
String msg = "Unable to retrieve TagTypes from remote resource";
parentRecord.errorMessage = msg;
throw new Exception(msg);
}
Set<BusinessService> businessServices = businessServiceService.getListOfBusinessServices(0, 1000);
if (businessServices == null) {
String msg = "Unable to retrieve BusinessServices from remote resource";
parentRecord.errorMessage = msg;
throw new Exception(msg);
}


//we're not allowed duplicate application names within the file
Set<String> discreteAppNames = new HashSet();
//make a list of all the duplicate app names
List<ApplicationImport> importListMinusDuplicates = applicationTypeImports;
List<ApplicationImport> duplicateAppNames = applicationTypeImports.stream().filter(importApp ->
!discreteAppNames.add(importApp.getApplicationName())).collect(Collectors.toList());
if (!duplicateAppNames.isEmpty()) {
//find all the imported apps with a duplicate name and set appropriate error message
duplicateAppNames.forEach(app -> {
applicationTypeImports.stream().filter(importApp ->
app.getApplicationName().equals(importApp.getApplicationName())).collect(Collectors.toList())
.forEach(duplicateApp -> {
importListMinusDuplicates.remove(duplicateApp);
duplicateApp.setErrorMessage("Duplicate Application Name within file: " + duplicateApp.getApplicationName());
markFailedImportAsInvalid(duplicateApp);
});

});
}
mapImportsToApplication(importListMinusDuplicates, tags, businessServices, parentRecord);
}
Set<BusinessService> businessServices =businessServiceService.getListOfBusinessServices(0, 1000);
if (businessServices == null)
{
String msg = "Unable to retrieve BusinessServices from remote resource";
parentRecord.errorMessage = msg;
throw new Exception(msg);

List<ApplicationImport> dependencyTypeImports = importList.stream().filter(anImportRow ->
anImportRow.getRecordType1().equals(DEPENDENCY_IMPORT_TYPE)).collect(Collectors.toList());

if(!dependencyTypeImports.isEmpty()) {
mapImportsToDependency(dependencyTypeImports, parentRecord);
}

List<ApplicationImport> importList = writeFile(data.getFile(), data.getFileName(), parentRecord);
//we're not allowed duplicate application names within the file
Set<String> discreteAppNames = new HashSet();
//make a list of all the duplicate app names
List<ApplicationImport> importListMinusDuplicates = importList;
List<ApplicationImport> duplicateAppNames = importList.stream().filter(importApp ->
!discreteAppNames.add(importApp.getApplicationName())).collect(Collectors.toList());
if( !duplicateAppNames.isEmpty())
{
//find all the imported apps with a duplicate name and set appropriate error message
duplicateAppNames.forEach(app -> {
importList.stream().filter(importApp ->
app.getApplicationName().equals(importApp.getApplicationName())).collect(Collectors.toList())
.forEach(duplicateApp -> {
importListMinusDuplicates.remove(duplicateApp);
duplicateApp.setErrorMessage("Duplicate Application Name within file: " + duplicateApp.getApplicationName());
markFailedImportAsInvalid(duplicateApp);
});
List<ApplicationImport> noTypeImports = importList.stream().filter(anImportRow ->
!anImportRow.getRecordType1().equals(APPLICATION_IMPORT_TYPE)
&& !anImportRow.getRecordType1().equals(DEPENDENCY_IMPORT_TYPE)).collect(Collectors.toList());

if(!noTypeImports.isEmpty()) {
m-brophy marked this conversation as resolved.
Show resolved Hide resolved
noTypeImports.forEach(noTypeImport -> {
noTypeImport.setErrorMessage("Invalid Record Type");
markFailedImportAsInvalid(noTypeImport);
});
}
mapImportsToApplication(importListMinusDuplicates, tags, businessServices, parentRecord);
parentRecord.importStatus = COMPLETED_STATUS;
parentRecord.flush();

Expand Down Expand Up @@ -186,6 +214,19 @@ public void mapImportsToApplication(List<ApplicationImport> importList, Set<Tag>
});
}

public void mapImportsToDependency(List<ApplicationImport> importList,ImportSummary parentRecord)
{
ApplicationMapper mapper = new ApplicationDependencyAPIMapper();
importList.forEach(importedApplication -> {
Response response = mapper.map(importedApplication, parentRecord.id);
if (response.getStatus() != Response.Status.OK.getStatusCode())
{
markFailedImportAsInvalid(importedApplication);
}

});
}

private void markFailedImportAsInvalid(ApplicationImport importFile)
{
importFile.setValid(Boolean.FALSE);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
alter table if exists application_import
add column dependency varchar (255),
add column dependencyDirection varchar (255);
m-brophy marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ public class FlywayMigrationTest {
@Test
public void testMigration() {
// check the number of migrations applied equals the number of files in resources/db/migration folder
assertEquals(17, flyway.info().applied().length);
assertEquals(18, flyway.info().applied().length);
// check the current migration version is the one from the last file in resources/db/migration folder
assertEquals("20210809.1", flyway.info().current().getVersion().toString());
assertEquals("20210914.1", flyway.info().current().getVersion().toString());
// just a basic test to double check the application started
// to prove the flyway scripts ran successfully during startup
given()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import io.tackle.applicationinventory.Tag;
import io.tackle.applicationinventory.entities.ApplicationImport;
import io.tackle.applicationinventory.entities.ImportSummary;
import io.tackle.applicationinventory.mapper.ApplicationDependencyAPIMapper;
import io.tackle.applicationinventory.mapper.ApplicationInventoryAPIMapper;
import io.tackle.commons.testcontainers.KeycloakTestResource;
import io.tackle.commons.testcontainers.PostgreSQLDatabaseTestResource;
Expand Down Expand Up @@ -160,6 +161,8 @@ protected void testNullTagTypes() {
appImport1.setErrorMessage(null);
appImport1.setRecordType1(null);
appImport1.setComments(null);
appImport1.setDependency(null);
appImport1.setDependencyDirection(null);

Set<Tag> tags = new HashSet<>() ;
Tag.TagType tagType1 = new Tag.TagType();
Expand Down Expand Up @@ -230,4 +233,46 @@ protected void testNullTagTypes() {


}

@Test
@Transactional
protected void testNullDependency() {
ImportSummary appImportParent = new ImportSummary();
appImportParent.persistAndFlush();

ApplicationImport appImport1 = new ApplicationImport();
appImport1.setBusinessService(null);
appImport1.setApplicationName("Online Investments service");
appImport1.importSummary = appImportParent;
appImport1.setRecordType1("2");
appImport1.setDependency(null);
appImport1.setDependencyDirection(null);

ApplicationDependencyAPIMapper apiMapper = new ApplicationDependencyAPIMapper();
apiMapper.map(appImport1, appImportParent.id);

assertNull(appImport1.getDependency());

ImportSummary appImportParent2 = new ImportSummary();
appImportParent2.persistAndFlush();

ApplicationImport appImport2 = new ApplicationImport();
appImport2.setBusinessService(null);
appImport2.setApplicationName(null);
appImport2.importSummary = appImportParent;
appImport1.setRecordType1("2");
appImport1.setDependency(null);
appImport1.setDependencyDirection(null);

apiMapper.map(appImport2, appImportParent2.id);

assertNull(appImport1.getDependency());






m-brophy marked this conversation as resolved.
Show resolved Hide resolved
}

}
Loading