Skip to content

Commit

Permalink
Merge pull request #7149 from deutschebank/db-contrib/waltz-7145-mr-bulk
Browse files Browse the repository at this point in the history
Db contrib/waltz 7145 mr bulk
  • Loading branch information
davidwatkins73 authored Sep 24, 2024
2 parents edbe7c0 + 02cd277 commit 7de1840
Show file tree
Hide file tree
Showing 31 changed files with 1,463 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ public ApplicationDao(DSLContext dsl) {


public Application getById(long id) {
return dsl.select()
return dsl
.select(APPLICATION.fields())
.from(APPLICATION)
.where(APPLICATION.ID.eq(id))
.fetchOne(TO_DOMAIN_MAPPER);
Expand All @@ -110,14 +111,15 @@ public Application getById(long id) {

public List<Application> findAll() {
return dsl
.select()
.select(APPLICATION.fields())
.from(APPLICATION)
.fetch(TO_DOMAIN_MAPPER);
}


public List<Application> findByIds(Collection<Long> ids) {
return dsl.select()
return dsl
.select(APPLICATION.fields())
.from(APPLICATION)
.where(APPLICATION.ID.in(ids))
.fetch(TO_DOMAIN_MAPPER);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@
import org.finos.waltz.model.tally.MeasurableRatingTally;
import org.finos.waltz.model.tally.Tally;
import org.finos.waltz.schema.Tables;
import org.finos.waltz.schema.tables.EntityHierarchy;
import org.finos.waltz.schema.tables.Measurable;
import org.finos.waltz.schema.tables.records.AllocationRecord;
import org.finos.waltz.schema.tables.records.MeasurableRatingPlannedDecommissionRecord;
import org.finos.waltz.schema.tables.records.MeasurableRatingRecord;
Expand Down Expand Up @@ -812,7 +810,7 @@ public boolean saveRatingItem(EntityReference entityRef,
long measurableId,
String ratingCode,
String username) {
return MeasurableRatingHelper.saveRatingItem(
return MeasurableRatingUtilities.saveRatingItem(
dsl,
entityRef,
measurableId,
Expand All @@ -822,7 +820,7 @@ public boolean saveRatingItem(EntityReference entityRef,


public boolean saveRatingIsPrimary(EntityReference entityRef, long measurableId, boolean isPrimary, String username) {
return dsl.transactionResult(ctx -> MeasurableRatingHelper.saveRatingIsPrimary(
return dsl.transactionResult(ctx -> MeasurableRatingUtilities.saveRatingIsPrimary(
ctx.dsl(),
entityRef,
measurableId,
Expand All @@ -832,7 +830,7 @@ public boolean saveRatingIsPrimary(EntityReference entityRef, long measurableId,


public boolean saveRatingDescription(EntityReference entityRef, long measurableId, String description, String username) {
return dsl.transactionResult(ctx -> MeasurableRatingHelper.saveRatingDescription(
return dsl.transactionResult(ctx -> MeasurableRatingUtilities.saveRatingDescription(
ctx.dsl(),
entityRef,
measurableId,
Expand All @@ -844,7 +842,7 @@ public boolean saveRatingDescription(EntityReference entityRef, long measurableI
public MeasurableRatingChangeSummary resolveLoggingContextForRatingChange(EntityReference entityRef,
long measurableId,
String desiredRatingCode) {
return MeasurableRatingHelper.resolveLoggingContextForRatingChange(dsl, entityRef, measurableId, desiredRatingCode);
return MeasurableRatingUtilities.resolveLoggingContextForRatingChange(dsl, entityRef, measurableId, desiredRatingCode);
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import static org.finos.waltz.schema.tables.MeasurableRating.MEASURABLE_RATING;
import static org.jooq.lambda.tuple.Tuple.tuple;

public class MeasurableRatingHelper {
public class MeasurableRatingUtilities {

/**
* Updates the given measurable rating to be set as primary.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
package org.finos.waltz.integration_test.inmem.service;


import org.finos.waltz.common.CollectionUtilities;
import org.finos.waltz.data.measurable_category.MeasurableCategoryDao;
import org.finos.waltz.integration_test.inmem.BaseInMemoryIntegrationTest;
import org.finos.waltz.model.EntityReference;
import org.finos.waltz.model.bulk_upload.BulkUpdateMode;
import org.finos.waltz.model.bulk_upload.measurable_rating.BulkMeasurableRatingValidatedItem;
import org.finos.waltz.model.bulk_upload.measurable_rating.BulkMeasurableRatingValidationResult;
import org.finos.waltz.model.bulk_upload.measurable_rating.ChangeOperation;
import org.finos.waltz.model.bulk_upload.measurable_rating.ValidationError;
import org.finos.waltz.model.measurable_category.MeasurableCategory;
import org.finos.waltz.service.measurable.MeasurableService;
import org.finos.waltz.service.measurable_rating.BulkMeasurableItemParser;
import org.finos.waltz.service.measurable_rating.BulkMeasurableRatingService;
import org.finos.waltz.test_common.helpers.AppHelper;
import org.finos.waltz.test_common.helpers.MeasurableHelper;
import org.finos.waltz.test_common.helpers.MeasurableRatingHelper;
import org.finos.waltz.test_common.helpers.RatingSchemeHelper;
import org.finos.waltz.test_common.helpers.UserHelper;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

import static java.lang.String.format;
import static java.util.Collections.emptySet;
import static org.finos.waltz.common.CollectionUtilities.all;
import static org.finos.waltz.common.CollectionUtilities.first;
import static org.finos.waltz.common.CollectionUtilities.isEmpty;
import static org.finos.waltz.common.ListUtilities.asList;
import static org.finos.waltz.common.ListUtilities.map;
import static org.finos.waltz.common.SetUtilities.asSet;
import static org.finos.waltz.test_common.helpers.NameHelper.mkName;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class BulkMeasurableRatingServiceTest extends BaseInMemoryIntegrationTest {
@Autowired
private UserHelper userHelper;

@Autowired
private AppHelper appHelper;

@Autowired
private MeasurableHelper measurableHelper;

@Autowired
private MeasurableRatingHelper measurableRatingHelper;

@Autowired
private MeasurableCategoryDao measurableCategoryDao;

@Autowired
private RatingSchemeHelper ratingSchemeHelper;

@Autowired
private MeasurableService measurableService;

@Autowired
private BulkMeasurableRatingService bulkMeasurableRatingService;

private static final String stem = "BMRST";
private static final Logger LOG = LoggerFactory.getLogger(BulkMeasurableRatingServiceTest.class);

@Test
public void previewAdds() {
MeasurableCategory category = measurableCategoryDao.getById(setupCategory());
measurableHelper.createMeasurable("CT-001", "M1", category.id().get());
String app1AssetCode = mkName(stem, "previewUpdatesCode");
EntityReference app1Ref = appHelper.createNewApp(
mkName(stem, "previewUpdatesApp"),
ouIds.root,
app1AssetCode);

ratingSchemeHelper.saveRatingItem(category.ratingSchemeId(), mkName(stem, "Rating1"), 0, "#111", "R", mkName(stem, "R"));
BulkMeasurableRatingValidationResult result = bulkMeasurableRatingService.bulkPreview(
category.entityReference(),
mkGoodTsv(app1AssetCode),
BulkMeasurableItemParser.InputFormat.TSV,
BulkUpdateMode.ADD_ONLY);

assertNotNull(result, "Expected a result");
assertNoErrors(result);
assertTaxonomyExternalIdMatch(result, asList("CT-001"));
}


@Test
public void previewUpdates() {
LOG.info("Setup app, measurable and rating scheme item");
MeasurableCategory category = measurableCategoryDao.getById(setupCategory());
long measurableId = measurableHelper.createMeasurable("CT-001", "M1", category.id().get());
String app1AssetCode = mkName(stem, "previewUpdatesCode");
EntityReference appRef = appHelper.createNewApp(
mkName(stem, "previewUpdatesApp"),
ouIds.root,
app1AssetCode);
ratingSchemeHelper.saveRatingItem(category.ratingSchemeId(), mkName(stem, "Rating1"), 0, "#111", "R", mkName(stem, "R"));

LOG.info("Create an existing rating for the app");
measurableRatingHelper.saveRatingItem(appRef, measurableId, "R", "user");

BulkMeasurableRatingValidationResult result = bulkMeasurableRatingService.bulkPreview(
category.entityReference(),
mkGoodTsv(app1AssetCode),
BulkMeasurableItemParser.InputFormat.TSV,
BulkUpdateMode.ADD_ONLY);

assertNotNull(result, "Expected a result");
assertNoErrors(result);
assertTaxonomyExternalIdMatch(result, asList("CT-001"));
assertEquals(1, result.validatedItems().size(), "Only one parsed row expected");
assertEquals(
ChangeOperation.UPDATE,
first(result.validatedItems()).changeOperation());
}


@Test
public void previewComprehensive() {
LOG.info("Setup app, measurable and rating scheme item");
MeasurableCategory category = measurableCategoryDao.getById(setupCategory());
long measurable1Id = measurableHelper.createMeasurable("CT-001", "M1", category.id().get());
measurableHelper.createMeasurable("CT-002", "M2", category.id().get());

String app1AssetCode = mkName(stem, "previewUpdatesCode");
EntityReference appRef = appHelper.createNewApp(
mkName(stem, "previewUpdatesApp"),
ouIds.root,
app1AssetCode);
ratingSchemeHelper.saveRatingItem(category.ratingSchemeId(), mkName(stem, "Rating1"), 0, "#111", "R", mkName(stem, "R"));

LOG.info("Create an existing rating for the app");
measurableRatingHelper.saveRatingItem(appRef, measurable1Id, "R", "user");

BulkMeasurableRatingValidationResult result = bulkMeasurableRatingService.bulkPreview(
category.entityReference(),
mkComprehensiveTsv(app1AssetCode, "CT-002", "CT-001"),
BulkMeasurableItemParser.InputFormat.TSV,
BulkUpdateMode.ADD_ONLY);

assertNotNull(result, "Expected a result");
assertEquals(3, result.validatedItems().size(), "Only one parsed row expected");
assertEquals(
asList(ChangeOperation.ADD, ChangeOperation.NONE, ChangeOperation.UPDATE),
map(result.validatedItems(), BulkMeasurableRatingValidatedItem::changeOperation));
assertEquals(
asList(emptySet(), asSet(ValidationError.APPLICATION_NOT_FOUND), emptySet()),
map(result.validatedItems(), BulkMeasurableRatingValidatedItem::errors));
}



@Test
public void previewUpdatesError() {
MeasurableCategory category = measurableCategoryDao.getById(setupCategory());
measurableHelper.createMeasurable("CT-001", "M1", category.id().get());
String app1AssetCode = mkName("assetCode1", "previewUpdates");
EntityReference app1Ref = appHelper.createNewApp(
mkName("app1", "previewUpdates"),
ouIds.root,
app1AssetCode);
ratingSchemeHelper.saveRatingItem(category.ratingSchemeId(), "Rating1", 0, "#111", "R");
BulkMeasurableRatingValidationResult result = bulkMeasurableRatingService.bulkPreview(
category.entityReference(),
mkBadRefsTsv(app1AssetCode, "CT-001", 'R'),
BulkMeasurableItemParser.InputFormat.TSV,
BulkUpdateMode.ADD_ONLY);

assertNotNull(result, "Expected a result");

result
.validatedItems()
.forEach(d -> {
if (d.parsedItem().assetCode().equals("badApp")) {
assertTrue(d.errors().contains(ValidationError.APPLICATION_NOT_FOUND), "Should be complaining about the bad app (assetCode)");
}
if (d.parsedItem().taxonomyExternalId().equals("badMeasurable")) {
assertTrue(d.errors().contains(ValidationError.MEASURABLE_NOT_FOUND), "Should be complaining about the bad measurable ref (taxonomyExtId)");
}
if (d.parsedItem().ratingCode() == '_') {
assertTrue(d.errors().contains(ValidationError.RATING_NOT_FOUND), "Should be complaining about the bad rating code");
}
});

assertEquals(5, result.validatedItems().size(), "Expected 5 items");
assertTrue(CollectionUtilities.all(result.validatedItems(), d -> ! d.errors().isEmpty()));
}



// --- helpers -------------------


private long setupCategory() {
String categoryName = mkName(stem, "category");
return measurableHelper.createMeasurableCategory(categoryName);
}


private String mkGoodTsv(String assetCode) {
return "assetCode\ttaxonomyExternalId\tratingCode\tisPrimary\tcomment\n"
+ assetCode + "\tCT-001\tR\ttrue\tcomment\n";
}


private void assertTaxonomyExternalIdMatch(BulkMeasurableRatingValidationResult result,
List<String> taxonomyExternalId) {
assertEquals(
taxonomyExternalId,
map(result.validatedItems(), d -> d.parsedItem().taxonomyExternalId()),
"Expected taxonomyExternalId do not match");
}


private void assertNoErrors(BulkMeasurableRatingValidationResult result) {
assertTrue(
all(result.validatedItems(), d -> isEmpty(d.errors())),
"Should have no errors");
}


private String mkBadRefsTsv(String goodApp,
String goodMeasurable,
char goodRating) {
return "assetCode\ttaxonomyExternalId\tratingCode\tisPrimary\tcomment\n" +
format("%s\t%s\t%s\ttrue\tcomment\n", goodApp, "badMeasurable", goodRating) +
format("%s\t%s\t%s\ttrue\tcomment\n", "badApp", goodMeasurable, goodRating) +
format("%s\t%s\t%s\ttrue\tcomment\n", goodApp, goodMeasurable, '_') +
format("%s\t%s\t%s\ttrue\tcomment\n", goodApp, "badMeasurable", '_') +
format("%s\t%s\t%s\ttrue\tcomment\n", "badApp", "badMeasurable", '_');
}


private String mkComprehensiveTsv(String goodApp,
String goodMeasurable,
String existingMeasurable) {
return "assetCode\ttaxonomyExternalId\tratingCode\tisPrimary\tcomment\n" +
format("%s\t%s\t%s\ttrue\tcomment\n", goodApp, goodMeasurable, 'R') + // should be an 'add'
format("%s\t%s\t%s\ttrue\tcomment\n", "badApp", goodMeasurable, 'R') + // should be 'none' (with errors)
format("%s\t%s\t%s\ttrue\tcomment\n", goodApp, existingMeasurable, 'R'); // should be 'update'
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.finos.waltz.model.bulk_upload.measurable_rating;

import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.immutables.value.Value;

@Value.Immutable
@JsonSerialize(as = ImmutableBulkMeasurableRatingApplyResult.class)
public interface BulkMeasurableRatingApplyResult {

int recordsAdded();
int recordsUpdated();
int recordsRemoved();

int skippedRows();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.finos.waltz.model.bulk_upload.measurable_rating;

import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import org.finos.waltz.model.Nullable;
import org.immutables.value.Value;

@Value.Immutable
@JsonDeserialize(as = ImmutableBulkMeasurableRatingItem.class)
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonFormat(with = JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)
public interface BulkMeasurableRatingItem {

@JsonAlias({"nar_id", "application_id", "asset_code"})
String assetCode();

@JsonAlias({"taxonomy_external_id", "external_id", "ext_id", "extId"})
String taxonomyExternalId();

char ratingCode();

@Value.Default
default boolean isPrimary() { return false; }

@Nullable
String comment();
}
Loading

0 comments on commit 7de1840

Please sign in to comment.