Skip to content

Commit

Permalink
chore: Refactor fcli ssc appversion create copy options
Browse files Browse the repository at this point in the history
feat: `fcli ssc appversion create`: Allow for copying attributes & user access
  • Loading branch information
rsenden committed May 28, 2024
1 parent 318ca98 commit 667ba4f
Show file tree
Hide file tree
Showing 12 changed files with 280 additions and 211 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package com.fortify.cli.ssc.access_control.helper;

import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;

Expand Down Expand Up @@ -89,11 +90,20 @@ public final SSCAppVersionUserUpdateBuilder add(boolean allowMultipleMatches, St
return this;
}

public SSCAppVersionUserUpdateBuilder add(boolean allowMultipleMatches, Collection<String> authEntitySpecs) {
if ( authEntitySpecs!=null && !authEntitySpecs.isEmpty() ) {
add(allowMultipleMatches, authEntitySpecs.toArray(String[]::new));
}
return this;
}

public final SSCAppVersionUserUpdateBuilder remove(boolean allowMultipleMatches, String... authEntitySpecs) {
this.allowMultipleMatchesForRemove |= allowMultipleMatches;
if ( authEntitySpecs!=null && authEntitySpecs.length>0 ) {
authEntitySpecsToRemove.addAll(Arrays.asList(authEntitySpecs));
}
return this;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,15 @@
*******************************************************************************/
package com.fortify.cli.ssc.appversion.cli.cmd;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand All @@ -31,17 +39,15 @@
import com.fortify.cli.ssc.app.helper.SSCAppHelper;
import com.fortify.cli.ssc.appversion.cli.mixin.SSCAppAndVersionNameResolverMixin;
import com.fortify.cli.ssc.appversion.cli.mixin.SSCAppVersionCopyFromMixin;
import com.fortify.cli.ssc.appversion.cli.mixin.SSCAppVersionRefreshOptions;
import com.fortify.cli.ssc.appversion.cli.mixin.SSCAppVersionCopyFromMixin.SSCAppVersionCopyFromDescriptor;
import com.fortify.cli.ssc.appversion.cli.mixin.SSCAppVersionCopyFromMixin.SSCAppVersionCopyOption;
import com.fortify.cli.ssc.appversion.helper.SSCAppAndVersionNameDescriptor;
import com.fortify.cli.ssc.appversion.helper.SSCAppVersionCreateCopyFromBuilder;
import com.fortify.cli.ssc.appversion.helper.SSCAppVersionDescriptor;
import com.fortify.cli.ssc.appversion.helper.SSCAppVersionHelper;
import com.fortify.cli.ssc.attribute.cli.mixin.SSCAttributeUpdateMixin;
import com.fortify.cli.ssc.attribute.helper.SSCAttributeUpdateBuilder;
import com.fortify.cli.ssc.issue.cli.mixin.SSCIssueTemplateResolverMixin;
import com.fortify.cli.ssc.issue.helper.SSCIssueTemplateDescriptor;
import com.fortify.cli.ssc.system_state.helper.SSCJobDescriptor;
import com.fortify.cli.ssc.system_state.helper.SSCJobHelper;
import com.fortify.cli.ssc.issue.helper.SSCIssueTemplateHelper;

import kong.unirest.HttpRequest;
import kong.unirest.UnirestInstance;
Expand All @@ -59,7 +65,6 @@ public class SSCAppVersionCreateCommand extends AbstractSSCJsonNodeOutputCommand
@Mixin private SSCAttributeUpdateMixin.OptionalAttrOption attrUpdateMixin;
@Mixin private SSCAppVersionUserMixin.OptionalUserAddOption userAddMixin;
@Mixin private SSCAppVersionCopyFromMixin copyFromMixin;
@Mixin private SSCAppVersionRefreshOptions refreshOptions;
@Option(names={"--description","-d"}, required = false)
private String description;
@Option(names={"--active"}, required = false, defaultValue="true", arity="1")
Expand All @@ -72,22 +77,21 @@ public class SSCAppVersionCreateCommand extends AbstractSSCJsonNodeOutputCommand
@Override
public JsonNode getJsonNode(UnirestInstance unirest) {
if ( skipIfExists ) {
SSCAppVersionDescriptor descriptor = SSCAppVersionHelper.getOptionalAppVersionFromAppAndVersionName(unirest, sscAppAndVersionNameResolver.getAppAndVersionNameDescriptor());
if ( descriptor!=null ) { return descriptor.asObjectNode().put(IActionCommandResultSupplier.actionFieldName, "SKIPPED_EXISTING"); }
var existingDescriptor = SSCAppVersionHelper.getOptionalAppVersionFromAppAndVersionName(unirest, sscAppAndVersionNameResolver.getAppAndVersionNameDescriptor());
if ( existingDescriptor!=null ) { return existingDescriptor.asObjectNode().put(IActionCommandResultSupplier.actionFieldName, "SKIPPED_EXISTING"); }
}
SSCAttributeUpdateBuilder attrUpdateBuilder = getAttrUpdateBuilder(unirest);
SSCAppVersionUserUpdateBuilder authUpdateBuilder = getAuthUpdateBuilder(unirest);

SSCAppVersionCreateCopyFromBuilder copyFromBuilder = getCopyFromBuilder(unirest);
var copyFromDescriptor = copyFromMixin.getCopyFromDescriptor(unirest);
var attrUpdateBuilder = getAttrUpdateBuilder(unirest, copyFromDescriptor);
var authUpdateBuilder = getAuthUpdateBuilder(unirest, copyFromDescriptor);

SSCAppVersionDescriptor descriptor = createUncommittedAppVersion(unirest);
var descriptor = createUncommittedAppVersion(unirest, copyFromDescriptor);

SSCBulkResponse bulkResponse = new SSCBulkRequestBuilder()
.request("attrUpdate", attrUpdateBuilder.buildRequest(descriptor.getVersionId()))
.request("userUpdate", authUpdateBuilder.buildRequest(descriptor.getVersionId()))
.request("copyFrom", copyFromBuilder.buildCopyFromPartialRequest(descriptor.getVersionId()))
.request("copyFrom", buildCopyFromPartialRequest(unirest, descriptor, copyFromDescriptor))
.request("commit", getCommitRequest(unirest, descriptor))
.request("copyState", copyFromBuilder.buildCopyStateRequest(descriptor.getVersionId()))
.request("copyState", buildCopyStateRequest(unirest, descriptor, copyFromDescriptor))
.request("updatedVersion", unirest.get(SSCUrls.PROJECT_VERSION(descriptor.getVersionId())))
.execute(unirest);
return bulkResponse.body("updatedVersion");
Expand All @@ -108,68 +112,95 @@ public boolean isSingular() {
return true;
}

private final SSCAppVersionCreateCopyFromBuilder getCopyFromBuilder(UnirestInstance unirest) {
SSCAppVersionCreateCopyFromBuilder builder = new SSCAppVersionCreateCopyFromBuilder(unirest);
if(copyFromMixin.isCopyRequested()) {
SSCAppVersionDescriptor fromAppVersionDesc = SSCAppVersionHelper.getRequiredAppVersion(unirest, copyFromMixin.getAppVersionNameOrId(), sscAppAndVersionNameResolver.getDelimiter());

builder .setCopyRequested(true)
.setCopyFrom(fromAppVersionDesc)
.setCopyOptions(copyFromMixin.getCopyOptions());

// refreshMetrics if the source PV is required to fully copy the tags, audit or comments
if(builder.copyStateEnabled()
&& refreshOptions.isRefresh()
&& fromAppVersionDesc.isRefreshRequired()){
SSCJobDescriptor refreshJobDesc = SSCAppVersionHelper.refreshMetrics(unirest, fromAppVersionDesc);
SSCJobHelper.waitForJob(unirest,refreshJobDesc);
}
}

return builder;
}

private final SSCAppVersionUserUpdateBuilder getAuthUpdateBuilder(UnirestInstance unirest) {
private final SSCAppVersionUserUpdateBuilder getAuthUpdateBuilder(UnirestInstance unirest, SSCAppVersionCopyFromDescriptor copyFromDescriptor) {
return new SSCAppVersionUserUpdateBuilder(unirest)
.add(false, getUsersFromSource(unirest, copyFromDescriptor))
.add(false, userAddMixin.getAuthEntitySpecs());
}

private Set<String> getUsersFromSource(UnirestInstance unirest, SSCAppVersionCopyFromDescriptor copyFromDescriptor) {
if ( copyFromDescriptor.isCopyRequested() && copyFromDescriptor.getCopyOptions().contains(SSCAppVersionCopyOption.Users) ) {
return getUsersSet(unirest, copyFromDescriptor.getAppVersionDescriptor());
}
return null;
}

public static final Set<String> getUsersSet(UnirestInstance unirest, SSCAppVersionDescriptor descriptor) {
Set<String> result = new LinkedHashSet<>();
var users = SSCAppVersionHelper.getUsers(unirest, descriptor);
for (JsonNode user : users) {
result.add(user.get("id").asText());
}
return result;
}

private final SSCAttributeUpdateBuilder getAttrUpdateBuilder(UnirestInstance unirest) {
Map<String, String> attributes = attrUpdateMixin.getAttributes();
private final SSCAttributeUpdateBuilder getAttrUpdateBuilder(UnirestInstance unirest, SSCAppVersionCopyFromDescriptor copyFromDescriptor) {
return new SSCAttributeUpdateBuilder(unirest)
.add(attributes)
.add(getAttributesFromSource(unirest, copyFromDescriptor))
.add(attrUpdateMixin.getAttributes())
.addRequiredAttrs(autoRequiredAttrs)
.checkRequiredAttrs(true)
.prepareAndCheckRequest();
}

private SSCAppVersionDescriptor createUncommittedAppVersion(UnirestInstance unirest) {
SSCIssueTemplateDescriptor issueTemplateDescriptor = issueTemplateResolver.getIssueTemplateDescriptorOrDefault(unirest);
SSCAppAndVersionNameDescriptor appAndVersionNameDescriptor = sscAppAndVersionNameResolver.getAppAndVersionNameDescriptor();

if ( issueTemplateDescriptor==null ) {
throw new IllegalArgumentException("--issue-template is required, as no default template is configured on SSC");
private Map<String, String> getAttributesFromSource(UnirestInstance unirest, SSCAppVersionCopyFromDescriptor copyFromDescriptor) {
if ( copyFromDescriptor.isCopyRequested() && copyFromDescriptor.getCopyOptions().contains(SSCAppVersionCopyOption.Attributes) ) {
return getAttributesMap(unirest, copyFromDescriptor.getAppVersionDescriptor());
}
return null;
}

public static final Map<String,String> getAttributesMap(UnirestInstance unirest, SSCAppVersionDescriptor descriptor) {
var result = new LinkedHashMap<String,String>();
var attributes = SSCAppVersionHelper.getAttributes(unirest, descriptor);
for (JsonNode attr : attributes) {
List<String> values = new ArrayList<>();
for (JsonNode value: attr.get("values")) {
values.add(value.get("guid").textValue());
}
result.put(attr.get("attributeDefinitionId").toString(), String.join(";", values));
}
return result;
}

private SSCAppVersionDescriptor createUncommittedAppVersion(UnirestInstance unirest, SSCAppVersionCopyFromDescriptor copyFromDescriptor) {
String issueTemplateId = getIssueTemplateId(unirest, copyFromDescriptor);
SSCAppAndVersionNameDescriptor appAndVersionNameDescriptor = sscAppAndVersionNameResolver.getAppAndVersionNameDescriptor();
var description = this.description;
if ( StringUtils.isBlank(description) && copyFromDescriptor.isCopyRequested() ) {
description = String.format("Copied from "+copyFromDescriptor.getAppVersionDescriptor().getAppAndVersionName());
}
ObjectNode body = objectMapper.createObjectNode();
body.put("name", appAndVersionNameDescriptor.getVersionName())
.put("description", description==null ? "" : description)
.put("active", active)
.put("committed", false)
.put("issueTemplateId", issueTemplateDescriptor.getId())
.set("project", getProjectNode(unirest, appAndVersionNameDescriptor.getAppName(), issueTemplateDescriptor));
.put("issueTemplateId", issueTemplateId)
.set("project", getProjectNode(unirest, appAndVersionNameDescriptor.getAppName(), issueTemplateId));
JsonNode response = unirest.post(SSCUrls.PROJECT_VERSIONS).body(body).asObject(JsonNode.class).getBody().get("data");
return JsonHelper.treeToValue(response, SSCAppVersionDescriptor.class);
}

private JsonNode getProjectNode(UnirestInstance unirest, String appName, SSCIssueTemplateDescriptor issueTemplateDescriptor) {
private String getIssueTemplateId(UnirestInstance unirest, SSCAppVersionCopyFromDescriptor copyFromDescriptor) {
var issueTemplateNameOrId = issueTemplateResolver.getIssueTemplateNameOrId();
if ( StringUtils.isBlank(issueTemplateNameOrId) && copyFromDescriptor.isCopyRequested() ) {
issueTemplateNameOrId = copyFromDescriptor.getAppVersionDescriptor().getIssueTemplateId();
}
var issueTemplateDescriptor = new SSCIssueTemplateHelper(unirest).getIssueTemplateDescriptorOrDefault(issueTemplateNameOrId);
if ( issueTemplateDescriptor==null ) {
throw new IllegalArgumentException("--issue-template is required, as no default template is configured on SSC");
}
return issueTemplateDescriptor.getId();
}

private JsonNode getProjectNode(UnirestInstance unirest, String appName, String issueTemplateId) {
SSCAppDescriptor appDescriptor = SSCAppHelper.getApp(unirest, appName, false, "id");
if ( appDescriptor!=null ) {
return appDescriptor.asJsonNode();
} else {
ObjectNode appNode = new ObjectMapper().createObjectNode();
appNode.put("name", appName);
appNode.put("issueTemplateId", issueTemplateDescriptor.getId());
appNode.put("issueTemplateId", issueTemplateId);
return appNode;
}
}
Expand All @@ -178,4 +209,34 @@ private final HttpRequest<?> getCommitRequest(UnirestInstance unirest, SSCAppVer
ObjectNode body = objectMapper.createObjectNode().put("committed", true);
return unirest.put(SSCUrls.PROJECT_VERSION(descriptor.getVersionId())).body(body);
}

private HttpRequest<?> buildCopyFromPartialRequest(UnirestInstance unirest, SSCAppVersionDescriptor copyTo, SSCAppVersionCopyFromDescriptor copyFromDescriptor) {
if ( !copyFromDescriptor.isCopyRequested() ) { return null; }
var properties = copyFromDescriptor.getCopyOptions().stream()
.map(SSCAppVersionCopyOption::getCopyFromPartialProperty)
.filter(Objects::nonNull)
.toList();
if ( properties.isEmpty() ) { return null; }
var body = buildCopyFromAppVersionIdsBody(copyTo, copyFromDescriptor);
properties.forEach(p->body.put(p, true));
return unirest.post(SSCUrls.PROJECT_VERSIONS_ACTION_COPY_FROM_PARTIAL)
.body(body);
}

private HttpRequest<?> buildCopyStateRequest(UnirestInstance unirest, SSCAppVersionDescriptor copyTo, SSCAppVersionCopyFromDescriptor copyFromDescriptor) {
if ( !copyFromDescriptor.isCopyRequested()
|| !copyFromDescriptor.getCopyOptions().contains(SSCAppVersionCopyOption.State)) {
return null;
}
return unirest.post(SSCUrls.PROJECT_VERSIONS_ACTION_COPY_CURRENT_STATE)
.body(buildCopyFromAppVersionIdsBody(copyTo, copyFromDescriptor));
}

private ObjectNode buildCopyFromAppVersionIdsBody(SSCAppVersionDescriptor copyTo, SSCAppVersionCopyFromDescriptor copyFromDescriptor) {
var copyFrom = copyFromDescriptor.getAppVersionDescriptor();
return JsonHelper.getObjectMapper().createObjectNode()
.put("projectVersionId", copyTo.getVersionId())
.put("previousProjectVersionId", copyFrom.getVersionId());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Copyright 2023 Open Text.
*
* The only warranties for products and services of Open Text
* and its affiliates and licensors ("Open Text") are as may
* be set forth in the express warranty statements accompanying
* such products and services. Nothing herein should be construed
* as constituting an additional warranty. Open Text shall not be
* liable for technical or editorial errors or omissions contained
* herein. The information contained herein is subject to change
* without notice.
*/
package com.fortify.cli.ssc.appversion.cli.mixin;

public interface ISSCDelimiterMixinAware {
void setDelimiterMixin(SSCDelimiterMixin delimiterMixin);
}
Loading

0 comments on commit 667ba4f

Please sign in to comment.