Skip to content

Commit

Permalink
Merge pull request finos#7187 from deutschebank/db-contrib/7182-admin…
Browse files Browse the repository at this point in the history
…-toggle-read-only-on-logical-flows

Db contrib/7182 admin toggle read only on logical flows
  • Loading branch information
davidwatkins73 authored Nov 13, 2024
2 parents 906f6a1 + b49ac37 commit 06a2527
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,16 @@ public LogicalFlow getByFlowId(long dataFlowId) {
.fetchOne(TO_DOMAIN_MAPPER);
}

public long updateReadOnly(long flowId, boolean isReadOnly, String user) {
return dsl
.update(LOGICAL_FLOW)
.set(LOGICAL_FLOW.IS_READONLY, isReadOnly)
.set(LOGICAL_FLOW.LAST_UPDATED_AT, Timestamp.valueOf(nowUtc()))
.set(LOGICAL_FLOW.LAST_UPDATED_BY, user)
.where(LOGICAL_FLOW.ID.eq(flowId))
.execute();
}


public List<LogicalFlow> findAllActive() {
return baseQuery()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -430,4 +430,27 @@ public void findUpstreamFlowsForEntityReferences() {
assertEquals(3, allUpstreams.size(), "Returns all upstreams but not downstreams");
}

@Test
public void updateReadOnlyTest() {
helper.clearAllFlows();

EntityReference a = appHelper.createNewApp("xyz-app", ouIds.a);
EntityReference b = appHelper.createNewApp("xyz-app-2", ouIds.a);

LogicalFlow logicalFlow = helper.createLogicalFlow(a, b);

LogicalFlow updatedFlow = lfSvc.updateReadOnly(logicalFlow.id().get(), true, "updateTestUser");
assertTrue(updatedFlow.isReadOnly());

updatedFlow = lfSvc.updateReadOnly(logicalFlow.id().get(), false, "updateTestUser");
assertFalse(updatedFlow.isReadOnly());

updatedFlow = lfSvc.updateReadOnly(122, true, "updateTestUser");
assertNull(updatedFlow);


updatedFlow = lfSvc.updateReadOnly(logicalFlow.id().get(), false, "updateTestUser");
assertFalse(updatedFlow.isReadOnly());
}

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

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.finos.waltz.model.command.Command;
import org.immutables.value.Value;

@Value.Immutable
@JsonSerialize(as = ImmutableUpdateReadOnlyCommand.class)
@JsonDeserialize(as = ImmutableUpdateReadOnlyCommand.class)
public interface UpdateReadOnlyCommand extends Command {
boolean readOnly();
}
13 changes: 13 additions & 0 deletions waltz-ng/client/logical-flow/pages/view/logical-flow-view.html
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,19 @@
</waltz-svelte-component>
</div>
</div>
<div class="row">
<div waltz-has-role="ADMIN">
<div class="col-sm-4 waltz-display-field-label">
Toggle Read Only
</div>
<div class="col-sm-8">
<waltz-toggle state="ctrl.isReadOnly"
label-on="Read Only"
label-off="Editable"
on-toggle="ctrl.onToggleReadOnly()"></waltz-toggle>
</div>
</div>
</div>
</div>

<div class="col-md-6">
Expand Down
24 changes: 23 additions & 1 deletion waltz-ng/client/logical-flow/pages/view/logical-flow-view.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@ const initialState = {
canEdit: false,
canRestore: false,
canRemove: false,
updateCommand: {
readOnly: false,
},
AlignedDataTypesList
};


function controller($q,
$state,
$stateParams,
Expand Down Expand Up @@ -91,6 +93,22 @@ function controller($q,

};

const onToggleReadOnly = () => {
const changedField = !vm.logicalFlow.isReadOnly;
vm.updateCommand.readOnly = changedField;
return serviceBroker
.execute(CORE_API.LogicalFlowStore.updateReadOnly, [vm.updateCommand, vm.logicalFlow.id])
.then(() => {
toasts.success("Successfully made the flow " + (changedField ? `read only` : `editable`) + '.');
setTimeout(() => {
$window.location.reload();
}, 600);
})
.catch(e => {
toasts.error(e.data.message);
});
}

const removeLogicalFlow = () => {
return serviceBroker
.execute(CORE_API.LogicalFlowStore.removeFlow, [vm.logicalFlow.id])
Expand Down Expand Up @@ -134,6 +152,10 @@ function controller($q,
}
};

vm.onToggleReadOnly = () => {
onToggleReadOnly();
}

vm.restoreFlow = () => {
if (confirm("Are you sure you want to restore this flow ?")) {
console.log("restoring", vm.logicalFlow);
Expand Down
12 changes: 11 additions & 1 deletion waltz-ng/client/logical-flow/services/logical-flow-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ export function store($http, BaseApiUrl) {
.get(`${BASE}/cleanup-self-references`)
.then(r => r.data);

const updateReadOnly = (updateReadOnlyCmd, id) => $http
.post(`${BASE}/update/read-only/${id}`, updateReadOnlyCmd)
.then(r => r.data);

return {
findBySelector,
findByIds,
Expand All @@ -144,7 +148,8 @@ export function store($http, BaseApiUrl) {
findPermissionsForParentRef,
findPermissionsForFlow,
cleanupOrphans,
cleanupSelfReferences
cleanupSelfReferences,
updateReadOnly
};
}

Expand Down Expand Up @@ -252,6 +257,11 @@ export const LogicalFlowStore_API = {
serviceFnName: "cleanupSelfReferences",
description: "mark flows as removed where the flow source and target are the same"
},
updateReadOnly: {
serviceName,
serviceFnName: "updateReadOnly",
description: "update whether a logical flow is read only or editable"
},
};


Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ public LogicalFlow getByExternalId(String externalId) {
* <li>DATA_TYPE</li>
* <li>APPLICATION</li>
* </ul>
*
* @param options given to logical flow selector factory to determine in-scope flows
* @return a list of logical flows matching the given options
*/
Expand All @@ -233,10 +234,10 @@ public List<LogicalFlow> findBySelector(IdSelectionOptions options) {
/**
* Creates a logical flow and creates a default, 'UNKNOWN' data type decoration
* if possible.
*
* <p>
* If the flow already exists, but is inactive, the flow will be re-activated.
*
* @param addCmd Command object containing flow details
* @param addCmd Command object containing flow details
* @param username who is creating the flow
* @return the newly created logical flow
* @throws IllegalArgumentException if a flow already exists
Expand Down Expand Up @@ -316,22 +317,54 @@ private void rejectIfSelfLoop(AddLogicalFlowCommand addCmd) {
}
}

public LogicalFlow updateReadOnly(long flowId, boolean isReadOnly, String user) {
LogicalFlow logicalFlow = getById(flowId);
LocalDateTime now = nowUtc();

if (logicalFlow != null) {
// if the flag is being set to what it already was -> should not happen but just in case
if (isReadOnly == logicalFlow.isReadOnly()) {
return logicalFlow;
} else {
// update the read only flag to what you want
logicalFlowDao.updateReadOnly(flowId, isReadOnly, user);
LogicalFlow updatedFlow = getById(logicalFlow.id().get());

ChangeLog changeLog = ImmutableChangeLog
.builder()
.parentReference(EntityReference.mkRef(LOGICAL_DATA_FLOW, logicalFlow.id().get()))
.operation(Operation.UPDATE)
.createdAt(now)
.userId(user)
.message(isReadOnly
? format("Set to read only by waltz_support.")
: format("Set to editable by waltz_support."))
.build();
changeLogService.write(changeLog);
return updatedFlow;
}
}

// return null if the flow was not found
return null;
}


/**
* Removes the given logical flow and creates an audit log entry.
* The removal is a soft removal. After the removal usage stats are recalculated
*
* <p>
* todo: #WALTZ-1894 for cleanupOrphans task
*
* @param flowId identifier of flow to be removed
* @param username who initiated the removal
* @param flowId identifier of flow to be removed
* @param username who initiated the removal
* @return number of flows removed
*/
public int removeFlow(Long flowId, String username) {

LogicalFlow logicalFlow = logicalFlowDao.getByFlowId(flowId);

if(logicalFlow == null){
if (logicalFlow == null) {
LOG.warn("Logical flow cannot be found, no flows will be updated");
throw new IllegalArgumentException(format("Cannot find flow with id: %d, no logical flow removed", flowId));
} else {
Expand All @@ -352,6 +385,7 @@ public int removeFlow(Long flowId, String username) {

/**
* Calculate Stats by selector
*
* @param options determines which flows are in-scope for this calculation
* @return statistics about the in-scope flows
*/
Expand All @@ -367,7 +401,7 @@ public LogicalFlowStatistics calculateStats(IdSelectionOptions options) {
case DATA_TYPE:
return calculateStatsForAppIdSelector(options);
default:
throw new UnsupportedOperationException("Cannot calculate stats for selector kind: "+ options.entityReference().kind());
throw new UnsupportedOperationException("Cannot calculate stats for selector kind: " + options.entityReference().kind());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import org.finos.waltz.service.taxonomy_management.BulkTaxonomyItemParser.InputFormat;
import org.finos.waltz.service.user.UserRoleService;
import org.jooq.DSLContext;
import org.jooq.DeleteConditionStep;
import org.jooq.UpdateConditionStep;
import org.jooq.UpdateSetStep;
import org.jooq.impl.DSL;
Expand Down Expand Up @@ -280,6 +281,17 @@ public BulkTaxonomyApplyResult applyBulk(EntityReference taxonomyRef,
})
.collect(Collectors.toSet());

Set<UpdateConditionStep<MeasurableRecord>> toRemove = bulkRequest
.plannedRemovals()
.stream()
.map(r -> {
UpdateSetStep<MeasurableRecord> updRemove = DSL.update(org.finos.waltz.schema.tables.Measurable.MEASURABLE);
return updRemove
.set(org.finos.waltz.schema.tables.Measurable.MEASURABLE.ENTITY_LIFECYCLE_STATUS, EntityLifecycleStatus.REMOVED.name())
.where(org.finos.waltz.schema.tables.Measurable.MEASURABLE.ID.eq(r.id().get()));
})
.collect(Collectors.toSet());


boolean requiresRebuild = requiresHierarchyRebuild(bulkRequest.validatedItems());

Expand All @@ -289,6 +301,7 @@ public BulkTaxonomyApplyResult applyBulk(EntityReference taxonomyRef,
int insertCount = summarizeResults(tx.batchInsert(toAdd).execute());
int restoreCount = summarizeResults(tx.batch(toRestore).execute());
int updateCount = summarizeResults(tx.batch(toUpdate).execute());
int removalCount = summarizeResults(tx.batch(toRemove).execute());

Set<TaxonomyChangeRecord> changeRecords = mkTaxonomyChangeRecords(tx, taxonomyRef, bulkRequest, userId);

Expand All @@ -306,7 +319,7 @@ public BulkTaxonomyApplyResult applyBulk(EntityReference taxonomyRef,
.recordsAdded(insertCount)
.recordsUpdated(updateCount)
.recordsRestored(restoreCount)
.recordsRemoved(0)//TODO: add removeCount
.recordsRemoved(removalCount)
.hierarchyRebuilt(requiresRebuild)
.build();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

package org.finos.waltz.web.endpoints.api;

import org.eclipse.jetty.util.IO;
import org.finos.waltz.common.exception.InsufficientPrivelegeException;
import org.finos.waltz.model.EntityKind;
import org.finos.waltz.model.IdSelectionOptions;
Expand All @@ -35,6 +36,7 @@
import org.finos.waltz.model.logical_flow.LogicalFlow;
import org.finos.waltz.model.logical_flow.LogicalFlowGraphSummary;
import org.finos.waltz.model.logical_flow.LogicalFlowStatistics;
import org.finos.waltz.model.logical_flow.UpdateReadOnlyCommand;
import org.finos.waltz.model.user.SystemRole;
import org.jooq.lambda.tuple.Tuple;
import org.jooq.lambda.tuple.Tuple2;
Expand Down Expand Up @@ -108,6 +110,7 @@ public void register() {
String addFlowsPath = mkPath(BASE_URL, "list");
String getFlowGraphSummaryPath = mkPath(BASE_URL, "entity", ":kind", ":id", "data-type", ":dtId", "graph-summary");
String getFlowViewPath = mkPath(BASE_URL, "view");
String updateReadOnlyPath = mkPath(BASE_URL, "update", "read-only", ":id");

ListRoute<LogicalFlow> getByEntityRef = (request, response)
-> logicalFlowService.findByEntityReference(getEntityReference(request));
Expand Down Expand Up @@ -177,6 +180,7 @@ public void register() {
postForList(addFlowsPath, this::addFlowsRoute);
putForDatum(restoreFlowPath, this::restoreFlowRoute);
postForDatum(getFlowViewPath, getFlowViewRoute);
postForDatum(updateReadOnlyPath, this::updateReadOnly);
}


Expand Down Expand Up @@ -286,4 +290,18 @@ private void ensureUserHasEditRights(EntityReference source, EntityReference tar
private void ensureUserHasAdminRights(Request request) {
requireRole(userRoleService, request, SystemRole.ADMIN);
}

private LogicalFlow updateReadOnly(Request request, Response response) throws IOException {
long flowId = getId(request);
String user = getUsername(request);
UpdateReadOnlyCommand cmd = readBody(request, UpdateReadOnlyCommand.class);

ensureUserHasAdminRights(request);
LogicalFlow resp = logicalFlowService.updateReadOnly(flowId, cmd.readOnly(), user);
if(resp == null) {
throw new IllegalArgumentException("No such Logical Flow exists");
}

return resp;
}
}

0 comments on commit 06a2527

Please sign in to comment.