Skip to content

Commit

Permalink
chore: Add support for copying SSC appversions/state
Browse files Browse the repository at this point in the history
feat: `fcli ssc appversion create`: Add options for copying existing application version

feat: Add `fcli ssc appversion copy-state` command

feat: Add `fcli system-state wait-for-job` command
  • Loading branch information
gendry-gh authored and rsenden committed Nov 15, 2023
1 parent 29e83a6 commit 75461db
Show file tree
Hide file tree
Showing 16 changed files with 544 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
import com.fortify.cli.common.util.StringUtils;
import com.fortify.cli.ssc.access_control.helper.SSCTokenCreateRequest;
import com.fortify.cli.ssc.access_control.helper.SSCTokenCreateResponse;
import com.fortify.cli.ssc.access_control.helper.SSCTokenHelper;
import com.fortify.cli.ssc.access_control.helper.SSCTokenCreateResponse.SSCTokenData;
import com.fortify.cli.ssc.access_control.helper.SSCTokenHelper;

import lombok.Data;
import lombok.EqualsAndHashCode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
subcommands = {
SSCAppVersionCreateCommand.class,
SSCAppVersionDeleteCommand.class,
SSCAppVersionCopyStateCommand.class,
SSCAppVersionDownloadStateCommand.class,
SSCAppVersionGetCommand.class,
SSCAppVersionListCommand.class,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*******************************************************************************
* Copyright 2021, 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.cmd;

import java.util.HashMap;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fortify.cli.common.output.cli.cmd.IJsonNodeSupplier;
import com.fortify.cli.common.output.cli.mixin.OutputHelperMixins;
import com.fortify.cli.common.output.transform.IActionCommandResultSupplier;
import com.fortify.cli.common.output.transform.IRecordTransformer;
import com.fortify.cli.ssc._common.output.cli.cmd.AbstractSSCJsonNodeOutputCommand;
import com.fortify.cli.ssc._common.rest.SSCUrls;
import com.fortify.cli.ssc.appversion.cli.mixin.SSCDelimiterMixin;
import com.fortify.cli.ssc.appversion.helper.SSCAppVersionDescriptor;
import com.fortify.cli.ssc.appversion.helper.SSCAppVersionHelper;

import kong.unirest.UnirestInstance;
import lombok.Getter;
import picocli.CommandLine.Command;
import picocli.CommandLine.Mixin;
import picocli.CommandLine.Option;

@Command(name = "copy-state")
public class SSCAppVersionCopyStateCommand extends AbstractSSCJsonNodeOutputCommand implements IJsonNodeSupplier, IRecordTransformer, IActionCommandResultSupplier {
@Getter
@Mixin
private OutputHelperMixins.TableNoQuery outputHelper;

@Getter
@Mixin
private SSCDelimiterMixin delimiterMixin;

@Option(names = {"--copy-from", "--from"}, required = true, descriptionKey = "fcli.ssc.appversion.resolver.copy-from.nameOrId")
@Getter
private String fromAppVersionNameOrId;

@Option(names = {"--copy-to", "--to"}, required = true, descriptionKey = "fcli.ssc.appversion.resolver.copy-to.nameOrId")
@Getter
private String toAppVersionNameOrId;

@Override
public JsonNode getJsonNode(UnirestInstance unirest) {
ObjectMapper mapper = new ObjectMapper();
SSCAppVersionDescriptor fromAppVersionDescriptor = SSCAppVersionHelper.getRequiredAppVersion(unirest, getFromAppVersionNameOrId(), delimiterMixin.getDelimiter());
SSCAppVersionDescriptor toAppVersionDescriptor = SSCAppVersionHelper.getRequiredAppVersion(unirest, getToAppVersionNameOrId(), delimiterMixin.getDelimiter());

return mapper.valueToTree(copyState(unirest, fromAppVersionDescriptor, toAppVersionDescriptor));

}

@Override
public JsonNode transformRecord(JsonNode record) {
return SSCAppVersionHelper.renameFields(record);
}

@Override
public String getActionCommandResult() {
return "COPY_REQUESTED";
}

@Override
public boolean isSingular() {
return true;
}

private static final HashMap<String, String> copyState(UnirestInstance unirest, SSCAppVersionDescriptor sourceAppVersion, SSCAppVersionDescriptor targetAppVersion) {
HashMap<String, String> appVersionIds = new HashMap<String, String>();
appVersionIds.put("previousProjectVersionId", sourceAppVersion.getVersionId());
appVersionIds.put("projectVersionId", targetAppVersion.getVersionId());

unirest.post(SSCUrls.PROJECT_VERSIONS_ACTION_COPY_CURRENT_STATE)
.body(appVersionIds)
.asObject(JsonNode.class).getBody();

return appVersionIds;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
import com.fortify.cli.ssc.app.helper.SSCAppDescriptor;
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.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;
Expand All @@ -47,7 +49,7 @@

@Command(name = OutputHelperMixins.Create.CMD_NAME)
public class SSCAppVersionCreateCommand extends AbstractSSCJsonNodeOutputCommand implements IRecordTransformer, IActionCommandResultSupplier {
@Getter @Mixin private OutputHelperMixins.Create outputHelper;
@Getter @Mixin private OutputHelperMixins.Create outputHelper;
private final ObjectMapper objectMapper = new ObjectMapper();
@Mixin private SSCAppAndVersionNameResolverMixin.PositionalParameter sscAppAndVersionNameResolver;
@Mixin private SSCIssueTemplateResolverMixin.OptionalOption issueTemplateResolver;
Expand All @@ -57,11 +59,11 @@ public class SSCAppVersionCreateCommand extends AbstractSSCJsonNodeOutputCommand
private String description;
@Option(names={"--active"}, required = false, defaultValue="true", arity="1")
private boolean active;
@Mixin private SSCAppVersionCopyFromMixin copyFromMixin;
@Option(names={"--auto-required-attrs"}, required = false)
private boolean autoRequiredAttrs = false;
@Option(names={"--skip-if-exists"}, required = false)
private boolean skipIfExists = false;


@Override
public JsonNode getJsonNode(UnirestInstance unirest) {
Expand All @@ -71,37 +73,51 @@ public JsonNode getJsonNode(UnirestInstance unirest) {
}
SSCAttributeUpdateBuilder attrUpdateBuilder = getAttrUpdateBuilder(unirest);
SSCAppVersionUserUpdateBuilder authUpdateBuilder = getAuthUpdateBuilder(unirest);

SSCAppVersionCreateCopyFromBuilder copyFromBuilder = getCopyFromBuilder(unirest);

SSCAppVersionDescriptor descriptor = createUncommittedAppVersion(unirest);
SSCBulkResponse bulkResponse = new SSCBulkRequestBuilder()
.request("attrUpdate", attrUpdateBuilder.buildRequest(descriptor.getVersionId()))
.request("userUpdate", authUpdateBuilder.buildRequest(descriptor.getVersionId()))
.request("copyFrom", copyFromBuilder.buildCopyFromPartialRequest(descriptor.getVersionId()))
.request("commit", getCommitRequest(unirest, descriptor))
.request("copyState", copyFromBuilder.buildCopyStateRequest(descriptor.getVersionId()))
.request("updatedVersion", unirest.get(SSCUrls.PROJECT_VERSION(descriptor.getVersionId())))
.execute(unirest);
return bulkResponse.body("updatedVersion");
}

@Override
public JsonNode transformRecord(JsonNode input) {
return SSCAppVersionHelper.renameFields(input);
}

@Override
public String getActionCommandResult() {
return "CREATED";
}

@Override
public boolean isSingular() {
return true;
}


private final SSCAppVersionCreateCopyFromBuilder getCopyFromBuilder(UnirestInstance unirest) {
SSCAppVersionCreateCopyFromBuilder builder = new SSCAppVersionCreateCopyFromBuilder(unirest);
if(copyFromMixin.isCopyRequested()) {
builder .setCopyRequested(true)
.setCopyFrom(SSCAppVersionHelper.getRequiredAppVersion(unirest, copyFromMixin.getAppVersionNameOrId(), sscAppAndVersionNameResolver.getDelimiter()))
.setCopyOptions(copyFromMixin.getCopyOptions());
}

return builder;
}

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

private final SSCAttributeUpdateBuilder getAttrUpdateBuilder(UnirestInstance unirest) {
Map<String, String> attributes = attrUpdateMixin.getAttributes();
return new SSCAttributeUpdateBuilder(unirest)
Expand All @@ -114,11 +130,11 @@ private final SSCAttributeUpdateBuilder getAttrUpdateBuilder(UnirestInstance uni
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");
}

ObjectNode body = objectMapper.createObjectNode();
body.put("name", appAndVersionNameDescriptor.getVersionName())
.put("description", description==null ? "" : description)
Expand All @@ -129,7 +145,7 @@ private SSCAppVersionDescriptor createUncommittedAppVersion(UnirestInstance unir
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) {
SSCAppDescriptor appDescriptor = SSCAppHelper.getApp(unirest, appName, false, "id");
if ( appDescriptor!=null ) {
Expand All @@ -141,7 +157,7 @@ private JsonNode getProjectNode(UnirestInstance unirest, String appName, SSCIssu
return appNode;
}
}

private final HttpRequest<?> getCommitRequest(UnirestInstance unirest, SSCAppVersionDescriptor descriptor) {
ObjectNode body = objectMapper.createObjectNode().put("committed", true);
return unirest.put(SSCUrls.PROJECT_VERSION(descriptor.getVersionId())).body(body);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,20 @@
import com.fortify.cli.common.output.cli.mixin.OutputHelperMixins;
import com.fortify.cli.common.output.transform.IActionCommandResultSupplier;
import com.fortify.cli.common.output.transform.IRecordTransformer;
import com.fortify.cli.common.variable.DefaultVariablePropertyName;
import com.fortify.cli.ssc._common.output.cli.cmd.AbstractSSCJsonNodeOutputCommand;
import com.fortify.cli.ssc.appversion.cli.mixin.SSCAppVersionBulkEmbedMixin;
import com.fortify.cli.ssc.appversion.cli.mixin.SSCAppVersionResolverMixin;
import com.fortify.cli.ssc.appversion.helper.SSCAppVersionHelper;
import com.fortify.cli.ssc.system_state.helper.SSCJobDescriptor;

import kong.unirest.UnirestInstance;
import lombok.Getter;
import picocli.CommandLine.Command;
import picocli.CommandLine.Mixin;

@Command(name = "refresh-metrics")
@DefaultVariablePropertyName("job.jobName")
public class SSCAppVersionRefreshMetricsCommand extends AbstractSSCJsonNodeOutputCommand implements IRecordTransformer, IActionCommandResultSupplier {
@Getter @Mixin private OutputHelperMixins.TableNoQuery outputHelper;
@Mixin private SSCAppVersionResolverMixin.PositionalParameter appVersionResolver;
Expand All @@ -35,8 +38,14 @@ public class SSCAppVersionRefreshMetricsCommand extends AbstractSSCJsonNodeOutpu
@Override
public JsonNode getJsonNode(UnirestInstance unirest) {
var descriptor = appVersionResolver.getAppVersionDescriptor(unirest);
return descriptor.asObjectNode()
.put(IActionCommandResultSupplier.actionFieldName, SSCAppVersionHelper.refreshMetrics(unirest, descriptor));
SSCJobDescriptor refreshJobDescriptor = SSCAppVersionHelper.refreshMetrics(unirest, descriptor);
if(refreshJobDescriptor == null){
return descriptor.asObjectNode()
.put(IActionCommandResultSupplier.actionFieldName, "NO_REFRESH_REQUIRED");
} else {
return descriptor.asObjectNode().set("job", refreshJobDescriptor.asJsonNode());
}

}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*******************************************************************************
* Copyright 2021, 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;

import com.fortify.cli.common.util.DisableTest;
import com.fortify.cli.common.util.DisableTest.TestType;
import com.fortify.cli.ssc.appversion.helper.SSCAppVersionDescriptor;
import com.fortify.cli.ssc.appversion.helper.SSCAppVersionHelper;
import com.fortify.cli.ssc.appversion.helper.SSCAppVersionCopyType;

import kong.unirest.UnirestInstance;
import lombok.Getter;
import picocli.CommandLine.ArgGroup;
import picocli.CommandLine.Option;

public class SSCAppVersionCopyFromMixin {

@ArgGroup(exclusive=false, multiplicity = "0..1")
private SSCAppVersionCopyFromArgGroup argGroup;

public boolean isCopyRequested() { return argGroup!=null; }
public String getAppVersionNameOrId() {
return argGroup==null ? null : argGroup.getAppVersionNameOrId();
}

public final SSCAppVersionCopyType[] getCopyOptions(){
return this.argGroup.getCopyOptions();
}

public SSCAppVersionDescriptor getAppVersionDescriptor(UnirestInstance unirest,String delimiter, String... fields){
return SSCAppVersionHelper.getRequiredAppVersion(unirest, getAppVersionNameOrId(), delimiter, fields);
}

private static class SSCAppVersionCopyFromArgGroup {
@Option(names = {"--copy-from", "--from"}, required = true, descriptionKey = "fcli.ssc.appversion.resolver.copy-from.nameOrId")
@Getter private String appVersionNameOrId;
@DisableTest(TestType.MULTI_OPT_PLURAL_NAME)
@Option(names = {"--copy"}, required = false, split = ",", descriptionKey = "fcli.ssc.appversion.create.copy-options")
@Getter private SSCAppVersionCopyType[] copyOptions;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*******************************************************************************
* Copyright 2021, 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.helper;

import java.util.stream.Stream;

/**
* This enumeration defines the items that can be copied from an existing application version
*/
public enum SSCAppVersionCopyType {
AnalysisProcessingRules("copyAnalysisProcessingRules"),
BugTrackerConfiguration("copyBugTrackerConfiguration"),
CustomTags("copyCustomTags"),
State("copyState");

private final String sscValue;

private SSCAppVersionCopyType(String sscValue) {
this.sscValue = sscValue;
}

public String getSscValue() {
return this.sscValue;
}

public static final SSCAppVersionCopyType fromSscValue(String sscValue) {
return Stream.of(SSCAppVersionCopyType.values())
.filter(v->v.getSscValue().equals(sscValue))
.findFirst()
.orElseThrow(()->new IllegalStateException("Unknown SSC copyFrom option: "+sscValue));
}

}
Loading

0 comments on commit 75461db

Please sign in to comment.