diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/access_control/helper/SSCAppVersionUserUpdateBuilder.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/access_control/helper/SSCAppVersionUserUpdateBuilder.java index 5f3dc61500..cf64f9ff6c 100644 --- a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/access_control/helper/SSCAppVersionUserUpdateBuilder.java +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/access_control/helper/SSCAppVersionUserUpdateBuilder.java @@ -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; @@ -89,6 +90,13 @@ public final SSCAppVersionUserUpdateBuilder add(boolean allowMultipleMatches, St return this; } + public SSCAppVersionUserUpdateBuilder add(boolean allowMultipleMatches, Collection 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 ) { @@ -96,4 +104,6 @@ public final SSCAppVersionUserUpdateBuilder remove(boolean allowMultipleMatches, } return this; } + + } 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 1f32fc54e2..0afba25699 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 @@ -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; @@ -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; @@ -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") @@ -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"); @@ -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 getUsersFromSource(UnirestInstance unirest, SSCAppVersionCopyFromDescriptor copyFromDescriptor) { + if ( copyFromDescriptor.isCopyRequested() && copyFromDescriptor.getCopyOptions().contains(SSCAppVersionCopyOption.Users) ) { + return getUsersSet(unirest, copyFromDescriptor.getAppVersionDescriptor()); + } + return null; + } + + public static final Set getUsersSet(UnirestInstance unirest, SSCAppVersionDescriptor descriptor) { + Set 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 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 getAttributesFromSource(UnirestInstance unirest, SSCAppVersionCopyFromDescriptor copyFromDescriptor) { + if ( copyFromDescriptor.isCopyRequested() && copyFromDescriptor.getCopyOptions().contains(SSCAppVersionCopyOption.Attributes) ) { + return getAttributesMap(unirest, copyFromDescriptor.getAppVersionDescriptor()); + } + return null; + } + + public static final Map getAttributesMap(UnirestInstance unirest, SSCAppVersionDescriptor descriptor) { + var result = new LinkedHashMap(); + var attributes = SSCAppVersionHelper.getAttributes(unirest, descriptor); + for (JsonNode attr : attributes) { + List 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; } } @@ -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()); + } + } diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/mixin/ISSCDelimiterMixinAware.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/mixin/ISSCDelimiterMixinAware.java new file mode 100644 index 0000000000..752063b41d --- /dev/null +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/mixin/ISSCDelimiterMixinAware.java @@ -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); +} 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 index 90f9036241..8d9d960564 100644 --- 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 @@ -12,33 +12,49 @@ *******************************************************************************/ package com.fortify.cli.ssc.appversion.cli.mixin; +import java.util.Collection; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.fortify.cli.common.util.DisableTest; import com.fortify.cli.common.util.DisableTest.TestType; -import com.fortify.cli.ssc.appversion.helper.SSCAppVersionCopyType; +import com.fortify.cli.common.util.StringUtils; import com.fortify.cli.ssc.appversion.helper.SSCAppVersionDescriptor; import com.fortify.cli.ssc.appversion.helper.SSCAppVersionHelper; +import com.fortify.cli.ssc.system_state.helper.SSCJobHelper; import kong.unirest.UnirestInstance; +import lombok.Data; import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; import picocli.CommandLine.ArgGroup; +import picocli.CommandLine.Mixin; import picocli.CommandLine.Option; -public class SSCAppVersionCopyFromMixin { - +public class SSCAppVersionCopyFromMixin implements ISSCDelimiterMixinAware { + @Setter private SSCDelimiterMixin delimiterMixin; @ArgGroup(exclusive=false, multiplicity = "0..1") - private SSCAppVersionCopyFromArgGroup argGroup; + private SSCAppVersionCopyFromArgGroup copyOptionsArgGroup = new SSCAppVersionCopyFromArgGroup(); + @Mixin private SSCAppVersionRefreshOptions refreshOptions; - public boolean isCopyRequested() { return argGroup!=null; } - public String getAppVersionNameOrId() { - return argGroup==null ? null : argGroup.getAppVersionNameOrId(); + public final SSCAppVersionCopyFromDescriptor getCopyFromDescriptor(UnirestInstance unirest) { + var appVersionDescriptor = refresh(unirest, + copyOptionsArgGroup.getCopyFromAppVersionDescriptor(unirest, delimiterMixin.getDelimiter())); + var copyOptions = copyOptionsArgGroup.getCopyOptionsOrDefault(); + return new SSCAppVersionCopyFromDescriptor(appVersionDescriptor, copyOptions); } - - 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 SSCAppVersionDescriptor refresh(UnirestInstance unirest, SSCAppVersionDescriptor copyFrom) { + if (copyFrom!=null && copyFrom.isRefreshRequired() && refreshOptions.isRefresh() ) { + var jobDescriptor = SSCAppVersionHelper.refreshMetrics(unirest, copyFrom); + SSCJobHelper.waitForJob(unirest, jobDescriptor); + } + return copyFrom; } private static class SSCAppVersionCopyFromArgGroup { @@ -46,6 +62,65 @@ private static class SSCAppVersionCopyFromArgGroup { @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; + @Getter private Set copyOptions; + @Getter(lazy = true) private final Set copyOptionsOrDefault = + SSCAppVersionCopyOption.getCopyOptionsOrDefaultStream(copyOptions).collect(Collectors.toSet()); + + private SSCAppVersionDescriptor getCopyFromAppVersionDescriptor(UnirestInstance unirest, String delimiter, String... fields) { + return StringUtils.isBlank(appVersionNameOrId) + ? null + : SSCAppVersionHelper.getRequiredAppVersion(unirest, appVersionNameOrId, delimiter, fields); + } + } + + @Data + public static final class SSCAppVersionCopyFromDescriptor { + private final SSCAppVersionDescriptor appVersionDescriptor; + private final Set copyOptions; + + public boolean isCopyRequested() { + return appVersionDescriptor!=null; + } + } + + /** + * This enumeration defines the items that can be copied from an existing application version + */ + @RequiredArgsConstructor @Getter + public static enum SSCAppVersionCopyOption { + CustomTags("copyCustomTags", null), + BugTracker("copyBugTrackerConfiguration", null), + BugTrackerConfiguration("copyBugTrackerConfiguration", BugTracker), // Deprecated + ProcessingRules("copyAnalysisProcessingRules", null), + AnalysisProcessingRules("copyAnalysisProcessingRules", ProcessingRules), // Deprecated + // Contrary to what's sent by SSC UI, attributes are not supported on COPY_FROM_PARTIAL + Attributes(null, null), + // Contrary to what's sent by SSC UI, auth entities are not supported on COPY_FROM_PARTIAL + Users(null, null), + // Requires separate call to COPY_CURRENT_STATE action + State(null, null); + + private static final Logger LOG = LoggerFactory.getLogger(SSCAppVersionCopyOption.class); + private final String copyFromPartialProperty; + private final SSCAppVersionCopyOption deprecatedReplacement; + + public static final Stream getCopyOptionsOrDefaultStream(Collection copyOptions) { + return isCopyAll(copyOptions) + ? Stream.of(SSCAppVersionCopyOption.values()) + .filter(o->o.getDeprecatedReplacement()==null) + : copyOptions.stream() + .peek(SSCAppVersionCopyOption::warnDeprecated); + } + + public static final boolean isCopyAll(Collection copyOptions) { + return copyOptions==null || copyOptions.isEmpty(); + } + + public static final void warnDeprecated(SSCAppVersionCopyOption o) { + var replacement = o.getDeprecatedReplacement(); + if ( replacement!=null ) { + LOG.warn(String.format("WARN: %s is deprecated, please use %s", o.name(), replacement.name())); + } + } } } diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/mixin/SSCDelimiterMixin.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/mixin/SSCDelimiterMixin.java index 76fc293587..474e46c22c 100644 --- a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/mixin/SSCDelimiterMixin.java +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/cli/mixin/SSCDelimiterMixin.java @@ -12,10 +12,25 @@ *******************************************************************************/ package com.fortify.cli.ssc.appversion.cli.mixin; +import com.fortify.cli.common.cli.mixin.ICommandAware; + import lombok.Getter; +import picocli.CommandLine.Model.CommandSpec; import picocli.CommandLine.Option; -public final class SSCDelimiterMixin { +public final class SSCDelimiterMixin implements ICommandAware { @Option(names = {"--delim"}, defaultValue = ":") @Getter private String delimiter; + + @Override + public void setCommandSpec(CommandSpec commandSpec) { + commandSpec.mixins().values().forEach(this::injectThis); + } + + private void injectThis(CommandSpec spec) { + var mixin = spec.userObject(); + if ( mixin instanceof ISSCDelimiterMixinAware ) { + ((ISSCDelimiterMixinAware)mixin).setDelimiterMixin(this); + } + } } \ No newline at end of file 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 deleted file mode 100644 index 43d47d1cc2..0000000000 --- a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/helper/SSCAppVersionCopyType.java +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************* - * 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 deleted file mode 100644 index 374edd0072..0000000000 --- a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/helper/SSCAppVersionCreateCopyFromBuilder.java +++ /dev/null @@ -1,96 +0,0 @@ -/******************************************************************************* - * 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 com.fasterxml.jackson.databind.node.ObjectNode; -import com.fortify.cli.common.json.JsonHelper; -import com.fortify.cli.ssc._common.rest.SSCUrls; - -import kong.unirest.HttpRequest; -import kong.unirest.UnirestInstance; - -public final class SSCAppVersionCreateCopyFromBuilder { - private final UnirestInstance unirest; - - private ObjectNode copyFromPartialOptions = JsonHelper.getObjectMapper().createObjectNode(); - private ObjectNode copyStateOptions = JsonHelper.getObjectMapper().createObjectNode(); - private SSCAppVersionDescriptor previousProjectVersion; - - private boolean copyRequested = false; - - private boolean copyState = false; - - public SSCAppVersionCreateCopyFromBuilder(UnirestInstance unirest) { - this.unirest = unirest; - } - - 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; - } - - this.copyStateOptions.put("projectVersionId", Integer.parseInt(projectVersionId)); - - ObjectNode body = JsonHelper.getObjectMapper().createObjectNode(); - body .put("type", "copy_current_state") - .set("values", copyStateOptions); - - return unirest - .post(SSCUrls.PROJECT_VERSIONS_ACTION(projectVersionId)) - .body(body); - } - - public final SSCAppVersionCreateCopyFromBuilder setCopyFrom(SSCAppVersionDescriptor previousProjectVersionDescriptor) { - this.previousProjectVersion = previousProjectVersionDescriptor; - this.copyFromPartialOptions.put("previousProjectVersionId", previousProjectVersionDescriptor.getVersionId()); - this.copyStateOptions.put("previousProjectVersionId", previousProjectVersionDescriptor.getIntVersionId()); - 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/SSCAppVersionDescriptor.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/helper/SSCAppVersionDescriptor.java index 75c24d2c6a..4d538a9ce5 100644 --- a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/helper/SSCAppVersionDescriptor.java +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/appversion/helper/SSCAppVersionDescriptor.java @@ -31,6 +31,7 @@ public class SSCAppVersionDescriptor extends JsonNodeHolder { @JsonProperty("id") private String versionId; @JsonProperty("name") private String versionName; private boolean refreshRequired; + private String issueTemplateId; @JsonProperty("project") public void unpackProject(Map project) { 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 ce4cc1193a..dc5af3fa9b 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 @@ -75,6 +75,20 @@ private static final SSCAppVersionDescriptor getOptionalDescriptor(GetRequest re return versions.size()==0 ? null : JsonHelper.treeToValue(versions.get(0), SSCAppVersionDescriptor.class); } + public static final JsonNode getAttributes(UnirestInstance unirest, SSCAppVersionDescriptor descriptor) { + return unirest.get(SSCUrls.PROJECT_VERSION_ATTRIBUTES(descriptor.getVersionId())) + .asObject(ObjectNode.class) + .getBody() + .get("data"); + } + + public static final JsonNode getUsers(UnirestInstance unirest, SSCAppVersionDescriptor descriptor) { + return unirest.get(SSCUrls.PROJECT_VERSION_AUTH_ENTITIES(descriptor.getVersionId())) + .asObject(ObjectNode.class) + .getBody() + .get("data"); + } + public static final SSCJobDescriptor refreshMetrics(UnirestInstance unirest, SSCAppVersionDescriptor descriptor) { if ( !descriptor.isRefreshRequired() ) { return null; diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/issue/cli/mixin/SSCIssueTemplateResolverMixin.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/issue/cli/mixin/SSCIssueTemplateResolverMixin.java index 43311d0c4c..dde5e223ef 100644 --- a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/issue/cli/mixin/SSCIssueTemplateResolverMixin.java +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/issue/cli/mixin/SSCIssueTemplateResolverMixin.java @@ -32,11 +32,6 @@ public SSCIssueTemplateDescriptor getIssueTemplateDescriptor(UnirestInstance uni ? null : new SSCIssueTemplateHelper(unirest).getDescriptorByNameOrId(issueTemplateNameOrId, true); } - - public SSCIssueTemplateDescriptor getIssueTemplateDescriptorOrDefault(UnirestInstance unirest) { - SSCIssueTemplateDescriptor descriptor = getIssueTemplateDescriptor(unirest); - return descriptor!=null ? descriptor : SSCIssueTemplateHelper.getDefaultIssueTemplateDescriptor(unirest); - } } public static class OptionalOption extends AbstractSSCIssueTemplateResolverMixin { diff --git a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/issue/helper/SSCIssueTemplateHelper.java b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/issue/helper/SSCIssueTemplateHelper.java index e4b3a4b055..a4b2b01257 100644 --- a/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/issue/helper/SSCIssueTemplateHelper.java +++ b/fcli-core/fcli-ssc/src/main/java/com/fortify/cli/ssc/issue/helper/SSCIssueTemplateHelper.java @@ -15,6 +15,8 @@ import java.util.HashMap; import java.util.Map; +import org.apache.commons.lang3.StringUtils; + import com.fasterxml.jackson.databind.JsonNode; import com.fortify.cli.common.json.JsonHelper; import com.fortify.cli.ssc._common.rest.SSCUrls; @@ -56,6 +58,12 @@ public SSCIssueTemplateDescriptor getDescriptorByNameOrId(String issueTemplateNa return descriptor; } + public SSCIssueTemplateDescriptor getIssueTemplateDescriptorOrDefault(String issueTemplateNameOrId) { + return StringUtils.isBlank(issueTemplateNameOrId) + ? getDefaultIssueTemplateDescriptor() + : getDescriptorByNameOrId(issueTemplateNameOrId, true); + } + /** * If only the default issue template is needed, then this method will be more performant than * new SSCIssueTemplateHelper(unirest).getDefaultIssueTemplateDescriptor(). 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 fa480f390a..8787a5d4f1 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 @@ -296,6 +296,14 @@ fcli.ssc.app.resolver.nameOrId = Application name or id. Note that numeric value # fcli ssc appversion fcli.ssc.appversion.usage.header = Manage SSC application versions. fcli.ssc.appversion.create.usage.header = Create an application version. +fcli.ssc.appversion.create.usage.description = This command allows for creating a new SSC application \ + version, optionally skipping creation if the application version already exists (see --skip-if-exists \ + option), and/or copying configuration and state of an existing application version (see --copy and \ + --copy-from options). \ + %n%nNote that depending on user and token permissions, SSC may reject some of the application version \ + configuration settings. In particular, user access permissions as specified through the --add-users \ + option or when copying an existing application version may not be applied if the fcli session was \ + created using a CIToken. fcli.ssc.appversion.create.auto-required-attrs = Automatically set a default value for required application \ version attributes. fcli.ssc.appversion.create.description = Application version description. @@ -343,6 +351,11 @@ fcli.ssc.appversion.refresh-metrics.usage.description = This command allows for version metrics, similar to the little 'refresh' button shown on the SSC application version page \ in case a refresh is required/pending. fcli.ssc.appversion.update.usage.header = Update an application version. +fcli.ssc.appversion.update.usage.description = This command allows for updating an existing SSC \ + application version. \ + %n%nNote that depending on user and token permissions, SSC may reject some of the application version \ + configuration settings. In particular, user access permissions as specified through the --add-users \ + or --rm-users option may not be applied if the fcli session was created using a CIToken. 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.