mirror of https://github.com/apache/nifi.git
NIFI-4839 - Fixing completer unit test
- Added pg-get-version, pg-get-all-versions, pg-change-version - Added info the Context to know if we are in interactive mode
This commit is contained in:
parent
8b490134c7
commit
9c3594ded6
|
@ -74,7 +74,7 @@ public class CLIMain {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs the interactive CLIE.
|
* Runs the interactive CLI.
|
||||||
*
|
*
|
||||||
* @throws IOException if an error occurs
|
* @throws IOException if an error occurs
|
||||||
*/
|
*/
|
||||||
|
@ -191,6 +191,7 @@ public class CLIMain {
|
||||||
.session(session)
|
.session(session)
|
||||||
.nifiClientFactory(niFiClientFactory)
|
.nifiClientFactory(niFiClientFactory)
|
||||||
.nifiRegistryClientFactory(nifiRegClientFactory)
|
.nifiRegistryClientFactory(nifiRegClientFactory)
|
||||||
|
.interactive(isInteractive)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,4 +34,6 @@ public interface Context {
|
||||||
|
|
||||||
PrintStream getOutput();
|
PrintStream getOutput();
|
||||||
|
|
||||||
|
boolean isInteractive();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.apache.nifi.toolkit.cli.impl.client.nifi.FlowClient;
|
||||||
import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
|
import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
|
||||||
import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientConfig;
|
import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientConfig;
|
||||||
import org.apache.nifi.toolkit.cli.impl.client.nifi.ProcessGroupClient;
|
import org.apache.nifi.toolkit.cli.impl.client.nifi.ProcessGroupClient;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.client.nifi.VersionsClient;
|
||||||
import org.apache.nifi.toolkit.cli.impl.client.nifi.impl.JerseyNiFiClient;
|
import org.apache.nifi.toolkit.cli.impl.client.nifi.impl.JerseyNiFiClient;
|
||||||
import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
|
import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
|
||||||
|
|
||||||
|
@ -164,6 +165,21 @@ public class NiFiClientFactory implements ClientFactory<NiFiClient> {
|
||||||
return wrappedClient.getProcessGroupClientForToken(token);
|
return wrappedClient.getProcessGroupClientForToken(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VersionsClient getVersionsClient() {
|
||||||
|
return wrappedClient.getVersionsClientForProxiedEntities(proxiedEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VersionsClient getVersionsClientForProxiedEntities(String... proxiedEntity) {
|
||||||
|
return wrappedClient.getVersionsClientForProxiedEntities(proxiedEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VersionsClient getVersionsClientForToken(String token) {
|
||||||
|
return wrappedClient.getVersionsClientForToken(token);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
wrappedClient.close();
|
wrappedClient.close();
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.apache.nifi.toolkit.cli.impl.client.nifi;
|
||||||
import org.apache.nifi.web.api.entity.CurrentUserEntity;
|
import org.apache.nifi.web.api.entity.CurrentUserEntity;
|
||||||
import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
|
import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
|
||||||
import org.apache.nifi.web.api.entity.ScheduleComponentsEntity;
|
import org.apache.nifi.web.api.entity.ScheduleComponentsEntity;
|
||||||
|
import org.apache.nifi.web.api.entity.VersionedFlowSnapshotMetadataSetEntity;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@ -27,6 +28,11 @@ import java.io.IOException;
|
||||||
*/
|
*/
|
||||||
public interface FlowClient {
|
public interface FlowClient {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the entity representing the current user accessing the NiFi instance
|
||||||
|
*/
|
||||||
|
CurrentUserEntity getCurrentUser() throws NiFiClientException, IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the id of the root process group
|
* @return the id of the root process group
|
||||||
*/
|
*/
|
||||||
|
@ -53,8 +59,14 @@ public interface FlowClient {
|
||||||
String processGroupId, ScheduleComponentsEntity scheduleComponentsEntity) throws NiFiClientException, IOException;
|
String processGroupId, ScheduleComponentsEntity scheduleComponentsEntity) throws NiFiClientException, IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the entity representing the current user accessing the NiFi instance
|
* Gets the possible versions for the given flow in the given bucket in the given registry.
|
||||||
|
*
|
||||||
|
* @param registryId the id of the registry client
|
||||||
|
* @param bucketId the bucket id
|
||||||
|
* @param flowId the flow id
|
||||||
|
* @return the set of snapshot metadata entities
|
||||||
*/
|
*/
|
||||||
CurrentUserEntity getCurrentUser() throws NiFiClientException, IOException;
|
VersionedFlowSnapshotMetadataSetEntity getVersions(String registryId, String bucketId, String flowId)
|
||||||
|
throws NiFiClientException, IOException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,13 @@ public interface NiFiClient extends Closeable {
|
||||||
|
|
||||||
ProcessGroupClient getProcessGroupClientForToken(String token);
|
ProcessGroupClient getProcessGroupClientForToken(String token);
|
||||||
|
|
||||||
|
// ----- VersionsClient -----
|
||||||
|
|
||||||
|
VersionsClient getVersionsClient();
|
||||||
|
|
||||||
|
VersionsClient getVersionsClientForProxiedEntities(String ... proxiedEntity);
|
||||||
|
|
||||||
|
VersionsClient getVersionsClientForToken(String token);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The builder interface that implementations should provide for obtaining the client.
|
* The builder interface that implementations should provide for obtaining the client.
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.nifi.toolkit.cli.impl.client.nifi;
|
||||||
|
|
||||||
|
import org.apache.nifi.web.api.entity.VersionControlInformationEntity;
|
||||||
|
import org.apache.nifi.web.api.entity.VersionedFlowUpdateRequestEntity;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public interface VersionsClient {
|
||||||
|
|
||||||
|
VersionControlInformationEntity getVersionControlInfo(String processGroupId) throws IOException, NiFiClientException;
|
||||||
|
|
||||||
|
VersionedFlowUpdateRequestEntity updateVersionControlInfo(String processGroupId, VersionControlInformationEntity entity)
|
||||||
|
throws IOException, NiFiClientException;
|
||||||
|
|
||||||
|
VersionedFlowUpdateRequestEntity getUpdateRequest(String updateRequestId) throws IOException, NiFiClientException;
|
||||||
|
|
||||||
|
VersionedFlowUpdateRequestEntity deleteUpdateRequest(String updateRequestId) throws IOException, NiFiClientException;
|
||||||
|
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
|
||||||
import org.apache.nifi.web.api.entity.CurrentUserEntity;
|
import org.apache.nifi.web.api.entity.CurrentUserEntity;
|
||||||
import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
|
import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
|
||||||
import org.apache.nifi.web.api.entity.ScheduleComponentsEntity;
|
import org.apache.nifi.web.api.entity.ScheduleComponentsEntity;
|
||||||
|
import org.apache.nifi.web.api.entity.VersionedFlowSnapshotMetadataSetEntity;
|
||||||
|
|
||||||
import javax.ws.rs.client.Entity;
|
import javax.ws.rs.client.Entity;
|
||||||
import javax.ws.rs.client.WebTarget;
|
import javax.ws.rs.client.WebTarget;
|
||||||
|
@ -102,4 +103,31 @@ public class JerseyFlowClient extends AbstractJerseyClient implements FlowClient
|
||||||
ScheduleComponentsEntity.class);
|
ScheduleComponentsEntity.class);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VersionedFlowSnapshotMetadataSetEntity getVersions(final String registryId, final String bucketId, final String flowId)
|
||||||
|
throws NiFiClientException, IOException {
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(registryId)) {
|
||||||
|
throw new IllegalArgumentException("Registry id cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(bucketId)) {
|
||||||
|
throw new IllegalArgumentException("Bucket id cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(flowId)) {
|
||||||
|
throw new IllegalArgumentException("Flow id cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
return executeAction("Error retrieving versions", () -> {
|
||||||
|
final WebTarget target = flowTarget
|
||||||
|
.path("registries/{registry-id}/buckets/{bucket-id}/flows/{flow-id}/versions")
|
||||||
|
.resolveTemplate("registry-id", registryId)
|
||||||
|
.resolveTemplate("bucket-id", bucketId)
|
||||||
|
.resolveTemplate("flow-id", flowId);
|
||||||
|
|
||||||
|
return getRequestBuilder(target).get(VersionedFlowSnapshotMetadataSetEntity.class);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.apache.nifi.toolkit.cli.impl.client.nifi.FlowClient;
|
||||||
import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
|
import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
|
||||||
import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientConfig;
|
import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientConfig;
|
||||||
import org.apache.nifi.toolkit.cli.impl.client.nifi.ProcessGroupClient;
|
import org.apache.nifi.toolkit.cli.impl.client.nifi.ProcessGroupClient;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.client.nifi.VersionsClient;
|
||||||
import org.glassfish.jersey.client.ClientConfig;
|
import org.glassfish.jersey.client.ClientConfig;
|
||||||
import org.glassfish.jersey.client.ClientProperties;
|
import org.glassfish.jersey.client.ClientProperties;
|
||||||
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJaxbJsonProvider;
|
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJaxbJsonProvider;
|
||||||
|
@ -159,6 +160,23 @@ public class JerseyNiFiClient implements NiFiClient {
|
||||||
return new JerseyProcessGroupClient(baseTarget, headers);
|
return new JerseyProcessGroupClient(baseTarget, headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VersionsClient getVersionsClient() {
|
||||||
|
return new JerseyVersionsClient(baseTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VersionsClient getVersionsClientForProxiedEntities(String... proxiedEntity) {
|
||||||
|
final Map<String,String> headers = getHeaders(proxiedEntity);
|
||||||
|
return new JerseyVersionsClient(baseTarget, headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VersionsClient getVersionsClientForToken(String base64token) {
|
||||||
|
final Map<String,String> headers = getHeadersWithToken(base64token);
|
||||||
|
return new JerseyVersionsClient(baseTarget, headers);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
if (this.client != null) {
|
if (this.client != null) {
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.nifi.toolkit.cli.impl.client.nifi.impl;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.client.nifi.VersionsClient;
|
||||||
|
import org.apache.nifi.web.api.entity.VersionControlInformationEntity;
|
||||||
|
import org.apache.nifi.web.api.entity.VersionedFlowUpdateRequestEntity;
|
||||||
|
|
||||||
|
import javax.ws.rs.client.Entity;
|
||||||
|
import javax.ws.rs.client.WebTarget;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jersey implementation of VersionsClient.
|
||||||
|
*/
|
||||||
|
public class JerseyVersionsClient extends AbstractJerseyClient implements VersionsClient {
|
||||||
|
|
||||||
|
private final WebTarget versionsTarget;
|
||||||
|
|
||||||
|
public JerseyVersionsClient(final WebTarget baseTarget) {
|
||||||
|
this(baseTarget, Collections.emptyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
public JerseyVersionsClient(final WebTarget baseTarget, final Map<String,String> headers) {
|
||||||
|
super(headers);
|
||||||
|
this.versionsTarget = baseTarget.path("/versions");
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /versions/process-groups/id
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VersionControlInformationEntity getVersionControlInfo(final String processGroupId) throws IOException, NiFiClientException {
|
||||||
|
if (StringUtils.isBlank(processGroupId)) {
|
||||||
|
throw new IllegalArgumentException("Process group id cannot be null or blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
return executeAction("Error getting version control info", () -> {
|
||||||
|
final WebTarget target = versionsTarget
|
||||||
|
.path("process-groups/{id}")
|
||||||
|
.resolveTemplate("id", processGroupId);
|
||||||
|
|
||||||
|
return getRequestBuilder(target).get(VersionControlInformationEntity.class);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /versions/update-requests/process-groups/id
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VersionedFlowUpdateRequestEntity updateVersionControlInfo(final String processGroupId, final VersionControlInformationEntity entity)
|
||||||
|
throws IOException, NiFiClientException {
|
||||||
|
if (StringUtils.isBlank(processGroupId)) {
|
||||||
|
throw new IllegalArgumentException("Process group id cannot be null or blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entity == null) {
|
||||||
|
throw new IllegalArgumentException("Version control information entity cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
return executeAction("Error updating version control information", () -> {
|
||||||
|
final WebTarget target = versionsTarget
|
||||||
|
.path("update-requests/process-groups/{id}")
|
||||||
|
.resolveTemplate("id", processGroupId);
|
||||||
|
|
||||||
|
return getRequestBuilder(target).post(
|
||||||
|
Entity.entity(entity, MediaType.APPLICATION_JSON_TYPE),
|
||||||
|
VersionedFlowUpdateRequestEntity.class
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /versions/update-requests/id
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VersionedFlowUpdateRequestEntity getUpdateRequest(final String updateRequestId) throws IOException, NiFiClientException {
|
||||||
|
if (StringUtils.isBlank(updateRequestId)) {
|
||||||
|
throw new IllegalArgumentException("Update request id cannot be null or blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
return executeAction("Error getting update request", () -> {
|
||||||
|
final WebTarget target = versionsTarget
|
||||||
|
.path("update-requests/{id}")
|
||||||
|
.resolveTemplate("id", updateRequestId);
|
||||||
|
|
||||||
|
return getRequestBuilder(target).get(VersionedFlowUpdateRequestEntity.class);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE /versions/update-requests/id
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VersionedFlowUpdateRequestEntity deleteUpdateRequest(final String updateRequestId) throws IOException, NiFiClientException {
|
||||||
|
if (StringUtils.isBlank(updateRequestId)) {
|
||||||
|
throw new IllegalArgumentException("Update request id cannot be null or blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
return executeAction("Error deleting update request", () -> {
|
||||||
|
final WebTarget target = versionsTarget
|
||||||
|
.path("update-requests/{id}")
|
||||||
|
.resolveTemplate("id", updateRequestId);
|
||||||
|
|
||||||
|
return getRequestBuilder(target).delete(VersionedFlowUpdateRequestEntity.class);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,10 @@ import org.apache.nifi.toolkit.cli.api.Command;
|
||||||
import org.apache.nifi.toolkit.cli.impl.command.AbstractCommandGroup;
|
import org.apache.nifi.toolkit.cli.impl.command.AbstractCommandGroup;
|
||||||
import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.CurrentUser;
|
import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.CurrentUser;
|
||||||
import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.GetRootId;
|
import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.GetRootId;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGChangeVersion;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGGetAllVersions;
|
||||||
import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGGetVars;
|
import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGGetVars;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGGetVersion;
|
||||||
import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGImport;
|
import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGImport;
|
||||||
import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGStart;
|
import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGStart;
|
||||||
import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGStop;
|
import org.apache.nifi.toolkit.cli.impl.command.nifi.pg.PGStop;
|
||||||
|
@ -36,8 +39,10 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public class NiFiCommandGroup extends AbstractCommandGroup {
|
public class NiFiCommandGroup extends AbstractCommandGroup {
|
||||||
|
|
||||||
|
public static final String NIFI_COMMAND_GROUP = "nifi";
|
||||||
|
|
||||||
public NiFiCommandGroup() {
|
public NiFiCommandGroup() {
|
||||||
super("nifi");
|
super(NIFI_COMMAND_GROUP);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -52,6 +57,9 @@ public class NiFiCommandGroup extends AbstractCommandGroup {
|
||||||
commands.add(new PGStart());
|
commands.add(new PGStart());
|
||||||
commands.add(new PGStop());
|
commands.add(new PGStop());
|
||||||
commands.add(new PGGetVars());
|
commands.add(new PGGetVars());
|
||||||
|
commands.add(new PGGetVersion());
|
||||||
|
commands.add(new PGChangeVersion());
|
||||||
|
commands.add(new PGGetAllVersions());
|
||||||
return new ArrayList<>(commands);
|
return new ArrayList<>(commands);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.nifi.toolkit.cli.impl.command.nifi.pg;
|
||||||
|
|
||||||
|
import org.apache.commons.cli.MissingOptionException;
|
||||||
|
import org.apache.nifi.toolkit.cli.api.CommandException;
|
||||||
|
import org.apache.nifi.toolkit.cli.api.Context;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.client.nifi.FlowClient;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.client.nifi.VersionsClient;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiCommand;
|
||||||
|
import org.apache.nifi.web.api.dto.VersionControlInformationDTO;
|
||||||
|
import org.apache.nifi.web.api.entity.VersionControlInformationEntity;
|
||||||
|
import org.apache.nifi.web.api.entity.VersionedFlowSnapshotMetadataEntity;
|
||||||
|
import org.apache.nifi.web.api.entity.VersionedFlowSnapshotMetadataSetEntity;
|
||||||
|
import org.apache.nifi.web.api.entity.VersionedFlowUpdateRequestEntity;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to change the version of a version controlled process group.
|
||||||
|
*/
|
||||||
|
public class PGChangeVersion extends AbstractNiFiCommand {
|
||||||
|
|
||||||
|
public PGChangeVersion() {
|
||||||
|
super("pg-change-version");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doInitialize(final Context context) {
|
||||||
|
addOption(CommandOption.PG_ID.createOption());
|
||||||
|
addOption(CommandOption.FLOW_VERSION.createOption());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doExecute(final NiFiClient client, final Properties properties)
|
||||||
|
throws NiFiClientException, IOException, MissingOptionException, CommandException {
|
||||||
|
final String pgId = getRequiredArg(properties, CommandOption.PG_ID);
|
||||||
|
|
||||||
|
final VersionsClient versionsClient = client.getVersionsClient();
|
||||||
|
final VersionControlInformationEntity existingVersionControlInfo = versionsClient.getVersionControlInfo(pgId);
|
||||||
|
final VersionControlInformationDTO existingVersionControlDTO = existingVersionControlInfo.getVersionControlInformation();
|
||||||
|
|
||||||
|
if (existingVersionControlDTO == null) {
|
||||||
|
throw new NiFiClientException("Process group is not under version control");
|
||||||
|
}
|
||||||
|
|
||||||
|
// start with the version specified in the arguments
|
||||||
|
Integer newVersion = getIntArg(properties, CommandOption.FLOW_VERSION);
|
||||||
|
|
||||||
|
// if no version was specified, automatically determine the latest and change to that
|
||||||
|
if (newVersion == null) {
|
||||||
|
newVersion = getLatestVersion(client, existingVersionControlDTO);
|
||||||
|
|
||||||
|
if (newVersion.intValue() == existingVersionControlDTO.getVersion().intValue()) {
|
||||||
|
throw new NiFiClientException("Process group already at latest version");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the version in the existing DTO to the new version so we can submit it back
|
||||||
|
existingVersionControlDTO.setVersion(newVersion);
|
||||||
|
|
||||||
|
final VersionedFlowUpdateRequestEntity initialUpdateRequest = versionsClient.updateVersionControlInfo(pgId, existingVersionControlInfo);
|
||||||
|
|
||||||
|
final String updateRequestId = initialUpdateRequest.getRequest().getRequestId();
|
||||||
|
try {
|
||||||
|
boolean completed = false;
|
||||||
|
for (int i = 0; i < 30; i++) {
|
||||||
|
final VersionedFlowUpdateRequestEntity updateRequest = versionsClient.getUpdateRequest(updateRequestId);
|
||||||
|
if (updateRequest != null && updateRequest.getRequest().isComplete()) {
|
||||||
|
completed = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!completed) {
|
||||||
|
throw new NiFiClientException("Unable to change version of process group, cancelling request");
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
versionsClient.deleteUpdateRequest(updateRequestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getLatestVersion(final NiFiClient client, final VersionControlInformationDTO existingVersionControlDTO)
|
||||||
|
throws NiFiClientException, IOException {
|
||||||
|
final FlowClient flowClient = client.getFlowClient();
|
||||||
|
|
||||||
|
final String registryId = existingVersionControlDTO.getRegistryId();
|
||||||
|
final String bucketId = existingVersionControlDTO.getBucketId();
|
||||||
|
final String flowId = existingVersionControlDTO.getFlowId();
|
||||||
|
|
||||||
|
final VersionedFlowSnapshotMetadataSetEntity versions = flowClient.getVersions(registryId, bucketId, flowId);
|
||||||
|
if (versions.getVersionedFlowSnapshotMetadataSet() == null || versions.getVersionedFlowSnapshotMetadataSet().isEmpty()) {
|
||||||
|
throw new NiFiClientException("No versions available");
|
||||||
|
}
|
||||||
|
|
||||||
|
int latestVersion = 1;
|
||||||
|
for (VersionedFlowSnapshotMetadataEntity version : versions.getVersionedFlowSnapshotMetadataSet()) {
|
||||||
|
if (version.getVersionedFlowSnapshotMetadata().getVersion() > latestVersion) {
|
||||||
|
latestVersion = version.getVersionedFlowSnapshotMetadata().getVersion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return latestVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.nifi.toolkit.cli.impl.command.nifi.pg;
|
||||||
|
|
||||||
|
import org.apache.commons.cli.MissingOptionException;
|
||||||
|
import org.apache.nifi.toolkit.cli.api.CommandException;
|
||||||
|
import org.apache.nifi.toolkit.cli.api.Context;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.client.nifi.FlowClient;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.client.nifi.VersionsClient;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiCommand;
|
||||||
|
import org.apache.nifi.web.api.dto.VersionControlInformationDTO;
|
||||||
|
import org.apache.nifi.web.api.entity.VersionControlInformationEntity;
|
||||||
|
import org.apache.nifi.web.api.entity.VersionedFlowSnapshotMetadataSetEntity;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to get all the available versions for a given process group that is under version control.
|
||||||
|
*/
|
||||||
|
public class PGGetAllVersions extends AbstractNiFiCommand {
|
||||||
|
|
||||||
|
public PGGetAllVersions() {
|
||||||
|
super("pg-get-all-versions");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doInitialize(final Context context) {
|
||||||
|
addOption(CommandOption.PG_ID.createOption());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doExecute(final NiFiClient client, final Properties properties)
|
||||||
|
throws NiFiClientException, IOException, MissingOptionException, CommandException {
|
||||||
|
final String pgId = getRequiredArg(properties, CommandOption.PG_ID);
|
||||||
|
|
||||||
|
final VersionsClient versionsClient = client.getVersionsClient();
|
||||||
|
final VersionControlInformationEntity existingVersionControlInfo = versionsClient.getVersionControlInfo(pgId);
|
||||||
|
final VersionControlInformationDTO existingVersionControlDTO = existingVersionControlInfo.getVersionControlInformation();
|
||||||
|
|
||||||
|
if (existingVersionControlDTO == null) {
|
||||||
|
throw new NiFiClientException("Process group is not under version control");
|
||||||
|
}
|
||||||
|
|
||||||
|
final String registryId = existingVersionControlDTO.getRegistryId();
|
||||||
|
final String bucketId = existingVersionControlDTO.getBucketId();
|
||||||
|
final String flowId = existingVersionControlDTO.getFlowId();
|
||||||
|
|
||||||
|
final FlowClient flowClient = client.getFlowClient();
|
||||||
|
final VersionedFlowSnapshotMetadataSetEntity versions = flowClient.getVersions(registryId, bucketId, flowId);
|
||||||
|
|
||||||
|
if (versions.getVersionedFlowSnapshotMetadataSet() == null || versions.getVersionedFlowSnapshotMetadataSet().isEmpty()) {
|
||||||
|
throw new NiFiClientException("No versions available");
|
||||||
|
}
|
||||||
|
|
||||||
|
writeResult(properties, versions);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.nifi.toolkit.cli.impl.command.nifi.pg;
|
||||||
|
|
||||||
|
import org.apache.commons.cli.MissingOptionException;
|
||||||
|
import org.apache.nifi.toolkit.cli.api.CommandException;
|
||||||
|
import org.apache.nifi.toolkit.cli.api.Context;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiCommand;
|
||||||
|
import org.apache.nifi.web.api.entity.VersionControlInformationEntity;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to get the version control info for a given process group.
|
||||||
|
*/
|
||||||
|
public class PGGetVersion extends AbstractNiFiCommand {
|
||||||
|
|
||||||
|
public PGGetVersion() {
|
||||||
|
super("pg-get-version");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doInitialize(final Context context) {
|
||||||
|
addOption(CommandOption.PG_ID.createOption());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doExecute(final NiFiClient client, final Properties properties)
|
||||||
|
throws NiFiClientException, IOException, MissingOptionException, CommandException {
|
||||||
|
final String pgId = getRequiredArg(properties, CommandOption.PG_ID);
|
||||||
|
final VersionControlInformationEntity entity = client.getVersionsClient().getVersionControlInfo(pgId);
|
||||||
|
if (entity.getVersionControlInformation() == null) {
|
||||||
|
throw new NiFiClientException("Process group is not under version control");
|
||||||
|
}
|
||||||
|
writeResult(properties, entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -35,8 +35,10 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public class NiFiRegistryCommandGroup extends AbstractCommandGroup {
|
public class NiFiRegistryCommandGroup extends AbstractCommandGroup {
|
||||||
|
|
||||||
|
public static String REGISTRY_COMMAND_GROUP = "registry";
|
||||||
|
|
||||||
public NiFiRegistryCommandGroup() {
|
public NiFiRegistryCommandGroup() {
|
||||||
super("registry");
|
super(REGISTRY_COMMAND_GROUP);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -64,7 +64,7 @@ public class ImportFlowVersion extends AbstractNiFiRegistryCommand {
|
||||||
|
|
||||||
// determine the bucket for the provided flow id
|
// determine the bucket for the provided flow id
|
||||||
final String bucketId = getBucketId(client, flowId);
|
final String bucketId = getBucketId(client, flowId);
|
||||||
|
|
||||||
// determine the latest existing version in the destination system
|
// determine the latest existing version in the destination system
|
||||||
Integer version;
|
Integer version;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -34,12 +34,14 @@ public class StandardContext implements Context {
|
||||||
private final ClientFactory<NiFiRegistryClient> niFiRegistryClientFactory;
|
private final ClientFactory<NiFiRegistryClient> niFiRegistryClientFactory;
|
||||||
private final Session session;
|
private final Session session;
|
||||||
private final PrintStream output;
|
private final PrintStream output;
|
||||||
|
private final boolean isInteractive;
|
||||||
|
|
||||||
private StandardContext(final Builder builder) {
|
private StandardContext(final Builder builder) {
|
||||||
this.niFiClientFactory = builder.niFiClientFactory;
|
this.niFiClientFactory = builder.niFiClientFactory;
|
||||||
this.niFiRegistryClientFactory = builder.niFiRegistryClientFactory;
|
this.niFiRegistryClientFactory = builder.niFiRegistryClientFactory;
|
||||||
this.session = builder.session;
|
this.session = builder.session;
|
||||||
this.output = builder.output;
|
this.output = builder.output;
|
||||||
|
this.isInteractive = builder.isInteractive;
|
||||||
|
|
||||||
Validate.notNull(this.niFiClientFactory);
|
Validate.notNull(this.niFiClientFactory);
|
||||||
Validate.notNull(this.niFiRegistryClientFactory);
|
Validate.notNull(this.niFiRegistryClientFactory);
|
||||||
|
@ -67,11 +69,17 @@ public class StandardContext implements Context {
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInteractive() {
|
||||||
|
return isInteractive;
|
||||||
|
}
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
private ClientFactory<NiFiClient> niFiClientFactory;
|
private ClientFactory<NiFiClient> niFiClientFactory;
|
||||||
private ClientFactory<NiFiRegistryClient> niFiRegistryClientFactory;
|
private ClientFactory<NiFiRegistryClient> niFiRegistryClientFactory;
|
||||||
private Session session;
|
private Session session;
|
||||||
private PrintStream output;
|
private PrintStream output;
|
||||||
|
private boolean isInteractive;
|
||||||
|
|
||||||
public Builder nifiClientFactory(final ClientFactory<NiFiClient> niFiClientFactory) {
|
public Builder nifiClientFactory(final ClientFactory<NiFiClient> niFiClientFactory) {
|
||||||
this.niFiClientFactory = niFiClientFactory;
|
this.niFiClientFactory = niFiClientFactory;
|
||||||
|
@ -93,6 +101,11 @@ public class StandardContext implements Context {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder interactive(final boolean isInteractive) {
|
||||||
|
this.isInteractive = isInteractive;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public StandardContext build() {
|
public StandardContext build() {
|
||||||
return new StandardContext(this);
|
return new StandardContext(this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.apache.nifi.toolkit.cli.api.ClientFactory;
|
||||||
import org.apache.nifi.toolkit.cli.api.Command;
|
import org.apache.nifi.toolkit.cli.api.Command;
|
||||||
import org.apache.nifi.toolkit.cli.api.Context;
|
import org.apache.nifi.toolkit.cli.api.Context;
|
||||||
import org.apache.nifi.toolkit.cli.api.Session;
|
import org.apache.nifi.toolkit.cli.api.Session;
|
||||||
|
import org.apache.nifi.toolkit.cli.impl.command.registry.NiFiRegistryCommandGroup;
|
||||||
import org.apache.nifi.toolkit.cli.impl.context.StandardContext;
|
import org.apache.nifi.toolkit.cli.impl.context.StandardContext;
|
||||||
import org.apache.nifi.toolkit.cli.impl.session.InMemorySession;
|
import org.apache.nifi.toolkit.cli.impl.session.InMemorySession;
|
||||||
import org.apache.nifi.toolkit.cli.impl.session.SessionVariables;
|
import org.apache.nifi.toolkit.cli.impl.session.SessionVariables;
|
||||||
|
@ -92,7 +93,7 @@ public class TestCLICompleter {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCompletionWithWordIndexOneAndMatching() {
|
public void testCompletionWithWordIndexOneAndMatching() {
|
||||||
final String topCommand = "nifi-reg";
|
final String topCommand = NiFiRegistryCommandGroup.REGISTRY_COMMAND_GROUP;
|
||||||
|
|
||||||
final DefaultParser.ArgumentList parsedLine = new DefaultParser.ArgumentList(
|
final DefaultParser.ArgumentList parsedLine = new DefaultParser.ArgumentList(
|
||||||
"", Collections.singletonList(topCommand), 1, -1, -1);
|
"", Collections.singletonList(topCommand), 1, -1, -1);
|
||||||
|
@ -116,7 +117,7 @@ public class TestCLICompleter {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCompletionWithWordIndexTwoAndMatching() {
|
public void testCompletionWithWordIndexTwoAndMatching() {
|
||||||
final String topCommand = "nifi-reg";
|
final String topCommand = NiFiRegistryCommandGroup.REGISTRY_COMMAND_GROUP;
|
||||||
final String subCommand = "list-buckets";
|
final String subCommand = "list-buckets";
|
||||||
|
|
||||||
final DefaultParser.ArgumentList parsedLine = new DefaultParser.ArgumentList(
|
final DefaultParser.ArgumentList parsedLine = new DefaultParser.ArgumentList(
|
||||||
|
@ -130,7 +131,7 @@ public class TestCLICompleter {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCompletionWithWordIndexTwoAndNotMatching() {
|
public void testCompletionWithWordIndexTwoAndNotMatching() {
|
||||||
final String topCommand = "nifi-reg";
|
final String topCommand = NiFiRegistryCommandGroup.REGISTRY_COMMAND_GROUP;
|
||||||
final String subCommand = "NOT-A-TOP-LEVEL-COMMAND";
|
final String subCommand = "NOT-A-TOP-LEVEL-COMMAND";
|
||||||
|
|
||||||
final DefaultParser.ArgumentList parsedLine = new DefaultParser.ArgumentList(
|
final DefaultParser.ArgumentList parsedLine = new DefaultParser.ArgumentList(
|
||||||
|
@ -143,7 +144,7 @@ public class TestCLICompleter {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCompletionWithMultipleArguments() {
|
public void testCompletionWithMultipleArguments() {
|
||||||
final String topCommand = "nifi-reg";
|
final String topCommand = NiFiRegistryCommandGroup.REGISTRY_COMMAND_GROUP;
|
||||||
final String subCommand = "list-buckets";
|
final String subCommand = "list-buckets";
|
||||||
|
|
||||||
final DefaultParser.ArgumentList parsedLine = new DefaultParser.ArgumentList(
|
final DefaultParser.ArgumentList parsedLine = new DefaultParser.ArgumentList(
|
||||||
|
@ -157,7 +158,7 @@ public class TestCLICompleter {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCompletionWithFileArguments() {
|
public void testCompletionWithFileArguments() {
|
||||||
final String topCommand = "nifi-reg";
|
final String topCommand = NiFiRegistryCommandGroup.REGISTRY_COMMAND_GROUP;
|
||||||
final String subCommand = "list-buckets";
|
final String subCommand = "list-buckets";
|
||||||
|
|
||||||
final DefaultParser.ArgumentList parsedLine = new DefaultParser.ArgumentList(
|
final DefaultParser.ArgumentList parsedLine = new DefaultParser.ArgumentList(
|
||||||
|
|
Loading…
Reference in New Issue