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

Db contrib/7182 admin toggle read only on logical flows #7187

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;
}
}
Loading