diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/_common/session/helper/SSCSessionDescriptor.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/_common/session/helper/SSCSessionDescriptor.java index fce9f1af0a..a4ba8b7eb8 100644 --- a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/_common/session/helper/SSCSessionDescriptor.java +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/_common/session/helper/SSCSessionDescriptor.java @@ -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; diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/cmd/SSCAppVersionCommands.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/cmd/SSCAppVersionCommands.java index a74e6b54ab..259b09cae2 100644 --- a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/cmd/SSCAppVersionCommands.java +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/cmd/SSCAppVersionCommands.java @@ -23,6 +23,7 @@ subcommands = { SSCAppVersionCreateCommand.class, SSCAppVersionDeleteCommand.class, + SSCAppVersionCopyStateCommand.class, SSCAppVersionDownloadStateCommand.class, SSCAppVersionGetCommand.class, SSCAppVersionListCommand.class, diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/cmd/SSCAppVersionCopyStateCommand.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/cmd/SSCAppVersionCopyStateCommand.java new file mode 100644 index 0000000000..d54a68843e --- /dev/null +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/cmd/SSCAppVersionCopyStateCommand.java @@ -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 copyState(UnirestInstance unirest, SSCAppVersionDescriptor sourceAppVersion, SSCAppVersionDescriptor targetAppVersion) { + HashMap appVersionIds = new HashMap(); + 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; + } + +} diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/cmd/SSCAppVersionCreateCommand.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/cmd/SSCAppVersionCreateCommand.java index ac90c445f8..69b0270526 100644 --- a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/cmd/SSCAppVersionCreateCommand.java +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/cmd/SSCAppVersionCreateCommand.java @@ -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; @@ -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; @@ -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) { @@ -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 attributes = attrUpdateMixin.getAttributes(); return new SSCAttributeUpdateBuilder(unirest) @@ -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) @@ -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 ) { @@ -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); diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/cmd/SSCAppVersionRefreshMetricsCommand.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/cmd/SSCAppVersionRefreshMetricsCommand.java index 71f18d4ce2..a15cae8b84 100644 --- a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/cmd/SSCAppVersionRefreshMetricsCommand.java +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/cmd/SSCAppVersionRefreshMetricsCommand.java @@ -16,10 +16,12 @@ 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; @@ -27,6 +29,7 @@ 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; @@ -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 diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/mixin/SSCAppVersionCopyFromMixin.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/mixin/SSCAppVersionCopyFromMixin.java new file mode 100644 index 0000000000..9c5a5e3a51 --- /dev/null +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/mixin/SSCAppVersionCopyFromMixin.java @@ -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; + } +} diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/helper/SSCAppVersionCopyType.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/helper/SSCAppVersionCopyType.java new file mode 100644 index 0000000000..43d47d1cc2 --- /dev/null +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/helper/SSCAppVersionCopyType.java @@ -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)); + } + +} diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/helper/SSCAppVersionCreateCopyFromBuilder.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/helper/SSCAppVersionCreateCopyFromBuilder.java new file mode 100644 index 0000000000..91a9334265 --- /dev/null +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/helper/SSCAppVersionCreateCopyFromBuilder.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * 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.HashMap; + +import com.fortify.cli.ssc._common.rest.SSCUrls; +import com.fortify.cli.ssc.system_state.helper.SSCJobDescriptor; +import com.fortify.cli.ssc.system_state.helper.SSCJobHelper; + +import kong.unirest.HttpRequest; +import kong.unirest.UnirestInstance; + +public final class SSCAppVersionCreateCopyFromBuilder { + private final UnirestInstance unirest; + + private HashMap copyFromPartialOptions = new HashMap(); + private HashMap copyStateOptions = new HashMap(); + private SSCAppVersionDescriptor previousProjectVersion; + + private boolean copyRequested = false; + + private boolean copyState; + + public SSCAppVersionCreateCopyFromBuilder(UnirestInstance unirest) { + this.unirest = unirest; + } + + public final HttpRequest buildCopyFromRefreshRequest(String projectVersionId) { + if(!copyState || !copyRequested){ + return null; + } + + this.copyFromPartialOptions.put("projectVersionId", projectVersionId); + return unirest + .post(SSCUrls.PROJECT_VERSIONS_ACTION_COPY_FROM_PARTIAL) + .body(copyFromPartialOptions); + } + + public final HttpRequest buildCopyFromPartialRequest(String projectVersionId) { + if(!copyRequested){ + return null; + } + + this.copyFromPartialOptions.put("projectVersionId", projectVersionId); + return unirest + .post(SSCUrls.PROJECT_VERSIONS_ACTION_COPY_FROM_PARTIAL) + .body(copyFromPartialOptions); + } + + public final HttpRequest buildCopyStateRequest(String projectVersionId) { + if(!copyState || !copyRequested){ + return null; + } + // refreshMetrics if the source PV is required to fully copy the tags, audit or comments + if(this.previousProjectVersion.isRefreshRequired()){ + SSCJobDescriptor refreshJobDesc = SSCAppVersionHelper.refreshMetrics(unirest, this.previousProjectVersion); + SSCJobHelper.waitForJob(unirest,refreshJobDesc); + } + this.copyStateOptions.put("projectVersionId", projectVersionId); + return unirest + .post(SSCUrls.PROJECT_VERSIONS_ACTION_COPY_CURRENT_STATE) + .body(copyStateOptions); + } + + public final SSCAppVersionCreateCopyFromBuilder setCopyFrom(SSCAppVersionDescriptor previousProjectVersionDescriptor) { + this.previousProjectVersion = previousProjectVersionDescriptor; + this.copyFromPartialOptions.put("previousProjectVersionId", previousProjectVersionDescriptor.getVersionId()); + this.copyStateOptions.put("previousProjectVersionId", previousProjectVersionDescriptor.getVersionId()); + return this; + } + + public final SSCAppVersionCreateCopyFromBuilder setCopyOptions(SSCAppVersionCopyType[] copyOptions) { + if(copyOptions == null) { + copyOptions = SSCAppVersionCopyType.values(); + } + for (SSCAppVersionCopyType option : copyOptions) { + if(option.getSscValue() == "copyState") { + this.copyState = true; + } else { + this.copyFromPartialOptions.put(option.getSscValue(), "true"); + } + } + + return this; + } + + public final SSCAppVersionCreateCopyFromBuilder setCopyRequested(boolean status){ + this.copyRequested = status; + + return this; + } + + public boolean copyStateEnabled() { + return this.copyState; + } +} diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/helper/SSCAppVersionHelper.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/helper/SSCAppVersionHelper.java index 69fe521edd..ce4cc1193a 100644 --- a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/helper/SSCAppVersionHelper.java +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/helper/SSCAppVersionHelper.java @@ -18,6 +18,8 @@ import com.fortify.cli.common.json.JsonHelper; import com.fortify.cli.common.output.transform.fields.RenameFieldsTransformer; import com.fortify.cli.ssc._common.rest.SSCUrls; +import com.fortify.cli.ssc.system_state.helper.SSCJobDescriptor; +import com.fortify.cli.ssc.system_state.helper.SSCJobHelper; import kong.unirest.GetRequest; import kong.unirest.UnirestInstance; @@ -73,15 +75,15 @@ private static final SSCAppVersionDescriptor getOptionalDescriptor(GetRequest re return versions.size()==0 ? null : JsonHelper.treeToValue(versions.get(0), SSCAppVersionDescriptor.class); } - public static final String refreshMetrics(UnirestInstance unirest, SSCAppVersionDescriptor descriptor) { + public static final SSCJobDescriptor refreshMetrics(UnirestInstance unirest, SSCAppVersionDescriptor descriptor) { if ( !descriptor.isRefreshRequired() ) { - return "NO_REFRESH_REQUIRED"; + return null; } else { - unirest.post(SSCUrls.PROJECT_VERSIONS_ACTION_REFRESH) + JsonNode response = unirest.post(SSCUrls.PROJECT_VERSIONS_ACTION_REFRESH) .body(new SSCAppVersionRefreshRequest(descriptor.getVersionId())) .asObject(ObjectNode.class) .getBody(); - return "REFRESH_REQUESTED"; + return SSCJobHelper.getJobDescriptor(unirest, response.get("data").get("data").get("id").textValue()); } } diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/system_state/cli/cmd/SSCStateJobWaitForCommand.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/system_state/cli/cmd/SSCStateJobWaitForCommand.java new file mode 100644 index 0000000000..8dadf3066a --- /dev/null +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/system_state/cli/cmd/SSCStateJobWaitForCommand.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * 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.system_state.cli.cmd; + +import java.util.Set; + +import com.fortify.cli.common.cli.util.CommandGroup; +import com.fortify.cli.common.rest.cli.cmd.AbstractWaitForCommand; +import com.fortify.cli.common.rest.wait.WaitHelper.WaitHelperBuilder; +import com.fortify.cli.ssc._common.output.cli.mixin.SSCProductHelperStandardMixin; +import com.fortify.cli.ssc.system_state.cli.mixin.SSCJobResolverMixin; +import com.fortify.cli.ssc.system_state.helper.SSCJobStatus; +import com.fortify.cli.ssc.system_state.helper.SSCJobStatus.SSCJobStatusIterable; + +import kong.unirest.UnirestInstance; +import lombok.Getter; +import picocli.CommandLine.Command; +import picocli.CommandLine.Mixin; +import picocli.CommandLine.Option; + +@Command(name = "wait-for-job") @CommandGroup("job") +public class SSCStateJobWaitForCommand extends AbstractWaitForCommand { + @Getter @Mixin SSCProductHelperStandardMixin productHelper; + @Mixin private SSCJobResolverMixin.PositionalParameterMulti jobsResolver; + @Option(names={"-s", "--any-state"}, required=true, split=",", defaultValue="FINISHED", completionCandidates = SSCJobStatusIterable.class) + private Set states; + + @Override + protected WaitHelperBuilder configure(UnirestInstance unirest, WaitHelperBuilder builder) { + return builder + .recordsSupplier(jobsResolver::getJobDescriptorJsonNodes) + .currentStateProperty("state") + .knownStates(SSCJobStatus.getKnownStateNames()) + .failureStates(SSCJobStatus.getFailureStateNames()) + .matchStates(states); + } +} diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/system_state/cli/cmd/SSCSystemStateCommands.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/system_state/cli/cmd/SSCSystemStateCommands.java index 69edf8b102..edaab0cba2 100644 --- a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/system_state/cli/cmd/SSCSystemStateCommands.java +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/system_state/cli/cmd/SSCSystemStateCommands.java @@ -29,6 +29,7 @@ SSCStateJobGetCommand.class, SSCStateJobListCommand.class, SSCStateJobUpdateCommand.class, + SSCStateJobWaitForCommand.class } ) public class SSCSystemStateCommands extends AbstractContainerCommand { diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/system_state/cli/mixin/SSCJobResolverMixin.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/system_state/cli/mixin/SSCJobResolverMixin.java index 5eb38039de..893777dfdf 100644 --- a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/system_state/cli/mixin/SSCJobResolverMixin.java +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/system_state/cli/mixin/SSCJobResolverMixin.java @@ -12,6 +12,11 @@ *******************************************************************************/ package com.fortify.cli.ssc.system_state.cli.mixin; +import java.util.Collection; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.fasterxml.jackson.databind.JsonNode; import com.fortify.cli.common.cli.util.EnvSuffix; import com.fortify.cli.ssc.system_state.helper.SSCJobDescriptor; import com.fortify.cli.ssc.system_state.helper.SSCJobHelper; @@ -33,6 +38,22 @@ public String getJobName(UnirestInstance unirest) { return getJobDescriptor(unirest, "jobName").getJobName(); } } + + public static abstract class AbstractSSCAppVersionMultiJobResolverMixin { + public abstract String[] getJobNames(); + + public SSCJobDescriptor[] getJobDescriptors(UnirestInstance unirest){ + return Stream.of(getJobNames()).map(id-> SSCJobHelper.getJobDescriptor(unirest, id)).toArray(SSCJobDescriptor[]::new); + } + + public Collection getJobDescriptorJsonNodes(UnirestInstance unirest){ + return Stream.of(getJobDescriptors(unirest)).map(SSCJobDescriptor::asJsonNode).collect(Collectors.toList()); + } + + public String[] getJobNames(UnirestInstance unirest) { + return Stream.of(getJobDescriptors(unirest)).map(SSCJobDescriptor::getJobName).toArray(String[]::new); + } + } public static class RequiredOption extends AbstractSSCJobResolverMixin { @Option(names="--job", required = true, descriptionKey = "fcli.ssc.system-state.job.resolver.name") @@ -43,4 +64,9 @@ public static class PositionalParameter extends AbstractSSCJobResolverMixin { @EnvSuffix("JOB") @Parameters(index = "0", arity = "1", descriptionKey = "fcli.ssc.system-state.job.resolver.name") @Getter private String jobName; } + + public static class PositionalParameterMulti extends SSCJobResolverMixin.AbstractSSCAppVersionMultiJobResolverMixin { + @EnvSuffix("JOB") @Parameters(index = "0", arity = "1..", paramLabel = "job-names", descriptionKey = "fcli.ssc.system-state.job.resolver.names") + @Getter private String[] jobNames; + } } diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/system_state/helper/SSCJobDescriptor.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/system_state/helper/SSCJobDescriptor.java index 5e1ef2eef1..31f116073a 100644 --- a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/system_state/helper/SSCJobDescriptor.java +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/system_state/helper/SSCJobDescriptor.java @@ -12,9 +12,15 @@ *******************************************************************************/ package com.fortify.cli.ssc.system_state.helper; +import java.util.Collection; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.fasterxml.jackson.databind.JsonNode; import com.formkiq.graalvm.annotations.Reflectable; import com.fortify.cli.common.json.JsonNodeHolder; +import kong.unirest.UnirestInstance; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -26,4 +32,12 @@ public class SSCJobDescriptor extends JsonNodeHolder { private String jobGroup; private Integer priority; private String jobState; + + public SSCJobDescriptor[] getJobDescriptors(UnirestInstance unirest){ + String col = jobName; + return Stream.of(col).map(id-> SSCJobHelper.getJobDescriptor(unirest, id)).toArray(SSCJobDescriptor[]::new); + } + public Collection getJobDescriptorJsonNodes(UnirestInstance unirest){ + return Stream.of(getJobDescriptors(unirest)).map(SSCJobDescriptor::asJsonNode).collect(Collectors.toList()); + } } diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/system_state/helper/SSCJobHelper.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/system_state/helper/SSCJobHelper.java index 86f0f32f8f..f1fa4c12d2 100644 --- a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/system_state/helper/SSCJobHelper.java +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/system_state/helper/SSCJobHelper.java @@ -12,14 +12,21 @@ *******************************************************************************/ package com.fortify.cli.ssc.system_state.helper; +import java.util.Arrays; +import java.util.HashSet; + import com.fasterxml.jackson.databind.JsonNode; import com.fortify.cli.common.json.JsonHelper; +import com.fortify.cli.common.rest.wait.WaitHelper; +import com.fortify.cli.common.rest.wait.WaitHelper.WaitHelperBuilder; +import com.fortify.cli.common.rest.wait.WaitType; import com.fortify.cli.ssc._common.rest.SSCUrls; import kong.unirest.GetRequest; import kong.unirest.UnirestInstance; -public final class SSCJobHelper { + +public final class SSCJobHelper { public static final SSCJobDescriptor getJobDescriptor(UnirestInstance unirest, String jobName, String... fields) { GetRequest request = unirest.get(SSCUrls.JOB(jobName)); if ( fields!=null && fields.length>0 ) { @@ -28,4 +35,39 @@ public static final SSCJobDescriptor getJobDescriptor(UnirestInstance unirest, S JsonNode jobBody = request.asObject(JsonNode.class).getBody(); return JsonHelper.treeToValue(jobBody.get("data"), SSCJobDescriptor.class); } + + private static JsonNode getJobJsonNode(UnirestInstance unirest, String jobName) { + return unirest.get(SSCUrls.JOB(jobName)) + .asObject(JsonNode.class).getBody().get("data"); + } + + private static final SSCJobDescriptor getDescriptor(JsonNode node) { + return JsonHelper.treeToValue(node, SSCJobDescriptor.class); + } + + public static final SSCJobDescriptor getJobDescriptor(UnirestInstance unirest, String jobName) { + return getDescriptor(getJobJsonNode(unirest, jobName)); + } + + public final static SSCJobDescriptor waitForJob(UnirestInstance unirest, SSCJobDescriptor descriptor){ + WaitHelperBuilder builder = WaitHelper.builder() + .waitType(new WaitType(WaitType.LoopType.Until, WaitType.AnyOrAll.all_match)) + .timeoutPeriod("60s") + .intervalPeriod("5s") + .onFailureState(null) + .onTimeout(null) + .onUnknownState(null) + .onUnknownStateRequested(null); + + builder .recordsSupplier(descriptor::getJobDescriptorJsonNodes) + .currentStateProperty("state") + .knownStates(SSCJobStatus.getKnownStateNames()) + .failureStates(SSCJobStatus.getFailureStateNames()) + .matchStates(new HashSet<>(Arrays.asList(SSCJobStatus.getDefaultCompleteStateNames()))); + + builder.build().wait(unirest); + + return descriptor; + } + } diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/system_state/helper/SSCJobStatus.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/system_state/helper/SSCJobStatus.java new file mode 100644 index 0000000000..8d377a5843 --- /dev/null +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/system_state/helper/SSCJobStatus.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * 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.system_state.helper; + +import java.util.ArrayList; +import java.util.stream.Stream; + +/** + * Enum values copied from SSC API Reference enum + */ +public enum SSCJobStatus { + PREPARED, FINISHED, RUNNING, DEFERRED, FAILED, CANCELLED, CANCELLING; + + public static final SSCJobStatus[] getFailureStates() { + return new SSCJobStatus[]{ + DEFERRED, FAILED, CANCELLED + }; + } + + public static final SSCJobStatus[] getKnownStates() { + return SSCJobStatus.values(); + } + + public static final SSCJobStatus[] getDefaultCompleteStates() { + return new SSCJobStatus[]{FINISHED}; + } + + public static final String[] getFailureStateNames() { + return Stream.of(getFailureStates()).map(SSCJobStatus::name).toArray(String[]::new); + } + + public static final String[] getKnownStateNames() { + return Stream.of(getKnownStates()).map(SSCJobStatus::name).toArray(String[]::new); + } + + public static final String[] getDefaultCompleteStateNames() { + return Stream.of(getDefaultCompleteStates()).map(SSCJobStatus::name).toArray(String[]::new); + } + + public static final class SSCJobStatusIterable extends ArrayList { + private static final long serialVersionUID = 1L; + + public SSCJobStatusIterable() { + super(Stream.of(SSCJobStatus.values()).map(SSCJobStatus::name).toList()); + } + } + +} diff --git a/fcli-core/fcli-ssc/src/main/resources/com/fortify/cli/ssc/i18n/SSCMessages.properties b/fcli-core/fcli-ssc/src/main/resources/com/fortify/cli/ssc/i18n/SSCMessages.properties index f9bdafff9f..4063f60e3b 100644 --- a/fcli-core/fcli-ssc/src/main/resources/com/fortify/cli/ssc/i18n/SSCMessages.properties +++ b/fcli-core/fcli-ssc/src/main/resources/com/fortify/cli/ssc/i18n/SSCMessages.properties @@ -228,7 +228,12 @@ fcli.ssc.appversion.create.active = Specify whether application version should b or not (false). fcli.ssc.appversion.create.skip-if-exists = Skip application version creation if an application version with \ the specified name already exists. +fcli.ssc.appversion.create.copy-options = Comma separated list of elements to copy (Requires --copy-from). By default, all are copied. \ + Allowed values: ${COMPLETION-CANDIDATES}. fcli.ssc.appversion.delete.usage.header = Delete an application version. +fcli.ssc.appversion.copy-from.usage.header = Copy from options +fcli.ssc.appversion.copy-state.usage.header = (PREVIEW) Copy application version state. +fcli.ssc.appversion.copy-state.usage.description = Copy application version state from another application version. \nNote that this command may potentially have unintended side effects when copying state to application versions that already contain artifacts. fcli.ssc.appversion.download-state.usage.header = Download application version state. fcli.ssc.appversion.download-state.usage.description = Download application version state artifact. See 'fcli ssc artifact download' for downloading individual artifacts. fcli.ssc.appversion.download-state.file = Optional output file path. @@ -265,7 +270,9 @@ fcli.ssc.appversion.update.name = Update application version name. fcli.ssc.appversion.update.description = Update application version description. fcli.ssc.appversion.resolver.name = Application and version name. fcli.ssc.appversion.resolver.nameOrId = Application version id or : name. - +fcli.ssc.appversion.resolver.copy-from.nameOrId = Copy FROM application version: \nid or : name. +fcli.ssc.appversion.resolver.copy-to.nameOrId = Copy TO application version: \nid or : name. + # fcli ssc artifact fcli.ssc.artifact.usage.header = Manage SSC artifacts. fcli.ssc.artifact.approve.usage.header = Approve an artifact. @@ -418,9 +425,14 @@ fcli.ssc.system-state.get-job.usage.header = Get job details. fcli.ssc.system-state.list-jobs.usage.header = List jobs. fcli.ssc.system-state.update-job.usage.header = Update job priority. fcli.ssc.system-state.update-job.priority = New priority for this job. -fcli.ssc.system-state.job.resolver.name = Job name. fcli.ssc.system-state.upload-seed-bundle.usage.header = Upload a seed bundle. fcli.ssc.system-state.upload-seed-bundle.file = Seed bundle to upload. +fcli.ssc.system-state.job.resolver.name = Job name. +fcli.ssc.system-state.job.resolver.names = One or more job names +fcli.ssc.system-state.wait-for-job.usage.header = Wait for multiple system jobs +fcli.ssc.system-state.wait-for-job.until=Wait until either any or all jobs match. If neither --until or --while are specified, default is to wait until all jobs match. +fcli.ssc.system-state.wait-for-job.while=Wait while either any or all jobs match. +fcli.ssc.system-state.wait-for-job.any-state=One or more processing states against which to match the given jobs. ################################################################################################################# # The following are technical properties that shouldn't be internationalized #################################### @@ -444,6 +456,7 @@ fcli.ssc.alert.definition.output.table.options = id,name,createdBy,recipientType fcli.ssc.app.output.table.options = id,name fcli.ssc.app.delete.output.table.options = id,application.name,name,createdBy,action fcli.ssc.appversion.output.table.options = id,application.name,name,issueTemplateName,createdBy +fcli.ssc.appversion.copy-state.output.table.options = previousProjectVersionId,projectVersionId # TODO Add scanTypes property using recordTransformer in command implementation fcli.ssc.artifact.output.table.options = id,scanTypes,lastScanDate,uploadDate,status fcli.ssc.attribute.output.table.options = id,category,guid,name,valueString