NIFIREG-321 Integrate revision concept into UI, REST API, and service layers
NIFIREG-321 Making response message more user friendly when encountering InvalidRevisionException
NIFIREG-321 Updating frontend to be consistent with revision error handling
NIFIREG-321 Verifying revisable entity exists on update/delete before checking revisions to produce appropriate response codes (404 vs 400)
NIFIREG-321 Fixing SecureFileIT to correctly delete entities so test-all-dbs profile passes
This closes #230.
Signed-off-by: Kevin Doran <kdoran@apache.org>
diff --git a/nifi-registry-core/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/BucketClient.java b/nifi-registry-core/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/BucketClient.java
index 80f72bb..87a0592 100644
--- a/nifi-registry-core/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/BucketClient.java
+++ b/nifi-registry-core/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/BucketClient.java
@@ -18,6 +18,7 @@
import org.apache.nifi.registry.bucket.Bucket;
import org.apache.nifi.registry.field.Fields;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
import java.io.IOException;
import java.util.List;
@@ -55,9 +56,10 @@
* Deletes the bucket with the given id.
*
* @param bucketId the id of the bucket to delete
+ * @param revision the revision info for the bucket being deleted
* @return the deleted bucket
*/
- Bucket delete(String bucketId) throws NiFiRegistryException, IOException;
+ Bucket delete(String bucketId, RevisionInfo revision) throws NiFiRegistryException, IOException;
/**
* Gets the fields that can be used to sort/search buckets.
diff --git a/nifi-registry-core/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/FlowClient.java b/nifi-registry-core/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/FlowClient.java
index 6dd72e9..73f4e91 100644
--- a/nifi-registry-core/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/FlowClient.java
+++ b/nifi-registry-core/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/FlowClient.java
@@ -19,6 +19,7 @@
import org.apache.nifi.registry.diff.VersionedFlowDifference;
import org.apache.nifi.registry.field.Fields;
import org.apache.nifi.registry.flow.VersionedFlow;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
import java.io.IOException;
import java.util.List;
@@ -79,11 +80,12 @@
*
* @param bucketId a bucket id
* @param flowId the id of the flow to delete
+ * @param revision the revision information for the entity being deleted
* @return the deleted flow
* @throws NiFiRegistryException if an error is encountered other than IOException
* @throws IOException if an I/O error is encountered
*/
- VersionedFlow delete(String bucketId, String flowId) throws NiFiRegistryException, IOException;
+ VersionedFlow delete(String bucketId, String flowId, RevisionInfo revision) throws NiFiRegistryException, IOException;
/**
* Gets the field info for flows.
diff --git a/nifi-registry-core/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/AbstractJerseyClient.java b/nifi-registry-core/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/AbstractJerseyClient.java
index 479699e..dd9792d 100644
--- a/nifi-registry-core/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/AbstractJerseyClient.java
+++ b/nifi-registry-core/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/AbstractJerseyClient.java
@@ -16,7 +16,9 @@
*/
package org.apache.nifi.registry.client.impl;
+import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.client.NiFiRegistryException;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.Invocation;
@@ -45,6 +47,32 @@
}
/**
+ * Adds query parameters for the given RevisionInfo if populated.
+ *
+ * @param target the WebTarget
+ * @param revision the RevisionInfo
+ * @return the target with query params added
+ */
+ protected WebTarget addRevisionQueryParams(WebTarget target, RevisionInfo revision) {
+ if (revision == null) {
+ return target;
+ }
+
+ WebTarget localTarget = target;
+
+ final Long version = revision.getVersion();
+ if (version != null) {
+ localTarget = localTarget.queryParam("version", version.longValue());
+ }
+
+ final String clientId = revision.getClientId();
+ if (!StringUtils.isBlank(clientId)) {
+ localTarget = localTarget.queryParam("clientId", clientId);
+ }
+ return localTarget;
+ }
+
+ /**
* Creates a new Invocation.Builder for the given WebTarget with the headers added to the builder.
*
* @param webTarget the target for the request
diff --git a/nifi-registry-core/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyBucketClient.java b/nifi-registry-core/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyBucketClient.java
index f84f8c6..2be7b20 100644
--- a/nifi-registry-core/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyBucketClient.java
+++ b/nifi-registry-core/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyBucketClient.java
@@ -21,6 +21,7 @@
import org.apache.nifi.registry.client.BucketClient;
import org.apache.nifi.registry.client.NiFiRegistryException;
import org.apache.nifi.registry.field.Fields;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
@@ -105,16 +106,22 @@
}
@Override
- public Bucket delete(final String bucketId) throws NiFiRegistryException, IOException {
+ public Bucket delete(final String bucketId, final RevisionInfo revision) throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(bucketId)) {
throw new IllegalArgumentException("Bucket ID cannot be blank");
}
+ if (revision == null) {
+ throw new IllegalArgumentException("RevisionInfo cannot be null");
+ }
+
return executeAction("Error deleting bucket", () -> {
- final WebTarget target = bucketsTarget
+ WebTarget target = bucketsTarget
.path("/{bucketId}")
.resolveTemplate("bucketId", bucketId);
+ target = addRevisionQueryParams(target, revision);
+
return getRequestBuilder(target).delete(Bucket.class);
});
}
diff --git a/nifi-registry-core/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyFlowClient.java b/nifi-registry-core/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyFlowClient.java
index 486a20a..2e6d4e3 100644
--- a/nifi-registry-core/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyFlowClient.java
+++ b/nifi-registry-core/nifi-registry-client/src/main/java/org/apache/nifi/registry/client/impl/JerseyFlowClient.java
@@ -22,6 +22,7 @@
import org.apache.nifi.registry.diff.VersionedFlowDifference;
import org.apache.nifi.registry.field.Fields;
import org.apache.nifi.registry.flow.VersionedFlow;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
@@ -138,7 +139,8 @@
}
@Override
- public VersionedFlow delete(final String bucketId, final String flowId) throws NiFiRegistryException, IOException {
+ public VersionedFlow delete(final String bucketId, final String flowId, final RevisionInfo revision)
+ throws NiFiRegistryException, IOException {
if (StringUtils.isBlank(bucketId)) {
throw new IllegalArgumentException("Bucket Identifier cannot be blank");
}
@@ -147,12 +149,18 @@
throw new IllegalArgumentException("Flow Identifier cannot be blank");
}
+ if (revision == null) {
+ throw new IllegalArgumentException("RevisionInfo cannot be null");
+ }
+
return executeAction("Error deleting flow", () -> {
- final WebTarget target = bucketFlowsTarget
+ WebTarget target = bucketFlowsTarget
.path("/{flowId}")
.resolveTemplate("bucketId", bucketId)
.resolveTemplate("flowId", flowId);
+ target = addRevisionQueryParams(target, revision);
+
return getRequestBuilder(target).delete(VersionedFlow.class);
});
}
diff --git a/nifi-registry-core/nifi-registry-data-model/pom.xml b/nifi-registry-core/nifi-registry-data-model/pom.xml
index 05352f7..c404598 100644
--- a/nifi-registry-core/nifi-registry-data-model/pom.xml
+++ b/nifi-registry-core/nifi-registry-data-model/pom.xml
@@ -33,6 +33,11 @@
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.nifi.registry</groupId>
+ <artifactId>nifi-registry-revision-entity-model</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </dependency>
</dependencies>
<profiles>
diff --git a/nifi-registry-core/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/AccessPolicySummary.java b/nifi-registry-core/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/AccessPolicySummary.java
index 909299e..e432dc4 100644
--- a/nifi-registry-core/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/AccessPolicySummary.java
+++ b/nifi-registry-core/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/AccessPolicySummary.java
@@ -19,23 +19,28 @@
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
+import org.apache.nifi.registry.revision.entity.RevisableEntity;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
/**
* Access policy summary of which actions ("read', "write", "delete") are allowable for a specified web resource.
*/
@ApiModel
-public class AccessPolicySummary {
+public class AccessPolicySummary implements RevisableEntity {
private String identifier;
private String resource;
private String action;
private Boolean configurable;
+ private RevisionInfo revision;
@ApiModelProperty(value = "The id of the policy. Set by server at creation time.", readOnly = true)
+ @Override
public String getIdentifier() {
return identifier;
}
+ @Override
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
@@ -71,4 +76,19 @@
public void setConfigurable(Boolean configurable) {
this.configurable = configurable;
}
+
+ @ApiModelProperty(
+ value = "The revision of this entity used for optimistic-locking during updates.",
+ readOnly = true
+ )
+ @Override
+ public RevisionInfo getRevision() {
+ return revision;
+ }
+
+ @Override
+ public void setRevision(RevisionInfo revision) {
+ this.revision = revision;
+ }
+
}
diff --git a/nifi-registry-core/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/Tenant.java b/nifi-registry-core/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/Tenant.java
index 59d8c17..7d416ae 100644
--- a/nifi-registry-core/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/Tenant.java
+++ b/nifi-registry-core/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/authorization/Tenant.java
@@ -18,6 +18,8 @@
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
+import org.apache.nifi.registry.revision.entity.RevisableEntity;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
import java.util.Collection;
import java.util.HashSet;
@@ -27,13 +29,14 @@
* A tenant of this NiFi Registry
*/
@ApiModel
-public class Tenant {
+public class Tenant implements RevisableEntity {
private String identifier;
private String identity;
private Boolean configurable;
private ResourcePermissions resourcePermissions;
private Set<AccessPolicySummary> accessPolicies;
+ private RevisionInfo revision;
public Tenant() {}
@@ -48,10 +51,12 @@
@ApiModelProperty(
value = "The computer-generated identifier of the tenant.",
readOnly = true)
+ @Override
public String getIdentifier() {
return identifier;
}
+ @Override
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
@@ -114,4 +119,17 @@
}
}
+ @ApiModelProperty(
+ value = "The revision of this entity used for optimistic-locking during updates.",
+ readOnly = true
+ )
+ @Override
+ public RevisionInfo getRevision() {
+ return revision;
+ }
+
+ @Override
+ public void setRevision(RevisionInfo revision) {
+ this.revision = revision;
+ }
}
diff --git a/nifi-registry-core/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/bucket/Bucket.java b/nifi-registry-core/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/bucket/Bucket.java
index 9cd82e0..e94c38e 100644
--- a/nifi-registry-core/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/bucket/Bucket.java
+++ b/nifi-registry-core/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/bucket/Bucket.java
@@ -20,6 +20,8 @@
import io.swagger.annotations.ApiModelProperty;
import org.apache.nifi.registry.authorization.Permissions;
import org.apache.nifi.registry.link.LinkableEntity;
+import org.apache.nifi.registry.revision.entity.RevisableEntity;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
@@ -28,7 +30,7 @@
@XmlRootElement
@ApiModel
-public class Bucket extends LinkableEntity {
+public class Bucket extends LinkableEntity implements RevisableEntity {
@NotBlank
private String identifier;
@@ -47,6 +49,8 @@
private Permissions permissions;
+ private RevisionInfo revision;
+
@ApiModelProperty(value = "An ID to uniquely identify this object.", readOnly = true)
public String getIdentifier() {
return identifier;
@@ -110,6 +114,20 @@
this.permissions = permissions;
}
+ @ApiModelProperty(
+ value = "The revision of this entity used for optimistic-locking during updates.",
+ readOnly = true
+ )
+ @Override
+ public RevisionInfo getRevision() {
+ return revision;
+ }
+
+ @Override
+ public void setRevision(RevisionInfo revision) {
+ this.revision = revision;
+ }
+
@Override
public int hashCode() {
return Objects.hashCode(this.getIdentifier());
diff --git a/nifi-registry-core/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/bucket/BucketItem.java b/nifi-registry-core/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/bucket/BucketItem.java
index 98a873d..09fe35b 100644
--- a/nifi-registry-core/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/bucket/BucketItem.java
+++ b/nifi-registry-core/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/bucket/BucketItem.java
@@ -54,6 +54,7 @@
private Permissions permissions;
+
public BucketItem(final BucketItemType type) {
this.type = type;
}
diff --git a/nifi-registry-core/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/flow/VersionedFlow.java b/nifi-registry-core/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/flow/VersionedFlow.java
index 5c5a43a..818dddd 100644
--- a/nifi-registry-core/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/flow/VersionedFlow.java
+++ b/nifi-registry-core/nifi-registry-data-model/src/main/java/org/apache/nifi/registry/flow/VersionedFlow.java
@@ -20,9 +20,12 @@
import io.swagger.annotations.ApiModelProperty;
import org.apache.nifi.registry.bucket.BucketItem;
import org.apache.nifi.registry.bucket.BucketItemType;
+import org.apache.nifi.registry.revision.entity.RevisableEntity;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
import javax.validation.constraints.Min;
import javax.xml.bind.annotation.XmlRootElement;
+import java.util.Objects;
/**
* <p>
@@ -35,11 +38,13 @@
*/
@XmlRootElement
@ApiModel
-public class VersionedFlow extends BucketItem {
+public class VersionedFlow extends BucketItem implements RevisableEntity {
@Min(0)
private long versionCount;
+ private RevisionInfo revision;
+
public VersionedFlow() {
super(BucketItemType.Flow);
}
@@ -53,4 +58,23 @@
this.versionCount = versionCount;
}
+ @ApiModelProperty(
+ value = "The revision of this entity used for optimistic-locking during updates.",
+ readOnly = true
+ )
+ @Override
+ public RevisionInfo getRevision() {
+ return revision;
+ }
+
+ @Override
+ public void setRevision(RevisionInfo revision) {
+ this.revision = revision;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(this.getIdentifier());
+ }
+
}
diff --git a/nifi-registry-core/nifi-registry-framework/pom.xml b/nifi-registry-core/nifi-registry-framework/pom.xml
index 28ccc1b..67f77c4 100644
--- a/nifi-registry-core/nifi-registry-framework/pom.xml
+++ b/nifi-registry-core/nifi-registry-framework/pom.xml
@@ -193,6 +193,11 @@
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
+ <groupId>org.apache.nifi.registry</groupId>
+ <artifactId>nifi-registry-flow-diff</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
index 5190b64..12f39b6 100644
--- a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/AuthorizationService.java
@@ -16,6 +16,7 @@
*/
package org.apache.nifi.registry.service;
+import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.authorization.AccessPolicy;
import org.apache.nifi.registry.authorization.AccessPolicySummary;
import org.apache.nifi.registry.authorization.CurrentUser;
@@ -26,6 +27,7 @@
import org.apache.nifi.registry.authorization.User;
import org.apache.nifi.registry.authorization.UserGroup;
import org.apache.nifi.registry.bucket.Bucket;
+import org.apache.nifi.registry.exception.ResourceNotFoundException;
import org.apache.nifi.registry.security.authorization.AccessPolicyProvider;
import org.apache.nifi.registry.security.authorization.AccessPolicyProviderInitializationContext;
import org.apache.nifi.registry.security.authorization.AuthorizableLookup;
@@ -49,6 +51,8 @@
import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils;
import org.apache.nifi.registry.security.exception.SecurityProviderCreationException;
import org.apache.nifi.registry.security.exception.SecurityProviderDestructionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -57,14 +61,16 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;
+/**
+ * Service for performing operations on users, groups, and policies.
+ */
@Service
public class AuthorizationService {
+ private static final Logger LOGGER = LoggerFactory.getLogger(AuthorizationService.class);
+
public static final String MSG_NON_MANAGED_AUTHORIZER = "This NiFi Registry is not configured to internally manage users, groups, or policies. Please contact your system administrator.";
public static final String MSG_NON_CONFIGURABLE_POLICIES = "This NiFi Registry is not configured to allow configurable policies. Please contact your system administrator.";
public static final String MSG_NON_CONFIGURABLE_USERS = "This NiFi Registry is not configured to allow configurable users and groups. Please contact your system administrator.";
@@ -75,10 +81,6 @@
private UserGroupProvider userGroupProvider;
private AccessPolicyProvider accessPolicyProvider;
- private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
- private final Lock readLock = lock.readLock();
- private final Lock writeLock = lock.writeLock();
-
@Autowired
public AuthorizationService(
final AuthorizableLookup authorizableLookup,
@@ -103,14 +105,41 @@
return authorizableLookup;
}
- public Authorizer getAuthorizer() {
- return authorizer;
- }
-
public void authorize(Authorizable authorizable, RequestAction action) throws AccessDeniedException {
authorizable.authorize(authorizer, action, NiFiUserUtils.getNiFiUser());
}
+ public boolean isManagedAuthorizer() {
+ return AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer);
+ }
+
+ public boolean isConfigurableUserGroupProvider() {
+ return AuthorizerCapabilityDetection.isConfigurableUserGroupProvider(authorizer);
+ }
+
+ public boolean isConfigurableAccessPolicyProvider() {
+ return AuthorizerCapabilityDetection.isConfigurableAccessPolicyProvider(authorizer);
+ }
+
+ public void verifyAuthorizerIsManaged() {
+ if (!isManagedAuthorizer()) {
+ throw new IllegalStateException(AuthorizationService.MSG_NON_MANAGED_AUTHORIZER);
+ }
+ }
+
+ public void verifyAuthorizerSupportsConfigurablePolicies() {
+ if (!isConfigurableAccessPolicyProvider()) {
+ verifyAuthorizerIsManaged();
+ throw new IllegalStateException(AuthorizationService.MSG_NON_CONFIGURABLE_POLICIES);
+ }
+ }
+
+ public void verifyAuthorizerSupportsConfigurableUserGroups() {
+ if (!isConfigurableUserGroupProvider()) {
+ throw new IllegalStateException(AuthorizationService.MSG_NON_CONFIGURABLE_USERS);
+ }
+ }
+
// ---------------------- Permissions methods ---------------------------------------
public CurrentUser getCurrentUser() {
@@ -176,255 +205,259 @@
// ---------------------- User methods ----------------------------------------------
- public User createUser(User user) {
+ public User createUser(final User user) {
verifyUserGroupProviderIsConfigurable();
- writeLock.lock();
- try {
- final org.apache.nifi.registry.security.authorization.User createdUser =
- ((ConfigurableUserGroupProvider) userGroupProvider).addUser(userFromDTO(user));
- return userToDTO(createdUser);
- } finally {
- writeLock.unlock();
+
+ if (StringUtils.isBlank(user.getIdentity())) {
+ throw new IllegalArgumentException("User identity must be specified when creating a new user.");
}
+
+ final org.apache.nifi.registry.security.authorization.User createdUser =
+ configurableUserGroupProvider().addUser(userFromDTO(user));
+ return userToDTO(createdUser);
}
public List<User> getUsers() {
- this.readLock.lock();
- try {
- return userGroupProvider.getUsers().stream().map(this::userToDTO).collect(Collectors.toList());
- } finally {
- this.readLock.unlock();
+ return userGroupProvider.getUsers().stream().map(this::userToDTO).collect(Collectors.toList());
+ }
+
+ public User getUser(final String identifier) {
+ final org.apache.nifi.registry.security.authorization.User user = userGroupProvider.getUser(identifier);
+ if (user == null) {
+ LOGGER.warn("The specified user id [{}] does not exist.", identifier);
+ throw new ResourceNotFoundException("The specified user ID does not exist in this registry.");
+ }
+
+ return userToDTO(user);
+ }
+
+ public User getUserByIdentity(final String identity) {
+ final org.apache.nifi.registry.security.authorization.User user = userGroupProvider.getUserByIdentity(identity);
+ if (user == null) {
+ LOGGER.warn("The specified user identity [{}] does not exist.", identity);
+ throw new ResourceNotFoundException("The specified user ID does not exist in this registry.");
+ }
+
+ return userToDTO(user);
+ }
+
+ public void verifyUserExists(final String identifier) {
+ final org.apache.nifi.registry.security.authorization.User user = userGroupProvider.getUser(identifier);
+ if (user == null) {
+ LOGGER.warn("The specified user id [{}] does not exist.", identifier);
+ throw new ResourceNotFoundException("The specified user ID does not exist in this registry.");
}
}
- public User getUser(String identifier) {
- this.readLock.lock();
- try {
- return userToDTO(userGroupProvider.getUser(identifier));
- } finally {
- this.readLock.unlock();
- }
- }
-
- public User getUserByIdentity(String identity) {
- this.readLock.lock();
- try {
- return userToDTO(userGroupProvider.getUserByIdentity(identity));
- } finally {
- this.readLock.unlock();
- }
- }
-
- public User updateUser(User user) {
+ public User updateUser(final User user) {
verifyUserGroupProviderIsConfigurable();
- this.writeLock.lock();
- try {
- final org.apache.nifi.registry.security.authorization.User updatedUser =
- ((ConfigurableUserGroupProvider) userGroupProvider).updateUser(userFromDTO(user));
- if (updatedUser == null) {
- return null;
- }
- return userToDTO(updatedUser);
- } finally {
- this.writeLock.unlock();
+
+ final org.apache.nifi.registry.security.authorization.User updatedUser =
+ configurableUserGroupProvider().updateUser(userFromDTO(user));
+
+ if (updatedUser == null) {
+ LOGGER.warn("The specified user id [{}] does not exist.", user.getIdentifier());
+ throw new ResourceNotFoundException("The specified user ID does not exist in this registry.");
}
+
+ return userToDTO(updatedUser);
}
- public User deleteUser(String identifier) {
+ public User deleteUser(final String identifier) {
verifyUserGroupProviderIsConfigurable();
- this.writeLock.lock();
- try {
- User deletedUserDTO = getUser(identifier);
- if (deletedUserDTO != null) {
- ((ConfigurableUserGroupProvider) userGroupProvider).deleteUser(identifier);
- }
- return deletedUserDTO;
- } finally {
- this.writeLock.unlock();
- }
- }
+ final org.apache.nifi.registry.security.authorization.User user = userGroupProvider.getUser(identifier);
+ if (user == null) {
+ LOGGER.warn("The specified user id [{}] does not exist.", identifier);
+ throw new ResourceNotFoundException("The specified user ID does not exist in this registry.");
+ }
+
+ configurableUserGroupProvider().deleteUser(user);
+ return userToDTO(user);
+ }
// ---------------------- User Group methods --------------------------------------
- public UserGroup createUserGroup(UserGroup userGroup) {
+ public UserGroup createUserGroup(final UserGroup userGroup) {
verifyUserGroupProviderIsConfigurable();
- writeLock.lock();
- try {
- final org.apache.nifi.registry.security.authorization.Group createdGroup =
- ((ConfigurableUserGroupProvider) userGroupProvider).addGroup(userGroupFromDTO(userGroup));
- return userGroupToDTO(createdGroup);
- } finally {
- writeLock.unlock();
+
+ if (StringUtils.isBlank(userGroup.getIdentity())) {
+ throw new IllegalArgumentException("User group identity must be specified when creating a new group.");
}
+
+ final org.apache.nifi.registry.security.authorization.Group createdGroup =
+ configurableUserGroupProvider().addGroup(userGroupFromDTO(userGroup));
+ return userGroupToDTO(createdGroup);
}
public List<UserGroup> getUserGroups() {
- this.readLock.lock();
- try {
- return userGroupProvider.getGroups().stream().map(this::userGroupToDTO).collect(Collectors.toList());
- } finally {
- this.readLock.unlock();
+ return userGroupProvider.getGroups().stream().map(this::userGroupToDTO).collect(Collectors.toList());
+ }
+
+ public UserGroup getUserGroup(final String identifier) {
+ final org.apache.nifi.registry.security.authorization.Group group = userGroupProvider.getGroup(identifier);
+
+ if (group == null) {
+ LOGGER.warn("The specified user group id [{}] does not exist.", identifier);
+ throw new ResourceNotFoundException("The specified user group ID does not exist in this registry.");
+ }
+
+ return userGroupToDTO(group);
+ }
+
+ public void verifyUserGroupExists(final String identifier) {
+ final org.apache.nifi.registry.security.authorization.Group group = userGroupProvider.getGroup(identifier);
+ if (group == null) {
+ LOGGER.warn("The specified user group id [{}] does not exist.", identifier);
+ throw new ResourceNotFoundException("The specified user group ID does not exist in this registry.");
}
}
- public UserGroup getUserGroup(String identifier) {
- this.readLock.lock();
- try {
- return userGroupToDTO(userGroupProvider.getGroup(identifier));
- } finally {
- this.readLock.unlock();
- }
- }
-
- public UserGroup updateUserGroup(UserGroup userGroup) {
+ public UserGroup updateUserGroup(final UserGroup userGroup) {
verifyUserGroupProviderIsConfigurable();
- writeLock.lock();
- try {
- final org.apache.nifi.registry.security.authorization.Group updatedGroup =
- ((ConfigurableUserGroupProvider) userGroupProvider).updateGroup(userGroupFromDTO(userGroup));
- if (updatedGroup == null) {
- return null;
- }
- return userGroupToDTO(updatedGroup);
- } finally {
- writeLock.unlock();
+
+ final org.apache.nifi.registry.security.authorization.Group updatedGroup =
+ configurableUserGroupProvider().updateGroup(userGroupFromDTO(userGroup));
+
+ if (updatedGroup == null) {
+ LOGGER.warn("The specified user group id [{}] does not exist.", userGroup.getIdentifier());
+ throw new ResourceNotFoundException("The specified user group ID does not exist in this registry.");
}
+
+ return userGroupToDTO(updatedGroup);
}
- public UserGroup deleteUserGroup(String identifier) {
+ public UserGroup deleteUserGroup(final String identifier) {
verifyUserGroupProviderIsConfigurable();
- writeLock.lock();
- try {
- final UserGroup userGroupDTO = getUserGroup(identifier);
- if (userGroupDTO != null) {
- ((ConfigurableUserGroupProvider) userGroupProvider).deleteGroup(identifier);
- }
- return userGroupDTO;
- } finally {
- writeLock.unlock();
+
+ final Group group = userGroupProvider.getGroup(identifier);
+ if (group == null) {
+ LOGGER.warn("The specified user group id [{}] does not exist.", group.getIdentifier());
+ throw new ResourceNotFoundException("The specified user group ID does not exist in this registry.");
}
+
+ configurableUserGroupProvider().deleteGroup(group);
+ return userGroupToDTO(group);
}
// ---------------------- Access Policy methods ----------------------------------------
- public AccessPolicy createAccessPolicy(AccessPolicy accessPolicy) {
+ public AccessPolicy createAccessPolicy(final AccessPolicy accessPolicy) {
verifyAccessPolicyProviderIsConfigurable();
- writeLock.lock();
- try {
- org.apache.nifi.registry.security.authorization.AccessPolicy createdAccessPolicy =
- ((ConfigurableAccessPolicyProvider) accessPolicyProvider).addAccessPolicy(accessPolicyFromDTO(accessPolicy));
- return accessPolicyToDTO(createdAccessPolicy);
- } finally {
- writeLock.unlock();
+
+ if (accessPolicy.getResource() == null) {
+ throw new IllegalArgumentException("Resource must be specified when creating a new access policy.");
}
+
+ RequestAction.valueOfValue(accessPolicy.getAction());
+
+ final org.apache.nifi.registry.security.authorization.AccessPolicy createdAccessPolicy =
+ configurableAccessPolicyProvider().addAccessPolicy(accessPolicyFromDTO(accessPolicy));
+ return accessPolicyToDTO(createdAccessPolicy);
}
- public AccessPolicy getAccessPolicy(String identifier) {
- readLock.lock();
- try {
- return accessPolicyToDTO(accessPolicyProvider.getAccessPolicy(identifier));
- } finally {
- readLock.unlock();
+ public AccessPolicy getAccessPolicy(final String identifier) {
+ final org.apache.nifi.registry.security.authorization.AccessPolicy accessPolicy =
+ accessPolicyProvider.getAccessPolicy(identifier);
+
+ if (accessPolicy == null) {
+ LOGGER.warn("The specified access policy id [{}] does not exist.", identifier);
+ throw new ResourceNotFoundException("The specified policy does not exist in this registry.");
}
+
+ return accessPolicyToDTO(accessPolicy);
}
- public AccessPolicy getAccessPolicy(String resource, RequestAction action) {
- readLock.lock();
- try {
- return accessPolicyToDTO(accessPolicyProvider.getAccessPolicy(resource, action));
- } finally {
- readLock.unlock();
+ public AccessPolicy getAccessPolicy(final String resource, final RequestAction action) {
+ final org.apache.nifi.registry.security.authorization.AccessPolicy accessPolicy =
+ accessPolicyProvider.getAccessPolicy(resource, action);
+
+ if (accessPolicy == null) {
+ throw new ResourceNotFoundException("No policy found for action='" + action + "', resource='" + resource + "'");
}
+
+ return accessPolicyToDTO(accessPolicy);
}
public List<AccessPolicy> getAccessPolicies() {
- readLock.lock();
- try {
- return accessPolicyProvider.getAccessPolicies().stream().map(this::accessPolicyToDTO).collect(Collectors.toList());
- } finally {
- readLock.unlock();
- }
+ return accessPolicyProvider.getAccessPolicies().stream().map(this::accessPolicyToDTO).collect(Collectors.toList());
}
public List<AccessPolicySummary> getAccessPolicySummaries() {
- readLock.lock();
- try {
- return accessPolicyProvider.getAccessPolicies().stream().map(this::accessPolicyToSummaryDTO).collect(Collectors.toList());
- } finally {
- readLock.unlock();
- }
+ return accessPolicyProvider.getAccessPolicies().stream().map(this::accessPolicyToSummaryDTO).collect(Collectors.toList());
}
private List<AccessPolicySummary> getAccessPolicySummariesForUser(String userIdentifier) {
- readLock.lock();
- try {
- return accessPolicyProvider.getAccessPolicies().stream()
- .filter(accessPolicy -> {
- if (accessPolicy.getUsers().contains(userIdentifier)) {
- return true;
- }
- return accessPolicy.getGroups().stream().anyMatch(g -> {
- final Group group = userGroupProvider.getGroup(g);
- return group != null && group.getUsers().contains(userIdentifier);
- });
- })
- .map(this::accessPolicyToSummaryDTO)
- .collect(Collectors.toList());
- } finally {
- readLock.unlock();
- }
+ return accessPolicyProvider.getAccessPolicies().stream()
+ .filter(accessPolicy -> {
+ if (accessPolicy.getUsers().contains(userIdentifier)) {
+ return true;
+ }
+ return accessPolicy.getGroups().stream().anyMatch(g -> {
+ final Group group = userGroupProvider.getGroup(g);
+ return group != null && group.getUsers().contains(userIdentifier);
+ });
+ })
+ .map(this::accessPolicyToSummaryDTO)
+ .collect(Collectors.toList());
}
private List<AccessPolicySummary> getAccessPolicySummariesForUserGroup(String userGroupIdentifier) {
- readLock.lock();
- try {
- return accessPolicyProvider.getAccessPolicies().stream()
- .filter(accessPolicy -> accessPolicy.getGroups().contains(userGroupIdentifier))
- .map(this::accessPolicyToSummaryDTO)
- .collect(Collectors.toList());
- } finally {
- readLock.unlock();
+ return accessPolicyProvider.getAccessPolicies().stream()
+ .filter(accessPolicy -> accessPolicy.getGroups().contains(userGroupIdentifier))
+ .map(this::accessPolicyToSummaryDTO)
+ .collect(Collectors.toList());
+ }
+
+ public void verifyAccessPolicyExists(final String identifier) {
+ final org.apache.nifi.registry.security.authorization.AccessPolicy accessPolicy =
+ accessPolicyProvider.getAccessPolicy(identifier);
+ if (accessPolicy == null) {
+ LOGGER.warn("The specified access policy id [{}] does not exist.", identifier);
+ throw new ResourceNotFoundException("The specified policy does not exist in this registry.");
}
}
- public AccessPolicy updateAccessPolicy(AccessPolicy accessPolicy) {
+ public AccessPolicy updateAccessPolicy(final AccessPolicy accessPolicy) {
verifyAccessPolicyProviderIsConfigurable();
- writeLock.lock();
- try {
- // Don't allow changing action or resource of existing policy (should only be adding/removing users/groups)
- org.apache.nifi.registry.security.authorization.AccessPolicy currentAccessPolicy =
- accessPolicyProvider.getAccessPolicy(accessPolicy.getIdentifier());
- if (currentAccessPolicy == null) {
- return null;
- }
- accessPolicy.setResource(currentAccessPolicy.getResource());
- accessPolicy.setAction(currentAccessPolicy.getAction().toString());
- org.apache.nifi.registry.security.authorization.AccessPolicy updatedAccessPolicy =
- ((ConfigurableAccessPolicyProvider) accessPolicyProvider).updateAccessPolicy(accessPolicyFromDTO(accessPolicy));
- if (updatedAccessPolicy == null) {
- return null;
- }
- return accessPolicyToDTO(updatedAccessPolicy);
- } finally {
- writeLock.unlock();
+ // Don't allow changing action or resource of existing policy (should only be adding/removing users/groups)
+ final org.apache.nifi.registry.security.authorization.AccessPolicy currentAccessPolicy =
+ accessPolicyProvider.getAccessPolicy(accessPolicy.getIdentifier());
+
+ if (currentAccessPolicy == null) {
+ LOGGER.warn("The specified access policy id [{}] does not exist.", accessPolicy.getIdentifier());
+ throw new ResourceNotFoundException("The specified policy does not exist in this registry.");
}
+
+ accessPolicy.setResource(currentAccessPolicy.getResource());
+ accessPolicy.setAction(currentAccessPolicy.getAction().toString());
+
+ final org.apache.nifi.registry.security.authorization.AccessPolicy updatedAccessPolicy =
+ configurableAccessPolicyProvider().updateAccessPolicy(accessPolicyFromDTO(accessPolicy));
+
+ if (updatedAccessPolicy == null) {
+ LOGGER.warn("The specified access policy id [{}] does not exist.", accessPolicy.getIdentifier());
+ throw new ResourceNotFoundException("The specified policy does not exist in this registry.");
+ }
+
+ return accessPolicyToDTO(updatedAccessPolicy);
}
- public AccessPolicy deleteAccessPolicy(String identifier) {
+ public AccessPolicy deleteAccessPolicy(final String identifier) {
verifyAccessPolicyProviderIsConfigurable();
- writeLock.lock();
- try {
- AccessPolicy deletedAccessPolicyDTO = getAccessPolicy(identifier);
- if (deletedAccessPolicyDTO != null) {
- ((ConfigurableAccessPolicyProvider) accessPolicyProvider).deleteAccessPolicy(identifier);
- }
- return deletedAccessPolicyDTO;
- } finally {
- writeLock.unlock();
+
+ final org.apache.nifi.registry.security.authorization.AccessPolicy accessPolicy =
+ accessPolicyProvider.getAccessPolicy(identifier);
+
+ if (accessPolicy == null) {
+ LOGGER.warn("The specified access policy id [{}] does not exist.", identifier);
+ throw new ResourceNotFoundException("The specified policy does not exist in this registry.");
}
+
+ configurableAccessPolicyProvider().deleteAccessPolicy(identifier);
+ return accessPolicyToDTO(accessPolicy);
}
@@ -466,6 +499,14 @@
// ---------------------- Private Helper methods --------------------------------------
+ private ConfigurableUserGroupProvider configurableUserGroupProvider() {
+ return ((ConfigurableUserGroupProvider) userGroupProvider);
+ }
+
+ private ConfigurableAccessPolicyProvider configurableAccessPolicyProvider() {
+ return ((ConfigurableAccessPolicyProvider) accessPolicyProvider);
+ }
+
private void verifyUserGroupProviderIsConfigurable() {
if (!(userGroupProvider instanceof ConfigurableUserGroupProvider)) {
throw new IllegalStateException(MSG_NON_CONFIGURABLE_USERS);
@@ -608,17 +649,12 @@
}
private Tenant tenantIdToDTO(String identifier) {
- this.readLock.lock();
- try {
- org.apache.nifi.registry.security.authorization.User user = userGroupProvider.getUser(identifier);
- if (user != null) {
- return tenantToDTO(user);
- } else {
- org.apache.nifi.registry.security.authorization.Group group = userGroupProvider.getGroup(identifier);
- return tenantToDTO(group);
- }
- } finally {
- this.readLock.unlock();
+ final org.apache.nifi.registry.security.authorization.User user = userGroupProvider.getUser(identifier);
+ if (user != null) {
+ return tenantToDTO(user);
+ } else {
+ org.apache.nifi.registry.security.authorization.Group group = userGroupProvider.getGroup(identifier);
+ return tenantToDTO(group);
}
}
@@ -672,7 +708,7 @@
return null;
}
return new org.apache.nifi.registry.security.authorization.User.Builder()
- .identifier(userDTO.getIdentifier() != null ? userDTO.getIdentifier() : UUID.randomUUID().toString())
+ .identifier(userDTO.getIdentifier())
.identity(userDTO.getIdentity())
.build();
}
@@ -683,7 +719,7 @@
return null;
}
org.apache.nifi.registry.security.authorization.Group.Builder groupBuilder = new org.apache.nifi.registry.security.authorization.Group.Builder()
- .identifier(userGroupDTO.getIdentifier() != null ? userGroupDTO.getIdentifier() : UUID.randomUUID().toString())
+ .identifier(userGroupDTO.getIdentifier())
.name(userGroupDTO.getIdentity());
Set<Tenant> users = userGroupDTO.getUsers();
if (users != null) {
@@ -696,7 +732,7 @@
final AccessPolicy accessPolicyDTO) {
org.apache.nifi.registry.security.authorization.AccessPolicy.Builder accessPolicyBuilder =
new org.apache.nifi.registry.security.authorization.AccessPolicy.Builder()
- .identifier(accessPolicyDTO.getIdentifier() != null ? accessPolicyDTO.getIdentifier() : UUID.randomUUID().toString())
+ .identifier(accessPolicyDTO.getIdentifier())
.resource(accessPolicyDTO.getResource())
.action(RequestAction.valueOfValue(accessPolicyDTO.getAction()));
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/RegistryService.java b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/RegistryService.java
index 8df44e8..b50196d 100644
--- a/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/RegistryService.java
+++ b/nifi-registry-core/nifi-registry-framework/src/main/java/org/apache/nifi/registry/service/RegistryService.java
@@ -31,21 +31,6 @@
import org.apache.nifi.registry.exception.ResourceNotFoundException;
import org.apache.nifi.registry.extension.BundleCoordinate;
import org.apache.nifi.registry.extension.BundlePersistenceProvider;
-import org.apache.nifi.registry.extension.bundle.Bundle;
-import org.apache.nifi.registry.extension.bundle.BundleFilterParams;
-import org.apache.nifi.registry.extension.bundle.BundleType;
-import org.apache.nifi.registry.extension.bundle.BundleVersion;
-import org.apache.nifi.registry.extension.bundle.BundleVersionFilterParams;
-import org.apache.nifi.registry.extension.bundle.BundleVersionMetadata;
-import org.apache.nifi.registry.extension.component.ExtensionFilterParams;
-import org.apache.nifi.registry.extension.component.ExtensionMetadata;
-import org.apache.nifi.registry.extension.component.TagCount;
-import org.apache.nifi.registry.extension.component.manifest.Extension;
-import org.apache.nifi.registry.extension.component.manifest.ProvidedServiceAPI;
-import org.apache.nifi.registry.extension.repo.ExtensionRepoArtifact;
-import org.apache.nifi.registry.extension.repo.ExtensionRepoBucket;
-import org.apache.nifi.registry.extension.repo.ExtensionRepoGroup;
-import org.apache.nifi.registry.extension.repo.ExtensionRepoVersionSummary;
import org.apache.nifi.registry.flow.FlowPersistenceProvider;
import org.apache.nifi.registry.flow.FlowSnapshotContext;
import org.apache.nifi.registry.flow.VersionedComponent;
@@ -65,7 +50,6 @@
import org.apache.nifi.registry.serialization.FlowContent;
import org.apache.nifi.registry.serialization.FlowContentSerializer;
import org.apache.nifi.registry.service.alias.RegistryUrlAliasService;
-import org.apache.nifi.registry.service.extension.ExtensionService;
import org.apache.nifi.registry.service.mapper.BucketMappings;
import org.apache.nifi.registry.service.mapper.ExtensionMappings;
import org.apache.nifi.registry.service.mapper.FlowMappings;
@@ -73,16 +57,13 @@
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
-import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -91,20 +72,12 @@
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
-import java.util.UUID;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;
/**
- * Main service for all back-end operations, REST resources should only interact with this service.
- *
- * This service is marked as @Transactional so that Spring will automatically start a transaction upon entering
- * any method, and will rollback the transaction if any Exception is thrown out of a method.
- *
+ * Main service for all back-end operations on buckets and flows.
*/
@Service
-@Transactional(rollbackFor = Exception.class)
public class RegistryService {
private static final Logger LOGGER = LoggerFactory.getLogger(RegistryService.class);
@@ -113,27 +86,20 @@
private final FlowPersistenceProvider flowPersistenceProvider;
private final BundlePersistenceProvider bundlePersistenceProvider;
private final FlowContentSerializer flowContentSerializer;
- private final ExtensionService extensionService;
private final Validator validator;
private final RegistryUrlAliasService registryUrlAliasService;
- private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
- private final Lock readLock = lock.readLock();
- private final Lock writeLock = lock.writeLock();
-
@Autowired
public RegistryService(final MetadataService metadataService,
final FlowPersistenceProvider flowPersistenceProvider,
final BundlePersistenceProvider bundlePersistenceProvider,
final FlowContentSerializer flowContentSerializer,
- final ExtensionService extensionService,
final Validator validator,
final RegistryUrlAliasService registryUrlAliasService) {
this.metadataService = Validate.notNull(metadataService);
this.flowPersistenceProvider = Validate.notNull(flowPersistenceProvider);
this.bundlePersistenceProvider = Validate.notNull(bundlePersistenceProvider);
this.flowContentSerializer = Validate.notNull(flowContentSerializer);
- this.extensionService = Validate.notNull(extensionService);
this.validator = Validate.notNull(validator);
this.registryUrlAliasService = Validate.notNull(registryUrlAliasService);
}
@@ -152,8 +118,7 @@
throw new IllegalArgumentException("Bucket cannot be null");
}
- // set an id, the created time, and clear out the flows since its read-only
- bucket.setIdentifier(UUID.randomUUID().toString());
+ // set the created time, and clear out the flows since its read-only
bucket.setCreatedTimestamp(System.currentTimeMillis());
if (bucket.isAllowBundleRedeploy() == null) {
@@ -166,18 +131,13 @@
validate(bucket, "Cannot create Bucket");
- writeLock.lock();
- try {
- final List<BucketEntity> bucketsWithSameName = metadataService.getBucketsByName(bucket.getName());
- if (bucketsWithSameName.size() > 0) {
- throw new IllegalStateException("A bucket with the same name already exists");
- }
-
- final BucketEntity createdBucket = metadataService.createBucket(BucketMappings.map(bucket));
- return BucketMappings.map(createdBucket);
- } finally {
- writeLock.unlock();
+ final List<BucketEntity> bucketsWithSameName = metadataService.getBucketsByName(bucket.getName());
+ if (bucketsWithSameName.size() > 0) {
+ throw new IllegalStateException("A bucket with the same name already exists");
}
+
+ final BucketEntity createdBucket = metadataService.createBucket(BucketMappings.map(bucket));
+ return BucketMappings.map(createdBucket);
}
public Bucket getBucket(final String bucketIdentifier) {
@@ -185,17 +145,24 @@
throw new IllegalArgumentException("Bucket identifier cannot be null");
}
- readLock.lock();
- try {
- final BucketEntity bucket = metadataService.getBucketById(bucketIdentifier);
- if (bucket == null) {
- LOGGER.warn("The specified bucket id [{}] does not exist.", bucketIdentifier);
- throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
- }
+ final BucketEntity bucket = metadataService.getBucketById(bucketIdentifier);
+ if (bucket == null) {
+ LOGGER.warn("The specified bucket id [{}] does not exist.", bucketIdentifier);
+ throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
+ }
- return BucketMappings.map(bucket);
- } finally {
- readLock.unlock();
+ return BucketMappings.map(bucket);
+ }
+
+ public void verifyBucketExists(final String bucketIdentifier) {
+ if (bucketIdentifier == null) {
+ throw new IllegalArgumentException("Bucket identifier cannot be null");
+ }
+
+ final BucketEntity bucket = metadataService.getBucketById(bucketIdentifier);
+ if (bucket == null) {
+ LOGGER.warn("The specified bucket id [{}] does not exist.", bucketIdentifier);
+ throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
}
}
@@ -204,38 +171,23 @@
throw new IllegalArgumentException("Bucket name cannot be null");
}
- readLock.lock();
- try {
- final List<BucketEntity> buckets = metadataService.getBucketsByName(bucketName);
- if (buckets.isEmpty()) {
- LOGGER.warn("The specified bucket name [{}] does not exist.", bucketName);
- throw new ResourceNotFoundException("The specified bucket name does not exist in this registry.");
- }
-
- return BucketMappings.map(buckets.get(0));
- } finally {
- readLock.unlock();
+ final List<BucketEntity> buckets = metadataService.getBucketsByName(bucketName);
+ if (buckets.isEmpty()) {
+ LOGGER.warn("The specified bucket name [{}] does not exist.", bucketName);
+ throw new ResourceNotFoundException("The specified bucket name does not exist in this registry.");
}
+
+ return BucketMappings.map(buckets.get(0));
}
public List<Bucket> getBuckets() {
- readLock.lock();
- try {
- final List<BucketEntity> buckets = metadataService.getAllBuckets();
- return buckets.stream().map(b -> BucketMappings.map(b)).collect(Collectors.toList());
- } finally {
- readLock.unlock();
- }
+ final List<BucketEntity> buckets = metadataService.getAllBuckets();
+ return buckets.stream().map(b -> BucketMappings.map(b)).collect(Collectors.toList());
}
public List<Bucket> getBuckets(final Set<String> bucketIds) {
- readLock.lock();
- try {
- final List<BucketEntity> buckets = metadataService.getBuckets(bucketIds);
- return buckets.stream().map(b -> BucketMappings.map(b)).collect(Collectors.toList());
- } finally {
- readLock.unlock();
- }
+ final List<BucketEntity> buckets = metadataService.getBuckets(bucketIds);
+ return buckets.stream().map(b -> BucketMappings.map(b)).collect(Collectors.toList());
}
public Bucket updateBucket(final Bucket bucket) {
@@ -251,51 +203,46 @@
throw new IllegalArgumentException("Bucket name cannot be blank");
}
- writeLock.lock();
- try {
- // ensure a bucket with the given id exists
- final BucketEntity existingBucketById = metadataService.getBucketById(bucket.getIdentifier());
- if (existingBucketById == null) {
- LOGGER.warn("The specified bucket id [{}] does not exist.", bucket.getIdentifier());
- throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
- }
+ // ensure a bucket with the given id exists
+ final BucketEntity existingBucketById = metadataService.getBucketById(bucket.getIdentifier());
+ if (existingBucketById == null) {
+ LOGGER.warn("The specified bucket id [{}] does not exist.", bucket.getIdentifier());
+ throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
+ }
- // ensure a different bucket with the same name does not exist
- // since we're allowing partial updates here, only check this if a non-null name is provided
- if (StringUtils.isNotBlank(bucket.getName())) {
- final List<BucketEntity> bucketsWithSameName = metadataService.getBucketsByName(bucket.getName());
- if (bucketsWithSameName != null) {
- for (final BucketEntity bucketWithSameName : bucketsWithSameName) {
- if (!bucketWithSameName.getId().equals(existingBucketById.getId())){
- throw new IllegalStateException("A bucket with the same name already exists - " + bucket.getName());
- }
+ // ensure a different bucket with the same name does not exist
+ // since we're allowing partial updates here, only check this if a non-null name is provided
+ if (StringUtils.isNotBlank(bucket.getName())) {
+ final List<BucketEntity> bucketsWithSameName = metadataService.getBucketsByName(bucket.getName());
+ if (bucketsWithSameName != null) {
+ for (final BucketEntity bucketWithSameName : bucketsWithSameName) {
+ if (!bucketWithSameName.getId().equals(existingBucketById.getId())){
+ throw new IllegalStateException("A bucket with the same name already exists - " + bucket.getName());
}
}
}
-
- // transfer over the new values to the existing bucket
- if (StringUtils.isNotBlank(bucket.getName())) {
- existingBucketById.setName(bucket.getName());
- }
-
- if (bucket.getDescription() != null) {
- existingBucketById.setDescription(bucket.getDescription());
- }
-
- if (bucket.isAllowBundleRedeploy() != null) {
- existingBucketById.setAllowExtensionBundleRedeploy(bucket.isAllowBundleRedeploy());
- }
-
- if (bucket.isAllowPublicRead() != null) {
- existingBucketById.setAllowPublicRead(bucket.isAllowPublicRead());
- }
-
- // perform the actual update
- final BucketEntity updatedBucket = metadataService.updateBucket(existingBucketById);
- return BucketMappings.map(updatedBucket);
- } finally {
- writeLock.unlock();
}
+
+ // transfer over the new values to the existing bucket
+ if (StringUtils.isNotBlank(bucket.getName())) {
+ existingBucketById.setName(bucket.getName());
+ }
+
+ if (bucket.getDescription() != null) {
+ existingBucketById.setDescription(bucket.getDescription());
+ }
+
+ if (bucket.isAllowBundleRedeploy() != null) {
+ existingBucketById.setAllowExtensionBundleRedeploy(bucket.isAllowBundleRedeploy());
+ }
+
+ if (bucket.isAllowPublicRead() != null) {
+ existingBucketById.setAllowPublicRead(bucket.isAllowPublicRead());
+ }
+
+ // perform the actual update
+ final BucketEntity updatedBucket = metadataService.updateBucket(existingBucketById);
+ return BucketMappings.map(updatedBucket);
}
public Bucket deleteBucket(final String bucketIdentifier) {
@@ -303,37 +250,32 @@
throw new IllegalArgumentException("Bucket identifier cannot be null");
}
- writeLock.lock();
- try {
- // ensure the bucket exists
- final BucketEntity existingBucket = metadataService.getBucketById(bucketIdentifier);
- if (existingBucket == null) {
- LOGGER.warn("The specified bucket id [{}] does not exist.", bucketIdentifier);
- throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
- }
-
- // for each flow in the bucket, delete all snapshots from the flow persistence provider
- for (final FlowEntity flowEntity : metadataService.getFlowsByBucket(existingBucket.getId())) {
- flowPersistenceProvider.deleteAllFlowContent(bucketIdentifier, flowEntity.getId());
- }
-
- // for each bundle in the bucket, delete all versions from the bundle persistence provider
- for (final BundleEntity bundleEntity : metadataService.getBundlesByBucket(existingBucket.getId())) {
- final BundleCoordinate bundleCoordinate = new StandardBundleCoordinate.Builder()
- .bucketId(bundleEntity.getBucketId())
- .groupId(bundleEntity.getGroupId())
- .artifactId(bundleEntity.getArtifactId())
- .build();
- bundlePersistenceProvider.deleteAllBundleVersions(bundleCoordinate);
- }
-
- // now delete the bucket from the metadata provider, which deletes all flows referencing it
- metadataService.deleteBucket(existingBucket);
-
- return BucketMappings.map(existingBucket);
- } finally {
- writeLock.unlock();
+ // ensure the bucket exists
+ final BucketEntity existingBucket = metadataService.getBucketById(bucketIdentifier);
+ if (existingBucket == null) {
+ LOGGER.warn("The specified bucket id [{}] does not exist.", bucketIdentifier);
+ throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
}
+
+ // for each flow in the bucket, delete all snapshots from the flow persistence provider
+ for (final FlowEntity flowEntity : metadataService.getFlowsByBucket(existingBucket.getId())) {
+ flowPersistenceProvider.deleteAllFlowContent(bucketIdentifier, flowEntity.getId());
+ }
+
+ // for each bundle in the bucket, delete all versions from the bundle persistence provider
+ for (final BundleEntity bundleEntity : metadataService.getBundlesByBucket(existingBucket.getId())) {
+ final BundleCoordinate bundleCoordinate = new StandardBundleCoordinate.Builder()
+ .bucketId(bundleEntity.getBucketId())
+ .groupId(bundleEntity.getGroupId())
+ .artifactId(bundleEntity.getArtifactId())
+ .build();
+ bundlePersistenceProvider.deleteAllBundleVersions(bundleCoordinate);
+ }
+
+ // now delete the bucket from the metadata provider, which deletes all flows referencing it
+ metadataService.deleteBucket(existingBucket);
+
+ return BucketMappings.map(existingBucket);
}
// ---------------------- BucketItem methods ---------------------------------------------
@@ -343,20 +285,15 @@
throw new IllegalArgumentException("Bucket identifier cannot be null");
}
- readLock.lock();
- try {
- final BucketEntity bucket = metadataService.getBucketById(bucketIdentifier);
- if (bucket == null) {
- LOGGER.warn("The specified bucket id [{}] does not exist.", bucketIdentifier);
- throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
- }
-
- final List<BucketItem> bucketItems = new ArrayList<>();
- metadataService.getBucketItems(bucket.getId()).stream().forEach(b -> addBucketItem(bucketItems, b));
- return bucketItems;
- } finally {
- readLock.unlock();
+ final BucketEntity bucket = metadataService.getBucketById(bucketIdentifier);
+ if (bucket == null) {
+ LOGGER.warn("The specified bucket id [{}] does not exist.", bucketIdentifier);
+ throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
}
+
+ final List<BucketItem> bucketItems = new ArrayList<>();
+ metadataService.getBucketItems(bucket.getId()).stream().forEach(b -> addBucketItem(bucketItems, b));
+ return bucketItems;
}
public List<BucketItem> getBucketItems(final Set<String> bucketIdentifiers) {
@@ -364,14 +301,9 @@
throw new IllegalArgumentException("Bucket identifiers cannot be null or empty");
}
- readLock.lock();
- try {
- final List<BucketItem> bucketItems = new ArrayList<>();
- metadataService.getBucketItems(bucketIdentifiers).stream().forEach(b -> addBucketItem(bucketItems, b));
- return bucketItems;
- } finally {
- readLock.unlock();
- }
+ final List<BucketItem> bucketItems = new ArrayList<>();
+ metadataService.getBucketItems(bucketIdentifiers).stream().forEach(b -> addBucketItem(bucketItems, b));
+ return bucketItems;
}
private void addBucketItem(final List<BucketItem> bucketItems, final BucketItemEntity itemEntity) {
@@ -406,39 +338,32 @@
versionedFlow.setBucketIdentifier(bucketIdentifier);
}
- versionedFlow.setIdentifier(UUID.randomUUID().toString());
-
final long timestamp = System.currentTimeMillis();
versionedFlow.setCreatedTimestamp(timestamp);
versionedFlow.setModifiedTimestamp(timestamp);
validate(versionedFlow, "Cannot create versioned flow");
- writeLock.lock();
- try {
- // ensure the bucket exists
- final BucketEntity existingBucket = metadataService.getBucketById(bucketIdentifier);
- if (existingBucket == null) {
- LOGGER.warn("The specified bucket id [{}] does not exist.", bucketIdentifier);
- throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
- }
-
- // ensure another flow with the same name doesn't exist
- final List<FlowEntity> flowsWithSameName = metadataService.getFlowsByName(existingBucket.getId(), versionedFlow.getName());
- if (flowsWithSameName != null && flowsWithSameName.size() > 0) {
- throw new IllegalStateException("A versioned flow with the same name already exists in the selected bucket");
- }
-
- // convert from dto to entity and set the bucket relationship
- final FlowEntity flowEntity = FlowMappings.map(versionedFlow);
- flowEntity.setBucketId(existingBucket.getId());
-
- // persist the flow and return the created entity
- final FlowEntity createdFlow = metadataService.createFlow(flowEntity);
- return FlowMappings.map(existingBucket, createdFlow);
- } finally {
- writeLock.unlock();
+ // ensure the bucket exists
+ final BucketEntity existingBucket = metadataService.getBucketById(bucketIdentifier);
+ if (existingBucket == null) {
+ LOGGER.warn("The specified bucket id [{}] does not exist.", bucketIdentifier);
+ throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
}
+
+ // ensure another flow with the same name doesn't exist
+ final List<FlowEntity> flowsWithSameName = metadataService.getFlowsByName(existingBucket.getId(), versionedFlow.getName());
+ if (flowsWithSameName != null && flowsWithSameName.size() > 0) {
+ throw new IllegalStateException("A versioned flow with the same name already exists in the selected bucket");
+ }
+
+ // convert from dto to entity and set the bucket relationship
+ final FlowEntity flowEntity = FlowMappings.map(versionedFlow);
+ flowEntity.setBucketId(existingBucket.getId());
+
+ // persist the flow and return the created entity
+ final FlowEntity createdFlow = metadataService.createFlow(flowEntity);
+ return FlowMappings.map(existingBucket, createdFlow);
}
public VersionedFlow getFlow(final String bucketIdentifier, final String flowIdentifier) {
@@ -450,29 +375,24 @@
throw new IllegalArgumentException("Versioned flow identifier cannot be null or blank");
}
- readLock.lock();
- try {
- // ensure the bucket exists
- final BucketEntity existingBucket = metadataService.getBucketById(bucketIdentifier);
- if (existingBucket == null) {
- LOGGER.warn("The specified bucket id [{}] does not exist.", bucketIdentifier);
- throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
- }
-
- final FlowEntity existingFlow = metadataService.getFlowByIdWithSnapshotCounts(flowIdentifier);
- if (existingFlow == null) {
- LOGGER.warn("The specified flow id [{}] does not exist.", flowIdentifier);
- throw new ResourceNotFoundException("The specified flow ID does not exist in this bucket.");
- }
-
- if (!existingBucket.getId().equals(existingFlow.getBucketId())) {
- throw new IllegalStateException("The requested flow is not located in the given bucket");
- }
-
- return FlowMappings.map(existingBucket, existingFlow);
- } finally {
- readLock.unlock();
+ // ensure the bucket exists
+ final BucketEntity existingBucket = metadataService.getBucketById(bucketIdentifier);
+ if (existingBucket == null) {
+ LOGGER.warn("The specified bucket id [{}] does not exist.", bucketIdentifier);
+ throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
}
+
+ final FlowEntity existingFlow = metadataService.getFlowByIdWithSnapshotCounts(flowIdentifier);
+ if (existingFlow == null) {
+ LOGGER.warn("The specified flow id [{}] does not exist.", flowIdentifier);
+ throw new ResourceNotFoundException("The specified flow ID does not exist in this bucket.");
+ }
+
+ if (!existingBucket.getId().equals(existingFlow.getBucketId())) {
+ throw new IllegalStateException("The requested flow is not located in the given bucket");
+ }
+
+ return FlowMappings.map(existingBucket, existingFlow);
}
public VersionedFlow getFlow(final String flowIdentifier) {
@@ -480,18 +400,25 @@
throw new IllegalArgumentException("Versioned flow identifier cannot be null or blank");
}
- readLock.lock();
- try {
- final FlowEntity existingFlow = metadataService.getFlowByIdWithSnapshotCounts(flowIdentifier);
- if (existingFlow == null) {
- LOGGER.warn("The specified flow id [{}] does not exist.", flowIdentifier);
- throw new ResourceNotFoundException("The specified flow ID does not exist.");
- }
+ final FlowEntity existingFlow = metadataService.getFlowByIdWithSnapshotCounts(flowIdentifier);
+ if (existingFlow == null) {
+ LOGGER.warn("The specified flow id [{}] does not exist.", flowIdentifier);
+ throw new ResourceNotFoundException("The specified flow ID does not exist.");
+ }
- final BucketEntity existingBucket = metadataService.getBucketById(existingFlow.getBucketId());
- return FlowMappings.map(existingBucket, existingFlow);
- } finally {
- readLock.unlock();
+ final BucketEntity existingBucket = metadataService.getBucketById(existingFlow.getBucketId());
+ return FlowMappings.map(existingBucket, existingFlow);
+ }
+
+ public void verifyFlowExists(final String flowIdentifier) {
+ if (StringUtils.isBlank(flowIdentifier)) {
+ throw new IllegalArgumentException("Versioned flow identifier cannot be null or blank");
+ }
+
+ final FlowEntity existingFlow = metadataService.getFlowById(flowIdentifier);
+ if (existingFlow == null) {
+ LOGGER.warn("The specified flow id [{}] does not exist.", flowIdentifier);
+ throw new ResourceNotFoundException("The specified flow ID does not exist.");
}
}
@@ -500,20 +427,15 @@
throw new IllegalArgumentException("Bucket identifier cannot be null");
}
- readLock.lock();
- try {
- final BucketEntity existingBucket = metadataService.getBucketById(bucketId);
- if (existingBucket == null) {
- LOGGER.warn("The specified bucket id [{}] does not exist.", bucketId);
- throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
- }
-
- // return non-verbose set of flows for the given bucket
- final List<FlowEntity> flows = metadataService.getFlowsByBucket(existingBucket.getId());
- return flows.stream().map(f -> FlowMappings.map(existingBucket, f)).collect(Collectors.toList());
- } finally {
- readLock.unlock();
+ final BucketEntity existingBucket = metadataService.getBucketById(bucketId);
+ if (existingBucket == null) {
+ LOGGER.warn("The specified bucket id [{}] does not exist.", bucketId);
+ throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
}
+
+ // return non-verbose set of flows for the given bucket
+ final List<FlowEntity> flows = metadataService.getFlowsByBucket(existingBucket.getId());
+ return flows.stream().map(f -> FlowMappings.map(existingBucket, f)).collect(Collectors.toList());
}
public VersionedFlow updateFlow(final VersionedFlow versionedFlow) {
@@ -533,53 +455,48 @@
throw new IllegalArgumentException("Versioned flow name cannot be blank");
}
- writeLock.lock();
- try {
- // ensure the bucket exists
- final BucketEntity existingBucket = metadataService.getBucketById(versionedFlow.getBucketIdentifier());
- if (existingBucket == null) {
- LOGGER.warn("The specified bucket id [{}] does not exist.", versionedFlow.getBucketIdentifier());
- throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
- }
+ // ensure the bucket exists
+ final BucketEntity existingBucket = metadataService.getBucketById(versionedFlow.getBucketIdentifier());
+ if (existingBucket == null) {
+ LOGGER.warn("The specified bucket id [{}] does not exist.", versionedFlow.getBucketIdentifier());
+ throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
+ }
- final FlowEntity existingFlow = metadataService.getFlowByIdWithSnapshotCounts(versionedFlow.getIdentifier());
- if (existingFlow == null) {
- LOGGER.warn("The specified flow id [{}] does not exist.", versionedFlow.getIdentifier());
- throw new ResourceNotFoundException("The specified flow ID does not exist in this bucket.");
- }
+ final FlowEntity existingFlow = metadataService.getFlowByIdWithSnapshotCounts(versionedFlow.getIdentifier());
+ if (existingFlow == null) {
+ LOGGER.warn("The specified flow id [{}] does not exist.", versionedFlow.getIdentifier());
+ throw new ResourceNotFoundException("The specified flow ID does not exist in this bucket.");
+ }
- if (!existingBucket.getId().equals(existingFlow.getBucketId())) {
- throw new IllegalStateException("The requested flow is not located in the given bucket");
- }
+ if (!existingBucket.getId().equals(existingFlow.getBucketId())) {
+ throw new IllegalStateException("The requested flow is not located in the given bucket");
+ }
- // ensure a different flow with the same name does not exist
- // since we're allowing partial updates here, only check this if a non-null name is provided
- if (StringUtils.isNotBlank(versionedFlow.getName())) {
- final List<FlowEntity> flowsWithSameName = metadataService.getFlowsByName(existingBucket.getId(), versionedFlow.getName());
- if (flowsWithSameName != null) {
- for (final FlowEntity flowWithSameName : flowsWithSameName) {
- if(!flowWithSameName.getId().equals(existingFlow.getId())) {
- throw new IllegalStateException("A versioned flow with the same name already exists in the selected bucket");
- }
+ // ensure a different flow with the same name does not exist
+ // since we're allowing partial updates here, only check this if a non-null name is provided
+ if (StringUtils.isNotBlank(versionedFlow.getName())) {
+ final List<FlowEntity> flowsWithSameName = metadataService.getFlowsByName(existingBucket.getId(), versionedFlow.getName());
+ if (flowsWithSameName != null) {
+ for (final FlowEntity flowWithSameName : flowsWithSameName) {
+ if(!flowWithSameName.getId().equals(existingFlow.getId())) {
+ throw new IllegalStateException("A versioned flow with the same name already exists in the selected bucket");
}
}
}
-
- // transfer over the new values to the existing flow
- if (StringUtils.isNotBlank(versionedFlow.getName())) {
- existingFlow.setName(versionedFlow.getName());
- }
-
- if (versionedFlow.getDescription() != null) {
- existingFlow.setDescription(versionedFlow.getDescription());
- }
-
- // perform the actual update
- final FlowEntity updatedFlow = metadataService.updateFlow(existingFlow);
- return FlowMappings.map(existingBucket, updatedFlow);
- } finally {
- writeLock.unlock();
}
+
+ // transfer over the new values to the existing flow
+ if (StringUtils.isNotBlank(versionedFlow.getName())) {
+ existingFlow.setName(versionedFlow.getName());
+ }
+
+ if (versionedFlow.getDescription() != null) {
+ existingFlow.setDescription(versionedFlow.getDescription());
+ }
+
+ // perform the actual update
+ final FlowEntity updatedFlow = metadataService.updateFlow(existingFlow);
+ return FlowMappings.map(existingBucket, updatedFlow);
}
public VersionedFlow deleteFlow(final String bucketIdentifier, final String flowIdentifier) {
@@ -590,36 +507,31 @@
throw new IllegalArgumentException("Flow identifier cannot be null or blank");
}
- writeLock.lock();
- try {
- // ensure the bucket exists
- final BucketEntity existingBucket = metadataService.getBucketById(bucketIdentifier);
- if (existingBucket == null) {
- LOGGER.warn("The specified bucket id [{}] does not exist.", bucketIdentifier);
- throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
- }
-
- // ensure the flow exists
- final FlowEntity existingFlow = metadataService.getFlowById(flowIdentifier);
- if (existingFlow == null) {
- LOGGER.warn("The specified flow id [{}] does not exist.", flowIdentifier);
- throw new ResourceNotFoundException("The specified flow ID does not exist in this bucket.");
- }
-
- if (!existingBucket.getId().equals(existingFlow.getBucketId())) {
- throw new IllegalStateException("The requested flow is not located in the given bucket");
- }
-
- // delete all snapshots from the flow persistence provider
- flowPersistenceProvider.deleteAllFlowContent(existingFlow.getBucketId(), existingFlow.getId());
-
- // now delete the flow from the metadata provider
- metadataService.deleteFlow(existingFlow);
-
- return FlowMappings.map(existingBucket, existingFlow);
- } finally {
- writeLock.unlock();
+ // ensure the bucket exists
+ final BucketEntity existingBucket = metadataService.getBucketById(bucketIdentifier);
+ if (existingBucket == null) {
+ LOGGER.warn("The specified bucket id [{}] does not exist.", bucketIdentifier);
+ throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
}
+
+ // ensure the flow exists
+ final FlowEntity existingFlow = metadataService.getFlowById(flowIdentifier);
+ if (existingFlow == null) {
+ LOGGER.warn("The specified flow id [{}] does not exist.", flowIdentifier);
+ throw new ResourceNotFoundException("The specified flow ID does not exist in this bucket.");
+ }
+
+ if (!existingBucket.getId().equals(existingFlow.getBucketId())) {
+ throw new IllegalStateException("The requested flow is not located in the given bucket");
+ }
+
+ // delete all snapshots from the flow persistence provider
+ flowPersistenceProvider.deleteAllFlowContent(existingFlow.getBucketId(), existingFlow.getId());
+
+ // now delete the flow from the metadata provider
+ metadataService.deleteFlow(existingFlow);
+
+ return FlowMappings.map(existingBucket, existingFlow);
}
// ---------------------- VersionedFlowSnapshot methods ---------------------------------------------
@@ -640,98 +552,93 @@
validate(flowSnapshot, "Cannot create versioned flow snapshot");
- writeLock.lock();
- try {
- final VersionedFlowSnapshotMetadata snapshotMetadata = flowSnapshot.getSnapshotMetadata();
+ final VersionedFlowSnapshotMetadata snapshotMetadata = flowSnapshot.getSnapshotMetadata();
- // ensure the bucket exists
- final BucketEntity existingBucket = metadataService.getBucketById(snapshotMetadata.getBucketIdentifier());
- if (existingBucket == null) {
- LOGGER.warn("The specified bucket id [{}] does not exist.", snapshotMetadata.getBucketIdentifier());
- throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
- }
-
- // ensure the flow exists
- final FlowEntity existingFlow = metadataService.getFlowById(snapshotMetadata.getFlowIdentifier());
- if (existingFlow == null) {
- LOGGER.warn("The specified flow id [{}] does not exist.", snapshotMetadata.getFlowIdentifier());
- throw new ResourceNotFoundException("The specified flow ID does not exist in this bucket.");
- }
-
- if (!existingBucket.getId().equals(existingFlow.getBucketId())) {
- throw new IllegalStateException("The requested flow is not located in the given bucket");
- }
-
- if (snapshotMetadata.getVersion() == 0) {
- throw new IllegalArgumentException("Version must be greater than zero, or use -1 to indicate latest version");
- }
-
- // convert the set of FlowSnapshotEntity to set of VersionedFlowSnapshotMetadata
- final SortedSet<VersionedFlowSnapshotMetadata> sortedSnapshots = new TreeSet<>();
- final List<FlowSnapshotEntity> existingFlowSnapshots = metadataService.getSnapshots(existingFlow.getId());
- if (existingFlowSnapshots != null) {
- existingFlowSnapshots.stream().forEach(s -> sortedSnapshots.add(FlowMappings.map(existingBucket, s)));
- }
-
- // if we already have snapshots we need to verify the new one has the correct version
- if (sortedSnapshots.size() > 0) {
- final VersionedFlowSnapshotMetadata lastSnapshot = sortedSnapshots.last();
-
- // if we have existing versions and a client sends -1, then make this the latest version
- if (snapshotMetadata.getVersion() == -1) {
- snapshotMetadata.setVersion(lastSnapshot.getVersion() + 1);
- } else if (snapshotMetadata.getVersion() <= lastSnapshot.getVersion()) {
- throw new IllegalStateException("A Versioned flow snapshot with the same version already exists: " + snapshotMetadata.getVersion());
- } else if (snapshotMetadata.getVersion() > (lastSnapshot.getVersion() + 1)) {
- throw new IllegalStateException("Version must be a one-up number, last version was " + lastSnapshot.getVersion()
- + " and version for this snapshot was " + snapshotMetadata.getVersion());
- }
-
- } else if (snapshotMetadata.getVersion() == -1) {
- // if we have no existing versions and a client sends -1, then this is the first version
- snapshotMetadata.setVersion(1);
- } else if (snapshotMetadata.getVersion() != 1) {
- throw new IllegalStateException("Version of first snapshot must be 1");
- }
-
- // serialize the snapshot
- final ByteArrayOutputStream out = new ByteArrayOutputStream();
- registryUrlAliasService.setInternal(flowSnapshot.getFlowContents());
-
- final FlowContent flowContent = new FlowContent();
- flowContent.setFlowSnapshot(flowSnapshot);
-
- // temporarily remove the metadata so it isn't serialized, but then put it back for returning the response
- flowSnapshot.setSnapshotMetadata(null);
- flowContentSerializer.serializeFlowContent(flowContent, out);
- flowSnapshot.setSnapshotMetadata(snapshotMetadata);
-
- // save the serialized snapshot to the persistence provider
- final Bucket bucket = BucketMappings.map(existingBucket);
- final VersionedFlow versionedFlow = FlowMappings.map(existingBucket, existingFlow);
- final FlowSnapshotContext context = new StandardFlowSnapshotContext.Builder(bucket, versionedFlow, snapshotMetadata).build();
- flowPersistenceProvider.saveFlowContent(context, out.toByteArray());
-
- // create snapshot in the metadata provider
- metadataService.createFlowSnapshot(FlowMappings.map(snapshotMetadata));
-
- // update the modified date on the flow
- metadataService.updateFlow(existingFlow);
-
- // get the updated flow, we need to use "with counts" here so we can return this is a part of the response
- final FlowEntity updatedFlow = metadataService.getFlowByIdWithSnapshotCounts(snapshotMetadata.getFlowIdentifier());
- if (updatedFlow == null) {
- throw new ResourceNotFoundException("Versioned flow does not exist for identifier " + snapshotMetadata.getFlowIdentifier());
- }
- final VersionedFlow updatedVersionedFlow = FlowMappings.map(existingBucket, updatedFlow);
-
- flowSnapshot.setBucket(bucket);
- flowSnapshot.setFlow(updatedVersionedFlow);
- registryUrlAliasService.setExternal(flowSnapshot.getFlowContents());
- return flowSnapshot;
- } finally {
- writeLock.unlock();
+ // ensure the bucket exists
+ final BucketEntity existingBucket = metadataService.getBucketById(snapshotMetadata.getBucketIdentifier());
+ if (existingBucket == null) {
+ LOGGER.warn("The specified bucket id [{}] does not exist.", snapshotMetadata.getBucketIdentifier());
+ throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
}
+
+ // ensure the flow exists
+ final FlowEntity existingFlow = metadataService.getFlowById(snapshotMetadata.getFlowIdentifier());
+ if (existingFlow == null) {
+ LOGGER.warn("The specified flow id [{}] does not exist.", snapshotMetadata.getFlowIdentifier());
+ throw new ResourceNotFoundException("The specified flow ID does not exist in this bucket.");
+ }
+
+ if (!existingBucket.getId().equals(existingFlow.getBucketId())) {
+ throw new IllegalStateException("The requested flow is not located in the given bucket");
+ }
+
+ if (snapshotMetadata.getVersion() == 0) {
+ throw new IllegalArgumentException("Version must be greater than zero, or use -1 to indicate latest version");
+ }
+
+ // convert the set of FlowSnapshotEntity to set of VersionedFlowSnapshotMetadata
+ final SortedSet<VersionedFlowSnapshotMetadata> sortedSnapshots = new TreeSet<>();
+ final List<FlowSnapshotEntity> existingFlowSnapshots = metadataService.getSnapshots(existingFlow.getId());
+ if (existingFlowSnapshots != null) {
+ existingFlowSnapshots.stream().forEach(s -> sortedSnapshots.add(FlowMappings.map(existingBucket, s)));
+ }
+
+ // if we already have snapshots we need to verify the new one has the correct version
+ if (sortedSnapshots.size() > 0) {
+ final VersionedFlowSnapshotMetadata lastSnapshot = sortedSnapshots.last();
+
+ // if we have existing versions and a client sends -1, then make this the latest version
+ if (snapshotMetadata.getVersion() == -1) {
+ snapshotMetadata.setVersion(lastSnapshot.getVersion() + 1);
+ } else if (snapshotMetadata.getVersion() <= lastSnapshot.getVersion()) {
+ throw new IllegalStateException("A Versioned flow snapshot with the same version already exists: " + snapshotMetadata.getVersion());
+ } else if (snapshotMetadata.getVersion() > (lastSnapshot.getVersion() + 1)) {
+ throw new IllegalStateException("Version must be a one-up number, last version was " + lastSnapshot.getVersion()
+ + " and version for this snapshot was " + snapshotMetadata.getVersion());
+ }
+
+ } else if (snapshotMetadata.getVersion() == -1) {
+ // if we have no existing versions and a client sends -1, then this is the first version
+ snapshotMetadata.setVersion(1);
+ } else if (snapshotMetadata.getVersion() != 1) {
+ throw new IllegalStateException("Version of first snapshot must be 1");
+ }
+
+ // serialize the snapshot
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ registryUrlAliasService.setInternal(flowSnapshot.getFlowContents());
+
+ final FlowContent flowContent = new FlowContent();
+ flowContent.setFlowSnapshot(flowSnapshot);
+
+ // temporarily remove the metadata so it isn't serialized, but then put it back for returning the response
+ flowSnapshot.setSnapshotMetadata(null);
+ flowContentSerializer.serializeFlowContent(flowContent, out);
+ flowSnapshot.setSnapshotMetadata(snapshotMetadata);
+
+ // save the serialized snapshot to the persistence provider
+ final Bucket bucket = BucketMappings.map(existingBucket);
+ final VersionedFlow versionedFlow = FlowMappings.map(existingBucket, existingFlow);
+ final FlowSnapshotContext context = new StandardFlowSnapshotContext.Builder(bucket, versionedFlow, snapshotMetadata).build();
+ flowPersistenceProvider.saveFlowContent(context, out.toByteArray());
+
+ // create snapshot in the metadata provider
+ metadataService.createFlowSnapshot(FlowMappings.map(snapshotMetadata));
+
+ // update the modified date on the flow
+ metadataService.updateFlow(existingFlow);
+
+ // get the updated flow, we need to use "with counts" here so we can return this is a part of the response
+ final FlowEntity updatedFlow = metadataService.getFlowByIdWithSnapshotCounts(snapshotMetadata.getFlowIdentifier());
+ if (updatedFlow == null) {
+ throw new ResourceNotFoundException("Versioned flow does not exist for identifier " + snapshotMetadata.getFlowIdentifier());
+ }
+ final VersionedFlow updatedVersionedFlow = FlowMappings.map(existingBucket, updatedFlow);
+
+ flowSnapshot.setBucket(bucket);
+ flowSnapshot.setFlow(updatedVersionedFlow);
+ registryUrlAliasService.setExternal(flowSnapshot.getFlowContents());
+ return flowSnapshot;
}
public VersionedFlowSnapshot getFlowSnapshot(final String bucketIdentifier, final String flowIdentifier, final Integer version) {
@@ -747,29 +654,24 @@
throw new IllegalArgumentException("Version cannot be null or blank");
}
- readLock.lock();
- try {
- final BucketEntity existingBucket = metadataService.getBucketById(bucketIdentifier);
- if (existingBucket == null) {
- LOGGER.warn("The specified bucket id [{}] does not exist.", bucketIdentifier);
- throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
- }
-
- // we need to populate the version count here so we have to do this retrieval instead of snapshotEntity.getFlow()
- final FlowEntity flowEntityWithCount = metadataService.getFlowByIdWithSnapshotCounts(flowIdentifier);
- if (flowEntityWithCount == null) {
- LOGGER.warn("The specified flow id [{}] does not exist.", flowIdentifier);
- throw new ResourceNotFoundException("The specified flow ID does not exist in this bucket.");
- }
-
- if (!existingBucket.getId().equals(flowEntityWithCount.getBucketId())) {
- throw new IllegalStateException("The requested flow is not located in the given bucket");
- }
-
- return getVersionedFlowSnapshot(existingBucket, flowEntityWithCount, version);
- } finally {
- readLock.unlock();
+ final BucketEntity existingBucket = metadataService.getBucketById(bucketIdentifier);
+ if (existingBucket == null) {
+ LOGGER.warn("The specified bucket id [{}] does not exist.", bucketIdentifier);
+ throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
}
+
+ // we need to populate the version count here so we have to do this retrieval instead of snapshotEntity.getFlow()
+ final FlowEntity flowEntityWithCount = metadataService.getFlowByIdWithSnapshotCounts(flowIdentifier);
+ if (flowEntityWithCount == null) {
+ LOGGER.warn("The specified flow id [{}] does not exist.", flowIdentifier);
+ throw new ResourceNotFoundException("The specified flow ID does not exist in this bucket.");
+ }
+
+ if (!existingBucket.getId().equals(flowEntityWithCount.getBucketId())) {
+ throw new IllegalStateException("The requested flow is not located in the given bucket");
+ }
+
+ return getVersionedFlowSnapshot(existingBucket, flowEntityWithCount, version);
}
private VersionedFlowSnapshot getVersionedFlowSnapshot(final BucketEntity bucketEntity, final FlowEntity flowEntity, final Integer version) {
@@ -837,38 +739,32 @@
throw new IllegalArgumentException("Flow identifier cannot be null or blank");
}
- readLock.lock();
- try {
- // ensure the bucket exists
- final BucketEntity existingBucket = metadataService.getBucketById(bucketIdentifier);
- if (existingBucket == null) {
- LOGGER.warn("The specified bucket id [{}] does not exist.", bucketIdentifier);
- throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
- }
-
- // ensure the flow exists
- final FlowEntity existingFlow = metadataService.getFlowById(flowIdentifier);
- if (existingFlow == null) {
- LOGGER.warn("The specified flow id [{}] does not exist.", flowIdentifier);
- throw new ResourceNotFoundException("The specified flow ID does not exist in this bucket.");
- }
-
- if (!existingBucket.getId().equals(existingFlow.getBucketId())) {
- throw new IllegalStateException("The requested flow is not located in the given bucket");
- }
-
- // convert the set of FlowSnapshotEntity to set of VersionedFlowSnapshotMetadata, ordered by version descending
- final SortedSet<VersionedFlowSnapshotMetadata> sortedSnapshots = new TreeSet<>(Collections.reverseOrder());
- final List<FlowSnapshotEntity> existingFlowSnapshots = metadataService.getSnapshots(existingFlow.getId());
- if (existingFlowSnapshots != null) {
- existingFlowSnapshots.stream().forEach(s -> sortedSnapshots.add(FlowMappings.map(existingBucket, s)));
- }
-
- return sortedSnapshots;
-
- } finally {
- readLock.unlock();
+ // ensure the bucket exists
+ final BucketEntity existingBucket = metadataService.getBucketById(bucketIdentifier);
+ if (existingBucket == null) {
+ LOGGER.warn("The specified bucket id [{}] does not exist.", bucketIdentifier);
+ throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
}
+
+ // ensure the flow exists
+ final FlowEntity existingFlow = metadataService.getFlowById(flowIdentifier);
+ if (existingFlow == null) {
+ LOGGER.warn("The specified flow id [{}] does not exist.", flowIdentifier);
+ throw new ResourceNotFoundException("The specified flow ID does not exist in this bucket.");
+ }
+
+ if (!existingBucket.getId().equals(existingFlow.getBucketId())) {
+ throw new IllegalStateException("The requested flow is not located in the given bucket");
+ }
+
+ // convert the set of FlowSnapshotEntity to set of VersionedFlowSnapshotMetadata, ordered by version descending
+ final SortedSet<VersionedFlowSnapshotMetadata> sortedSnapshots = new TreeSet<>(Collections.reverseOrder());
+ final List<FlowSnapshotEntity> existingFlowSnapshots = metadataService.getSnapshots(existingFlow.getId());
+ if (existingFlowSnapshots != null) {
+ existingFlowSnapshots.stream().forEach(s -> sortedSnapshots.add(FlowMappings.map(existingBucket, s)));
+ }
+
+ return sortedSnapshots;
}
public VersionedFlowSnapshotMetadata getLatestFlowSnapshotMetadata(final String bucketIdentifier, final String flowIdentifier) {
@@ -880,36 +776,31 @@
throw new IllegalArgumentException("Flow identifier cannot be null or blank");
}
- readLock.lock();
- try {
- // ensure the bucket exists
- final BucketEntity existingBucket = metadataService.getBucketById(bucketIdentifier);
- if (existingBucket == null) {
- LOGGER.warn("The specified bucket id [{}] does not exist.", bucketIdentifier);
- throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
- }
-
- // ensure the flow exists
- final FlowEntity existingFlow = metadataService.getFlowById(flowIdentifier);
- if (existingFlow == null) {
- LOGGER.warn("The specified flow id [{}] does not exist.", flowIdentifier);
- throw new ResourceNotFoundException("The specified flow ID does not exist in this bucket.");
- }
-
- if (!existingBucket.getId().equals(existingFlow.getBucketId())) {
- throw new IllegalStateException("The requested flow is not located in the given bucket");
- }
-
- // get latest snapshot for the flow
- final FlowSnapshotEntity latestSnapshot = metadataService.getLatestSnapshot(existingFlow.getId());
- if (latestSnapshot == null) {
- throw new ResourceNotFoundException("The specified flow ID has no versions");
- }
-
- return FlowMappings.map(existingBucket, latestSnapshot);
- } finally {
- readLock.unlock();
+ // ensure the bucket exists
+ final BucketEntity existingBucket = metadataService.getBucketById(bucketIdentifier);
+ if (existingBucket == null) {
+ LOGGER.warn("The specified bucket id [{}] does not exist.", bucketIdentifier);
+ throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
}
+
+ // ensure the flow exists
+ final FlowEntity existingFlow = metadataService.getFlowById(flowIdentifier);
+ if (existingFlow == null) {
+ LOGGER.warn("The specified flow id [{}] does not exist.", flowIdentifier);
+ throw new ResourceNotFoundException("The specified flow ID does not exist in this bucket.");
+ }
+
+ if (!existingBucket.getId().equals(existingFlow.getBucketId())) {
+ throw new IllegalStateException("The requested flow is not located in the given bucket");
+ }
+
+ // get latest snapshot for the flow
+ final FlowSnapshotEntity latestSnapshot = metadataService.getLatestSnapshot(existingFlow.getId());
+ if (latestSnapshot == null) {
+ throw new ResourceNotFoundException("The specified flow ID has no versions");
+ }
+
+ return FlowMappings.map(existingBucket, latestSnapshot);
}
public VersionedFlowSnapshotMetadata getLatestFlowSnapshotMetadata(final String flowIdentifier) {
@@ -917,32 +808,27 @@
throw new IllegalArgumentException("Flow identifier cannot be null or blank");
}
- readLock.lock();
- try {
- // ensure the flow exists
- final FlowEntity existingFlow = metadataService.getFlowById(flowIdentifier);
- if (existingFlow == null) {
- LOGGER.warn("The specified flow id [{}] does not exist.", flowIdentifier);
- throw new ResourceNotFoundException("The specified flow ID does not exist in this bucket.");
- }
-
- // ensure the bucket exists
- final BucketEntity existingBucket = metadataService.getBucketById(existingFlow.getBucketId());
- if (existingBucket == null) {
- LOGGER.warn("The specified bucket id [{}] does not exist.", existingFlow.getBucketId());
- throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
- }
-
- // get latest snapshot for the flow
- final FlowSnapshotEntity latestSnapshot = metadataService.getLatestSnapshot(existingFlow.getId());
- if (latestSnapshot == null) {
- throw new ResourceNotFoundException("The specified flow ID has no versions");
- }
-
- return FlowMappings.map(existingBucket, latestSnapshot);
- } finally {
- readLock.unlock();
+ // ensure the flow exists
+ final FlowEntity existingFlow = metadataService.getFlowById(flowIdentifier);
+ if (existingFlow == null) {
+ LOGGER.warn("The specified flow id [{}] does not exist.", flowIdentifier);
+ throw new ResourceNotFoundException("The specified flow ID does not exist in this bucket.");
}
+
+ // ensure the bucket exists
+ final BucketEntity existingBucket = metadataService.getBucketById(existingFlow.getBucketId());
+ if (existingBucket == null) {
+ LOGGER.warn("The specified bucket id [{}] does not exist.", existingFlow.getBucketId());
+ throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
+ }
+
+ // get latest snapshot for the flow
+ final FlowSnapshotEntity latestSnapshot = metadataService.getLatestSnapshot(existingFlow.getId());
+ if (latestSnapshot == null) {
+ throw new ResourceNotFoundException("The specified flow ID has no versions");
+ }
+
+ return FlowMappings.map(existingBucket, latestSnapshot);
}
public VersionedFlowSnapshotMetadata deleteFlowSnapshot(final String bucketIdentifier, final String flowIdentifier, final Integer version) {
@@ -958,42 +844,37 @@
throw new IllegalArgumentException("Version cannot be null or blank");
}
- writeLock.lock();
- try {
- // ensure the bucket exists
- final BucketEntity existingBucket = metadataService.getBucketById(bucketIdentifier);
- if (existingBucket == null) {
- LOGGER.warn("The specified bucket id [{}] does not exist.", bucketIdentifier);
- throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
- }
-
- // ensure the flow exists
- final FlowEntity existingFlow = metadataService.getFlowById(flowIdentifier);
- if (existingFlow == null) {
- LOGGER.warn("The specified flow id [{}] does not exist.", flowIdentifier);
- throw new ResourceNotFoundException("The specified flow ID does not exist in this bucket.");
- }
-
- if (!existingBucket.getId().equals(existingFlow.getBucketId())) {
- throw new IllegalStateException("The requested flow is not located in the given bucket");
- }
-
- // ensure the snapshot exists
- final FlowSnapshotEntity snapshotEntity = metadataService.getFlowSnapshot(flowIdentifier, version);
- if (snapshotEntity == null) {
- throw new ResourceNotFoundException("Versioned flow snapshot does not exist for flow "
- + flowIdentifier + " and version " + version);
- }
-
- // delete the content of the snapshot
- flowPersistenceProvider.deleteFlowContent(bucketIdentifier, flowIdentifier, version);
-
- // delete the snapshot itself
- metadataService.deleteFlowSnapshot(snapshotEntity);
- return FlowMappings.map(existingBucket, snapshotEntity);
- } finally {
- writeLock.unlock();
+ // ensure the bucket exists
+ final BucketEntity existingBucket = metadataService.getBucketById(bucketIdentifier);
+ if (existingBucket == null) {
+ LOGGER.warn("The specified bucket id [{}] does not exist.", bucketIdentifier);
+ throw new ResourceNotFoundException("The specified bucket ID does not exist in this registry.");
}
+
+ // ensure the flow exists
+ final FlowEntity existingFlow = metadataService.getFlowById(flowIdentifier);
+ if (existingFlow == null) {
+ LOGGER.warn("The specified flow id [{}] does not exist.", flowIdentifier);
+ throw new ResourceNotFoundException("The specified flow ID does not exist in this bucket.");
+ }
+
+ if (!existingBucket.getId().equals(existingFlow.getBucketId())) {
+ throw new IllegalStateException("The requested flow is not located in the given bucket");
+ }
+
+ // ensure the snapshot exists
+ final FlowSnapshotEntity snapshotEntity = metadataService.getFlowSnapshot(flowIdentifier, version);
+ if (snapshotEntity == null) {
+ throw new ResourceNotFoundException("Versioned flow snapshot does not exist for flow "
+ + flowIdentifier + " and version " + version);
+ }
+
+ // delete the content of the snapshot
+ flowPersistenceProvider.deleteFlowContent(bucketIdentifier, flowIdentifier, version);
+
+ // delete the snapshot itself
+ metadataService.deleteFlowSnapshot(snapshotEntity);
+ return FlowMappings.map(existingBucket, snapshotEntity);
}
/**
@@ -1022,51 +903,46 @@
final Integer older = Math.min(versionA, versionB);
final Integer newer = Math.max(versionA, versionB);
- readLock.lock();
- try {
- // Get the content for both versions of the flow
- final byte[] serializedSnapshotA = flowPersistenceProvider.getFlowContent(bucketIdentifier, flowIdentifier, older);
- if (serializedSnapshotA == null || serializedSnapshotA.length == 0) {
- throw new IllegalStateException("No serialized content found for snapshot with flow identifier "
- + flowIdentifier + " and version " + older);
- }
-
- final byte[] serializedSnapshotB = flowPersistenceProvider.getFlowContent(bucketIdentifier, flowIdentifier, newer);
- if (serializedSnapshotB == null || serializedSnapshotB.length == 0) {
- throw new IllegalStateException("No serialized content found for snapshot with flow identifier "
- + flowIdentifier + " and version " + newer);
- }
-
- // deserialize the contents
- final InputStream inputA = new ByteArrayInputStream(serializedSnapshotA);
- final VersionedFlowSnapshot snapshotA = deserializeFlowContent(inputA);
- final VersionedProcessGroup flowContentsA = snapshotA.getFlowContents();
-
- final InputStream inputB = new ByteArrayInputStream(serializedSnapshotB);
- final VersionedFlowSnapshot snapshotB = deserializeFlowContent(inputB);
- final VersionedProcessGroup flowContentsB = snapshotB.getFlowContents();
-
- final ComparableDataFlow comparableFlowA = new StandardComparableDataFlow(String.format("Version %d", older), flowContentsA);
- final ComparableDataFlow comparableFlowB = new StandardComparableDataFlow(String.format("Version %d", newer), flowContentsB);
-
- // Compare the two versions of the flow
- final FlowComparator flowComparator = new StandardFlowComparator(comparableFlowA, comparableFlowB,
- null, new ConciseEvolvingDifferenceDescriptor());
- final FlowComparison flowComparison = flowComparator.compare();
-
- final VersionedFlowDifference result = new VersionedFlowDifference();
- result.setBucketId(bucketIdentifier);
- result.setFlowId(flowIdentifier);
- result.setVersionA(older);
- result.setVersionB(newer);
-
- final Set<ComponentDifferenceGroup> differenceGroups = getStringComponentDifferenceGroupMap(flowComparison.getDifferences());
- result.setComponentDifferenceGroups(differenceGroups);
-
- return result;
- } finally {
- readLock.unlock();
+ // Get the content for both versions of the flow
+ final byte[] serializedSnapshotA = flowPersistenceProvider.getFlowContent(bucketIdentifier, flowIdentifier, older);
+ if (serializedSnapshotA == null || serializedSnapshotA.length == 0) {
+ throw new IllegalStateException("No serialized content found for snapshot with flow identifier "
+ + flowIdentifier + " and version " + older);
}
+
+ final byte[] serializedSnapshotB = flowPersistenceProvider.getFlowContent(bucketIdentifier, flowIdentifier, newer);
+ if (serializedSnapshotB == null || serializedSnapshotB.length == 0) {
+ throw new IllegalStateException("No serialized content found for snapshot with flow identifier "
+ + flowIdentifier + " and version " + newer);
+ }
+
+ // deserialize the contents
+ final InputStream inputA = new ByteArrayInputStream(serializedSnapshotA);
+ final VersionedFlowSnapshot snapshotA = deserializeFlowContent(inputA);
+ final VersionedProcessGroup flowContentsA = snapshotA.getFlowContents();
+
+ final InputStream inputB = new ByteArrayInputStream(serializedSnapshotB);
+ final VersionedFlowSnapshot snapshotB = deserializeFlowContent(inputB);
+ final VersionedProcessGroup flowContentsB = snapshotB.getFlowContents();
+
+ final ComparableDataFlow comparableFlowA = new StandardComparableDataFlow(String.format("Version %d", older), flowContentsA);
+ final ComparableDataFlow comparableFlowB = new StandardComparableDataFlow(String.format("Version %d", newer), flowContentsB);
+
+ // Compare the two versions of the flow
+ final FlowComparator flowComparator = new StandardFlowComparator(comparableFlowA, comparableFlowB,
+ null, new ConciseEvolvingDifferenceDescriptor());
+ final FlowComparison flowComparison = flowComparator.compare();
+
+ final VersionedFlowDifference result = new VersionedFlowDifference();
+ result.setBucketId(bucketIdentifier);
+ result.setFlowId(flowIdentifier);
+ result.setVersionA(older);
+ result.setVersionB(newer);
+
+ final Set<ComponentDifferenceGroup> differenceGroups = getStringComponentDifferenceGroupMap(flowComparison.getDifferences());
+ result.setComponentDifferenceGroups(differenceGroups);
+
+ return result;
}
/**
@@ -1091,214 +967,6 @@
return differenceGroups.values().stream().collect(Collectors.toSet());
}
- // ---------------------- Bundle methods ---------------------------------------------
-
- public BundleVersion createBundleVersion(final String bucketIdentifier, final BundleType bundleType,
- final InputStream inputStream, final String clientSha256) throws IOException {
- writeLock.lock();
- try {
- return extensionService.createBundleVersion(bucketIdentifier, bundleType, inputStream, clientSha256);
- } finally {
- writeLock.unlock();
- }
- }
-
- public List<Bundle> getBundles(final Set<String> bucketIdentifiers, final BundleFilterParams filterParams) {
- readLock.lock();
- try {
- return extensionService.getBundles(bucketIdentifiers, filterParams);
- } finally {
- readLock.unlock();
- }
- }
-
- public List<Bundle> getBundlesByBucket(final String bucketIdentifier) {
- readLock.lock();
- try {
- return extensionService.getBundlesByBucket(bucketIdentifier);
- } finally {
- readLock.unlock();
- }
- }
-
- public Bundle getBundle(final String extensionBundleId) {
- readLock.lock();
- try {
- return extensionService.getBundle(extensionBundleId);
- } finally {
- readLock.unlock();
- }
- }
-
- public Bundle deleteBundle(final Bundle bundle) {
- writeLock.lock();
- try {
- return extensionService.deleteBundle(bundle);
- } finally {
- writeLock.unlock();
- }
- }
-
- public SortedSet<BundleVersionMetadata> getBundleVersions(final Set<String> bucketIdentifiers, final BundleVersionFilterParams filterParams) {
- readLock.lock();
- try {
- return extensionService.getBundleVersions(bucketIdentifiers, filterParams);
- } finally {
- readLock.unlock();
- }
- }
-
-
- public SortedSet<BundleVersionMetadata> getBundleVersions(final String extensionBundleIdentifier) {
- readLock.lock();
- try {
- return extensionService.getBundleVersions(extensionBundleIdentifier);
- } finally {
- readLock.unlock();
- }
- }
-
- public BundleVersion getBundleVersion(final String bucketId, final String bundleId, final String version) {
- readLock.lock();
- try {
- return extensionService.getBundleVersion(bucketId, bundleId, version);
- } finally {
- readLock.unlock();
- }
- }
-
- public BundleVersion getBundleVersion(final String bucketId, final String groupId, final String artifactId, final String version) {
- readLock.lock();
- try {
- return extensionService.getBundleVersion(bucketId, groupId, artifactId, version);
- } finally {
- readLock.unlock();
- }
- }
-
- public void writeBundleVersionContent(final BundleVersion bundleVersion, final OutputStream out) {
- readLock.lock();
- try {
- extensionService.writeBundleVersionContent(bundleVersion, out);
- } finally {
- readLock.unlock();
- }
- }
-
- public BundleVersion deleteBundleVersion(final BundleVersion bundleVersion) {
- writeLock.lock();
- try {
- return extensionService.deleteBundleVersion(bundleVersion);
- } finally {
- writeLock.unlock();
- }
- }
-
- // ---------------------- Extension methods ---------------------------------------------
-
- public SortedSet<ExtensionMetadata> getExtensionMetadata(final Set<String> bucketIdentifiers, final ExtensionFilterParams filterParams) {
- readLock.lock();
- try {
- return extensionService.getExtensionMetadata(bucketIdentifiers, filterParams);
- } finally {
- readLock.unlock();
- }
- }
-
- public SortedSet<ExtensionMetadata> getExtensionMetadata(final Set<String> bucketIdentifiers, final ProvidedServiceAPI serviceAPI) {
- readLock.lock();
- try {
- return extensionService.getExtensionMetadata(bucketIdentifiers, serviceAPI);
- } finally {
- readLock.unlock();
- }
- }
-
- public SortedSet<ExtensionMetadata> getExtensionMetadata(final BundleVersion bundleVersion) {
- readLock.lock();
- try {
- return extensionService.getExtensionMetadata(bundleVersion);
- } finally {
- readLock.unlock();
- }
- }
-
- public Extension getExtension(final BundleVersion bundleVersion, final String name) {
- readLock.lock();
- try {
- return extensionService.getExtension(bundleVersion, name);
- } finally {
- readLock.unlock();
- }
- }
-
- public void writeExtensionDocs(final BundleVersion bundleVersion, final String name, final OutputStream outputStream)
- throws IOException {
- readLock.lock();
- try {
- extensionService.writeExtensionDocs(bundleVersion, name, outputStream);
- } finally {
- readLock.unlock();
- }
- }
-
- public void writeAdditionalDetailsDocs(final BundleVersion bundleVersion, final String name, final OutputStream outputStream)
- throws IOException {
- readLock.lock();
- try {
- extensionService.writeAdditionalDetailsDocs(bundleVersion, name, outputStream);
- } finally {
- readLock.unlock();
- }
- }
-
- public SortedSet<TagCount> getExtensionTags() {
- readLock.lock();
- try {
- return extensionService.getExtensionTags();
- } finally {
- readLock.unlock();
- }
- }
-
- // ---------------------- Extension Repository methods ---------------------------------------------
-
- public SortedSet<ExtensionRepoBucket> getExtensionRepoBuckets(final Set<String> bucketIds) {
- readLock.lock();
- try {
- return extensionService.getExtensionRepoBuckets(bucketIds);
- } finally {
- readLock.unlock();
- }
- }
-
- public SortedSet<ExtensionRepoGroup> getExtensionRepoGroups(final Bucket bucket) {
- readLock.lock();
- try {
- return extensionService.getExtensionRepoGroups(bucket);
- } finally {
- readLock.unlock();
- }
- }
-
- public SortedSet<ExtensionRepoArtifact> getExtensionRepoArtifacts(final Bucket bucket, final String groupId) {
- readLock.lock();
- try {
- return extensionService.getExtensionRepoArtifacts(bucket, groupId);
- } finally {
- readLock.unlock();
- }
- }
-
- public SortedSet<ExtensionRepoVersionSummary> getExtensionRepoVersions(final Bucket bucket, final String groupId, final String artifactId) {
- readLock.lock();
- try {
- return extensionService.getExtensionRepoVersions(bucket, groupId, artifactId);
- } finally {
- readLock.unlock();
- }
- }
-
// ---------------------- Field methods ---------------------------------------------
public Set<String> getBucketFields() {
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/resources/db/migration/default/V7__AddRevision.sql b/nifi-registry-core/nifi-registry-framework/src/main/resources/db/migration/default/V7__AddRevision.sql
new file mode 100644
index 0000000..eb37fcd
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/resources/db/migration/default/V7__AddRevision.sql
@@ -0,0 +1,21 @@
+-- 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.
+
+CREATE TABLE REVISION (
+ ENTITY_ID VARCHAR(200) NOT NULL,
+ VERSION BIGINT NOT NULL DEFAULT (0),
+ CLIENT_ID VARCHAR(100),
+ CONSTRAINT PK__REVISION_ENTITY_ID PRIMARY KEY (ENTITY_ID)
+);
\ No newline at end of file
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/resources/db/migration/mysql/V7__AddRevision.sql b/nifi-registry-core/nifi-registry-framework/src/main/resources/db/migration/mysql/V7__AddRevision.sql
new file mode 100644
index 0000000..e29a515
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/resources/db/migration/mysql/V7__AddRevision.sql
@@ -0,0 +1,21 @@
+-- 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.
+
+CREATE TABLE REVISION (
+ ENTITY_ID VARCHAR(200) NOT NULL,
+ VERSION BIGINT NOT NULL DEFAULT 0,
+ CLIENT_ID VARCHAR(100),
+ CONSTRAINT PK__REVISION_ENTITY_ID PRIMARY KEY (ENTITY_ID)
+);
\ No newline at end of file
diff --git a/nifi-registry-core/nifi-registry-framework/src/main/resources/db/migration/postgres/V7__AddRevision.sql b/nifi-registry-core/nifi-registry-framework/src/main/resources/db/migration/postgres/V7__AddRevision.sql
new file mode 100644
index 0000000..eb37fcd
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-framework/src/main/resources/db/migration/postgres/V7__AddRevision.sql
@@ -0,0 +1,21 @@
+-- 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.
+
+CREATE TABLE REVISION (
+ ENTITY_ID VARCHAR(200) NOT NULL,
+ VERSION BIGINT NOT NULL DEFAULT (0),
+ CLIENT_ID VARCHAR(100),
+ CONSTRAINT PK__REVISION_ENTITY_ID PRIMARY KEY (ENTITY_ID)
+);
\ No newline at end of file
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy b/nifi-registry-core/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy
index f47e7ce..33b9f40 100644
--- a/nifi-registry-core/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy
+++ b/nifi-registry-core/nifi-registry-framework/src/test/groovy/org/apache/nifi/registry/service/AuthorizationServiceSpec.groovy
@@ -20,6 +20,7 @@
import org.apache.nifi.registry.authorization.User
import org.apache.nifi.registry.authorization.UserGroup
import org.apache.nifi.registry.bucket.Bucket
+import org.apache.nifi.registry.exception.ResourceNotFoundException
import org.apache.nifi.registry.security.authorization.*
import org.apache.nifi.registry.security.authorization.AccessPolicy as AuthAccessPolicy
import org.apache.nifi.registry.security.authorization.User as AuthUser
@@ -56,12 +57,12 @@
accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>() // needed for converting user to DTO
when: "new user is created successfully"
- def user = new User(null, "username")
+ def user = new User("id", "username")
User createdUser = authorizationService.createUser(user)
then: "created user has been assigned an identifier"
with(createdUser) {
- identifier != null
+ identifier == "id"
identity == "username"
}
@@ -134,7 +135,7 @@
def user2 = authorizationService.getUser("does-not-exist")
then: "no user is returned"
- user2 == null
+ thrown(ResourceNotFoundException.class)
}
@@ -162,8 +163,9 @@
def "delete user"() {
setup:
- userGroupProvider.getUser("userId") >> new AuthUser.Builder().identifier("userId").identity("username").build()
- userGroupProvider.deleteUser("userId") >> new AuthUser.Builder().identifier("userId").identity("username").build()
+ def user1 = new AuthUser.Builder().identifier("userId").identity("username").build()
+ userGroupProvider.getUser("userId") >> user1
+ userGroupProvider.deleteUser(user1) >> user1
userGroupProvider.getGroups() >> new HashSet<Group>()
accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>()
@@ -190,12 +192,12 @@
accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>() // needed for converting to DTO
when: "new group is created successfully"
- def group = new UserGroup(null, "groupName")
+ def group = new UserGroup("id", "groupName")
UserGroup createdGroup = authorizationService.createUserGroup(group)
then: "created group has been assigned an identifier"
with(createdGroup) {
- identifier != null
+ identifier == "id"
identity == "groupName"
}
@@ -255,7 +257,7 @@
def g2 = authorizationService.getUserGroup("nonExistentId")
then: "no group is returned"
- g2 == null
+ thrown(ResourceNotFoundException.class)
}
@@ -282,8 +284,9 @@
def "delete user group"() {
setup:
- userGroupProvider.getGroup("id") >> new Group.Builder().identifier("id").name("name").build()
- userGroupProvider.deleteGroup("id") >> new Group.Builder().identifier("id").name("name").build()
+ def group1 = new Group.Builder().identifier("id").name("name").build();
+ userGroupProvider.getGroup("id") >> group1
+ userGroupProvider.deleteGroup(group1) >> group1
accessPolicyProvider.getAccessPolicies() >> new HashSet<AccessPolicy>()
@@ -316,11 +319,14 @@
when: "new access policy is created successfully"
- def createdPolicy = authorizationService.createAccessPolicy(new AccessPolicy([resource: "/resource", action: "read"]))
+ def accessPolicy = new AccessPolicy([resource: "/resource", action: "read"])
+ accessPolicy.setIdentifier("id")
+
+ def createdPolicy = authorizationService.createAccessPolicy(accessPolicy)
then: "created policy has been assigned an identifier"
with(createdPolicy) {
- identifier != null
+ identifier == "id"
resource == "/resource"
action == "read"
configurable == true
@@ -378,7 +384,7 @@
def p2 = authorizationService.getAccessPolicy("nonExistentId")
then: "no policy is returned"
- p2 == null
+ thrown(ResourceNotFoundException.class)
}
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/provider/flow/TestDatabaseFlowPersistenceProvider.java b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/provider/flow/TestDatabaseFlowPersistenceProvider.java
index 5314860..5851b21 100644
--- a/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/provider/flow/TestDatabaseFlowPersistenceProvider.java
+++ b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/provider/flow/TestDatabaseFlowPersistenceProvider.java
@@ -16,20 +16,13 @@
*/
package org.apache.nifi.registry.provider.flow;
-import org.apache.nifi.registry.db.DatabaseTestApplication;
+import org.apache.nifi.registry.db.DatabaseBaseTest;
import org.apache.nifi.registry.flow.FlowPersistenceProvider;
import org.apache.nifi.registry.flow.FlowSnapshotContext;
import org.junit.Before;
import org.junit.Test;
-import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.context.TestExecutionListeners;
-import org.springframework.test.context.junit4.SpringRunner;
-import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
-import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
-import org.springframework.transaction.annotation.Transactional;
import javax.sql.DataSource;
import java.nio.charset.StandardCharsets;
@@ -39,11 +32,7 @@
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.when;
-@Transactional
-@RunWith(SpringRunner.class)
-@SpringBootTest(classes = DatabaseTestApplication.class, webEnvironment = SpringBootTest.WebEnvironment.NONE)
-@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
-public class TestDatabaseFlowPersistenceProvider {
+public class TestDatabaseFlowPersistenceProvider extends DatabaseBaseTest {
@Autowired
private DataSource dataSource;
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/service/TestRegistryService.java b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/service/TestRegistryService.java
index 4e43645..dd09170 100644
--- a/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/service/TestRegistryService.java
+++ b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/service/TestRegistryService.java
@@ -34,8 +34,6 @@
import org.apache.nifi.registry.serialization.FlowContent;
import org.apache.nifi.registry.serialization.FlowContentSerializer;
import org.apache.nifi.registry.service.alias.RegistryUrlAliasService;
-import org.apache.nifi.registry.service.extension.ExtensionService;
-import org.apache.nifi.registry.service.extension.StandardExtensionService;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -78,7 +76,6 @@
private FlowPersistenceProvider flowPersistenceProvider;
private BundlePersistenceProvider bundlePersistenceProvider;
private FlowContentSerializer flowContentSerializer;
- private ExtensionService extensionService;
private Validator validator;
private RegistryUrlAliasService registryUrlAliasService;
@@ -90,14 +87,13 @@
flowPersistenceProvider = mock(FlowPersistenceProvider.class);
bundlePersistenceProvider = mock(BundlePersistenceProvider.class);
flowContentSerializer = mock(FlowContentSerializer.class);
- extensionService = mock(StandardExtensionService.class);
registryUrlAliasService = mock(RegistryUrlAliasService.class);
final ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
validator = validatorFactory.getValidator();
registryService = new RegistryService(metadataService, flowPersistenceProvider, bundlePersistenceProvider,
- flowContentSerializer, extensionService, validator, registryUrlAliasService);
+ flowContentSerializer, validator, registryUrlAliasService);
}
// ---------------------- Test Bucket methods ---------------------------------------------
@@ -105,6 +101,7 @@
@Test
public void testCreateBucketValid() {
final Bucket bucket = new Bucket();
+ bucket.setIdentifier("1");
bucket.setName("My Bucket");
bucket.setDescription("This is my bucket.");
@@ -117,6 +114,7 @@
assertNotNull(createdBucket.getIdentifier());
assertNotNull(createdBucket.getCreatedTimestamp());
+ assertEquals(bucket.getIdentifier(), createdBucket.getIdentifier());
assertEquals(bucket.getName(), createdBucket.getName());
assertEquals(bucket.getDescription(), createdBucket.getDescription());
}
@@ -124,6 +122,7 @@
@Test(expected = IllegalStateException.class)
public void testCreateBucketWithSameName() {
final Bucket bucket = new Bucket();
+ bucket.setIdentifier("b2");
bucket.setName("My Bucket");
bucket.setDescription("This is my bucket.");
@@ -313,6 +312,7 @@
when(metadataService.getBucketById(any(String.class))).thenReturn(null);
final VersionedFlow versionedFlow = new VersionedFlow();
+ versionedFlow.setIdentifier("f1");
versionedFlow.setName("My Flow");
versionedFlow.setBucketIdentifier("b1");
@@ -341,6 +341,7 @@
when(metadataService.getFlowsByName(existingBucket.getId(), flowWithSameName.getName())).thenReturn(Collections.singletonList(flowWithSameName));
final VersionedFlow versionedFlow = new VersionedFlow();
+ versionedFlow.setIdentifier("flow2");
versionedFlow.setName(flowWithSameName.getName());
versionedFlow.setBucketIdentifier("b1");
@@ -358,6 +359,7 @@
when(metadataService.getBucketById(existingBucket.getId())).thenReturn(existingBucket);
final VersionedFlow versionedFlow = new VersionedFlow();
+ versionedFlow.setIdentifier("f1");
versionedFlow.setName("My Flow");
versionedFlow.setBucketIdentifier("b1");
@@ -368,6 +370,7 @@
assertNotNull(createdFlow.getIdentifier());
assertTrue(createdFlow.getCreatedTimestamp() > 0);
assertTrue(createdFlow.getModifiedTimestamp() > 0);
+ assertEquals(versionedFlow.getIdentifier(), createdFlow.getIdentifier());
assertEquals(versionedFlow.getName(), createdFlow.getName());
assertEquals(versionedFlow.getBucketIdentifier(), createdFlow.getBucketIdentifier());
assertEquals(versionedFlow.getDescription(), createdFlow.getDescription());
diff --git a/nifi-registry-core/nifi-registry-framework/src/test/resources/application.properties b/nifi-registry-core/nifi-registry-framework/src/test/resources/application.properties
index 1e0d7c9..cbeee4f 100644
--- a/nifi-registry-core/nifi-registry-framework/src/test/resources/application.properties
+++ b/nifi-registry-core/nifi-registry-framework/src/test/resources/application.properties
@@ -23,3 +23,6 @@
#logging.level.org.springframework.core.io.support: DEBUG
#logging.level.org.springframework.context.annotation: DEBUG
#logging.level.org.springframework.web: DEBUG
+
+# Controls logging of SQL queries and parameters
+# logging.level.org.springframework.jdbc: TRACE
\ No newline at end of file
diff --git a/nifi-registry-core/nifi-registry-revision/nifi-registry-revision-entity-service/src/main/java/org/apache/nifi/registry/revision/entity/RevisableEntityService.java b/nifi-registry-core/nifi-registry-revision/nifi-registry-revision-entity-service/src/main/java/org/apache/nifi/registry/revision/entity/RevisableEntityService.java
index c5d66f5..fec764c 100644
--- a/nifi-registry-core/nifi-registry-revision/nifi-registry-revision-entity-service/src/main/java/org/apache/nifi/registry/revision/entity/RevisableEntityService.java
+++ b/nifi-registry-core/nifi-registry-revision/nifi-registry-revision-entity-service/src/main/java/org/apache/nifi/registry/revision/entity/RevisableEntityService.java
@@ -16,6 +16,7 @@
*/
package org.apache.nifi.registry.revision.entity;
+import java.util.Collection;
import java.util.List;
import java.util.function.Supplier;
@@ -75,4 +76,11 @@
*/
<T extends RevisableEntity> T delete(String entityIdentifier, RevisionInfo revisionInfo, Supplier<T> deleteEntity);
+ /**
+ * Populates RevisionInfo on any objects in the collection that implement RevisableEntity.
+ *
+ * @param entities the entities collection which may contain one or more RevisableEntity instances
+ */
+ void populateRevisions(Collection<?> entities);
+
}
diff --git a/nifi-registry-core/nifi-registry-revision/nifi-registry-revision-entity-service/src/main/java/org/apache/nifi/registry/revision/entity/StandardRevisableEntityService.java b/nifi-registry-core/nifi-registry-revision/nifi-registry-revision-entity-service/src/main/java/org/apache/nifi/registry/revision/entity/StandardRevisableEntityService.java
index cd55ea3..541f31f 100644
--- a/nifi-registry-core/nifi-registry-revision/nifi-registry-revision-entity-service/src/main/java/org/apache/nifi/registry/revision/entity/StandardRevisableEntityService.java
+++ b/nifi-registry-core/nifi-registry-revision/nifi-registry-revision-entity-service/src/main/java/org/apache/nifi/registry/revision/entity/StandardRevisableEntityService.java
@@ -26,6 +26,7 @@
import java.util.Collection;
import java.util.List;
+import java.util.Map;
import java.util.function.Supplier;
/**
@@ -72,7 +73,7 @@
@Override
public <T extends RevisableEntity> List<T> getEntities(final Supplier<List<T>> getEntities) {
final List<T> entities = getEntities.get();
- populateRevisions(entities);
+ populateRevisableEntityRevisions(entities);
return entities;
}
@@ -108,6 +109,24 @@
return revisionManager.deleteRevision(claim, () -> deleteEntity.get());
}
+ @Override
+ public void populateRevisions(final Collection<?> entities) {
+ if (entities == null || entities.isEmpty()) {
+ return;
+ }
+
+ // Note: This might be inefficient to retrieve all the revisions when there are lots of revisions
+ // and only a few entities that we might need revisions for, we could consider allowing a set of
+ // entity ids to be passed in, but then we also might end up with a massive OR statement when selecting
+ final Map<String,Revision> revisionMap = revisionManager.getRevisionMap();
+
+ for (final Object obj : entities) {
+ if (obj instanceof RevisableEntity) {
+ populateRevision(revisionMap, (RevisableEntity) obj);
+ }
+ }
+ }
+
private <T extends RevisableEntity> T createOrUpdate(final T requestEntity, final String userIdentity, final Supplier<T> updateOrCreateEntity) {
final Revision revision = createRevision(requestEntity.getIdentifier(), requestEntity.getRevision());
final RevisionClaim claim = new StandardRevisionClaim(revision);
@@ -127,21 +146,35 @@
return revisionUpdate.getEntity();
}
- private <T extends RevisableEntity> void populateRevisions(final Collection<T> revisableEntities) {
+ private <T extends RevisableEntity> void populateRevisableEntityRevisions(final Collection<T> revisableEntities) {
if (revisableEntities == null) {
return;
}
+ final Map<String,Revision> revisionMap = revisionManager.getRevisionMap();
revisableEntities.forEach(e -> {
- populateRevision(e);
+ populateRevision(revisionMap, e);
});
}
+ private void populateRevision(final Map<String, Revision> revisionMap, final RevisableEntity revisableEntity) {
+ final Revision revision = revisionMap.get(revisableEntity.getIdentifier());
+ if (revision != null) {
+ final RevisionInfo revisionInfo = createRevisionInfo(revision);
+ revisableEntity.setRevision(revisionInfo);
+ } else {
+ // need to make sure that if there isn't an entry in the map, we call getRevision which will cause a
+ // revision to be created in the RevisionManager
+ populateRevision(revisableEntity);
+ }
+ }
+
private void populateRevision(final RevisableEntity e) {
if (e == null) {
return;
}
+ // get or create the revision
final Revision entityRevision = revisionManager.getRevision(e.getIdentifier());
final RevisionInfo revisionInfo = createRevisionInfo(entityRevision);
e.setRevision(revisionInfo);
diff --git a/nifi-registry-core/nifi-registry-revision/pom.xml b/nifi-registry-core/nifi-registry-revision/pom.xml
index 9c80f29..c3ce4d8 100644
--- a/nifi-registry-core/nifi-registry-revision/pom.xml
+++ b/nifi-registry-core/nifi-registry-revision/pom.xml
@@ -27,8 +27,8 @@
<module>nifi-registry-revision-api</module>
<module>nifi-registry-revision-common</module>
<module>nifi-registry-revision-spring-jdbc</module>
- <module>nifi-registry-revision-entity-model</module>
- <module>nifi-registry-revision-entity-service</module>
+ <module>nifi-registry-revision-entity-model</module>
+ <module>nifi-registry-revision-entity-service</module>
</modules>
</project>
diff --git a/nifi-registry-core/nifi-registry-web-api/pom.xml b/nifi-registry-core/nifi-registry-web-api/pom.xml
index 4dada50..a65a75a 100644
--- a/nifi-registry-core/nifi-registry-web-api/pom.xml
+++ b/nifi-registry-core/nifi-registry-web-api/pom.xml
@@ -370,6 +370,16 @@
</dependency>
<dependency>
<groupId>org.apache.nifi.registry</groupId>
+ <artifactId>nifi-registry-revision-spring-jdbc</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.nifi.registry</groupId>
+ <artifactId>nifi-registry-revision-entity-service</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.nifi.registry</groupId>
<artifactId>nifi-registry-properties</artifactId>
<version>1.0.0-SNAPSHOT</version>
<scope>provided</scope> <!-- This will be in the lib directory -->
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java
index bfbeabf..3250807 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessPolicyResource.java
@@ -24,30 +24,30 @@
import io.swagger.annotations.Authorization;
import io.swagger.annotations.Extension;
import io.swagger.annotations.ExtensionProperty;
+import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.authorization.AccessPolicy;
import org.apache.nifi.registry.authorization.AccessPolicySummary;
import org.apache.nifi.registry.authorization.Resource;
import org.apache.nifi.registry.event.EventService;
-import org.apache.nifi.registry.exception.ResourceNotFoundException;
-import org.apache.nifi.registry.security.authorization.Authorizer;
-import org.apache.nifi.registry.security.authorization.AuthorizerCapabilityDetection;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
+import org.apache.nifi.registry.revision.web.ClientIdParameter;
+import org.apache.nifi.registry.revision.web.LongParameter;
import org.apache.nifi.registry.security.authorization.RequestAction;
-import org.apache.nifi.registry.security.authorization.resource.Authorizable;
-import org.apache.nifi.registry.service.AuthorizationService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.apache.nifi.registry.web.service.ServiceFacade;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@@ -65,19 +65,11 @@
description = "Endpoint for managing access policies.",
authorizations = { @Authorization("Authorization") }
)
-public class AccessPolicyResource extends AuthorizableApplicationResource {
-
- private static final Logger logger = LoggerFactory.getLogger(AccessPolicyResource.class);
-
- private Authorizer authorizer;
+public class AccessPolicyResource extends ApplicationResource {
@Autowired
- public AccessPolicyResource(
- Authorizer authorizer,
- AuthorizationService authorizationService,
- EventService eventService) {
- super(authorizationService, eventService);
- this.authorizer = authorizer;
+ public AccessPolicyResource(final ServiceFacade serviceFacade, final EventService eventService) {
+ super(serviceFacade, eventService);
}
/**
@@ -109,22 +101,7 @@
@ApiParam(value = "The access policy configuration details.", required = true)
final AccessPolicy requestAccessPolicy) {
- verifyAuthorizerSupportsConfigurablePolicies();
- authorizeAccess(RequestAction.WRITE);
-
- if (requestAccessPolicy == null) {
- throw new IllegalArgumentException("Access policy details must be specified when creating a new policy.");
- }
- if (requestAccessPolicy.getIdentifier() != null) {
- throw new IllegalArgumentException("Access policy ID cannot be specified when creating a new policy.");
- }
- if (requestAccessPolicy.getResource() == null) {
- throw new IllegalArgumentException("Resource must be specified when creating a new access policy.");
- }
- RequestAction.valueOfValue(requestAccessPolicy.getAction());
-
- AccessPolicy createdPolicy = authorizationService.createAccessPolicy(requestAccessPolicy);
-
+ AccessPolicy createdPolicy = serviceFacade.createAccessPolicy(requestAccessPolicy);
String locationUri = generateAccessPolicyUri(createdPolicy);
return generateCreatedResponse(URI.create(locationUri), createdPolicy).build();
}
@@ -152,11 +129,7 @@
@ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403),
@ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) })
public Response getAccessPolicies() {
-
- verifyAuthorizerIsManaged();
- authorizeAccess(RequestAction.READ);
-
- List<AccessPolicy> accessPolicies = authorizationService.getAccessPolicies();
+ List<AccessPolicy> accessPolicies = serviceFacade.getAccessPolicies();
if (accessPolicies == null) {
accessPolicies = Collections.emptyList();
}
@@ -191,17 +164,7 @@
public Response getAccessPolicy(
@ApiParam(value = "The access policy id.", required = true)
@PathParam("id") final String identifier) {
-
- verifyAuthorizerIsManaged();
- authorizeAccess(RequestAction.READ);
-
- final AccessPolicy accessPolicy = authorizationService.getAccessPolicy(identifier);
- if (accessPolicy == null) {
- logger.warn("The specified access policy id [{}] does not exist.", identifier);
-
- throw new ResourceNotFoundException("The specified policy does not exist in this registry.");
- }
-
+ final AccessPolicy accessPolicy = serviceFacade.getAccessPolicy(identifier);
return generateOkResponse(accessPolicy).build();
}
@@ -241,17 +204,11 @@
@PathParam("resource")
final String rawResource) {
- verifyAuthorizerIsManaged();
- authorizeAccess(RequestAction.READ);
-
// parse the action and resource type
final RequestAction requestAction = RequestAction.valueOfValue(action);
final String resource = "/" + rawResource;
- AccessPolicy accessPolicy = authorizationService.getAccessPolicy(resource, requestAction);
- if (accessPolicy == null) {
- throw new ResourceNotFoundException("No policy found for action='" + action + "', resource='" + resource + "'");
- }
+ final AccessPolicy accessPolicy = serviceFacade.getAccessPolicy(resource, requestAction);
return generateOkResponse(accessPolicy).build();
}
@@ -285,15 +242,12 @@
@ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409 + " The NiFi Registry might not be configured to use a ConfigurableAccessPolicyProvider.") })
public Response updateAccessPolicy(
@Context
- final HttpServletRequest httpServletRequest,
+ final HttpServletRequest httpServletRequest,
@ApiParam(value = "The access policy id.", required = true)
@PathParam("id")
- final String identifier,
+ final String identifier,
@ApiParam(value = "The access policy configuration details.", required = true)
- final AccessPolicy requestAccessPolicy) {
-
- verifyAuthorizerSupportsConfigurablePolicies();
- authorizeAccess(RequestAction.WRITE);
+ final AccessPolicy requestAccessPolicy) {
if (requestAccessPolicy == null) {
throw new IllegalArgumentException("Access policy details must be specified when updating a policy.");
@@ -303,9 +257,7 @@
+ "policy id of the requested resource (%s).", requestAccessPolicy.getIdentifier(), identifier));
}
- AccessPolicy createdPolicy = authorizationService.updateAccessPolicy(requestAccessPolicy);
-
- String locationUri = generateAccessPolicyUri(createdPolicy);
+ final AccessPolicy createdPolicy = serviceFacade.updateAccessPolicy(requestAccessPolicy);
return generateOkResponse(createdPolicy).build();
}
@@ -336,19 +288,21 @@
@ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404),
@ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409 + " The NiFi Registry might not be configured to use a ConfigurableAccessPolicyProvider.") })
public Response removeAccessPolicy(
- @Context final HttpServletRequest httpServletRequest,
+ @Context
+ final HttpServletRequest httpServletRequest,
+ @ApiParam(value = "The version is used to verify the client is working with the latest version of the entity.", required = true)
+ @QueryParam(VERSION)
+ final LongParameter version,
+ @ApiParam(value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.")
+ @QueryParam(CLIENT_ID)
+ @DefaultValue(StringUtils.EMPTY)
+ final ClientIdParameter clientId,
@ApiParam(value = "The access policy id.", required = true)
@PathParam("id")
- final String identifier) {
+ final String identifier) {
- verifyAuthorizerSupportsConfigurablePolicies();
- authorizeAccess(RequestAction.DELETE);
- AccessPolicy deletedPolicy = authorizationService.deleteAccessPolicy(identifier);
- if (deletedPolicy == null) {
- logger.warn("The specified access policy id [{}] does not exist.", identifier);
-
- throw new ResourceNotFoundException("The specified policy does not exist in this registry.");
- }
+ final RevisionInfo revisionInfo = getRevisionInfo(version, clientId);
+ final AccessPolicy deletedPolicy = serviceFacade.deleteAccessPolicy(identifier, revisionInfo);
return generateOkResponse(deletedPolicy).build();
}
@@ -376,32 +330,10 @@
@ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401),
@ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403) })
public Response getResources() {
- authorizeAccess(RequestAction.READ);
-
- final List<Resource> resources = authorizationService.getResources();
-
+ final List<Resource> resources = serviceFacade.getResources();
return generateOkResponse(resources).build();
}
-
- private void verifyAuthorizerIsManaged() {
- if (!AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer)) {
- throw new IllegalStateException(AuthorizationService.MSG_NON_MANAGED_AUTHORIZER);
- }
- }
-
- private void verifyAuthorizerSupportsConfigurablePolicies() {
- if (!AuthorizerCapabilityDetection.isConfigurableAccessPolicyProvider(authorizer)) {
- verifyAuthorizerIsManaged();
- throw new IllegalStateException(AuthorizationService.MSG_NON_CONFIGURABLE_POLICIES);
- }
- }
-
- private void authorizeAccess(RequestAction actionType) {
- final Authorizable policiesAuthorizable = authorizableLookup.getPoliciesAuthorizable();
- authorizationService.authorize(policiesAuthorizable, actionType);
- }
-
private String generateAccessPolicyUri(final AccessPolicySummary accessPolicy) {
return generateResourceUri("policies", accessPolicy.getIdentifier());
}
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java
index 30374f0..278f635 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AccessResource.java
@@ -36,11 +36,11 @@
import org.apache.nifi.registry.security.authentication.exception.InvalidCredentialsException;
import org.apache.nifi.registry.security.authorization.user.NiFiUser;
import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils;
-import org.apache.nifi.registry.service.AuthorizationService;
import org.apache.nifi.registry.web.exception.UnauthorizedException;
import org.apache.nifi.registry.web.security.authentication.jwt.JwtService;
import org.apache.nifi.registry.web.security.authentication.kerberos.KerberosSpnegoIdentityProvider;
import org.apache.nifi.registry.web.security.authentication.x509.X509IdentityProvider;
+import org.apache.nifi.registry.web.service.ServiceFacade;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -74,7 +74,6 @@
private static final Logger logger = LoggerFactory.getLogger(AccessResource.class);
private NiFiRegistryProperties properties;
- private AuthorizationService authorizationService;
private JwtService jwtService;
private X509IdentityProvider x509IdentityProvider;
private KerberosSpnegoIdentityProvider kerberosSpnegoIdentityProvider;
@@ -83,19 +82,18 @@
@Autowired
public AccessResource(
NiFiRegistryProperties properties,
- AuthorizationService authorizationService,
JwtService jwtService,
X509IdentityProvider x509IdentityProvider,
@Nullable KerberosSpnegoIdentityProvider kerberosSpnegoIdentityProvider,
@Nullable IdentityProvider identityProvider,
+ ServiceFacade serviceFacade,
EventService eventService) {
- super(eventService);
+ super(serviceFacade, eventService);
this.properties = properties;
this.jwtService = jwtService;
this.x509IdentityProvider = x509IdentityProvider;
this.kerberosSpnegoIdentityProvider = kerberosSpnegoIdentityProvider;
this.identityProvider = identityProvider;
- this.authorizationService = authorizationService;
}
/**
@@ -123,7 +121,7 @@
throw new WebApplicationException(new Throwable("Unable to access details for current user."));
}
- final CurrentUser currentUser = authorizationService.getCurrentUser();
+ final CurrentUser currentUser = serviceFacade.getCurrentUser();
currentUser.setLoginSupported(httpServletRequest.isSecure() && identityProvider != null);
return generateOkResponse(currentUser).build();
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ApplicationResource.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ApplicationResource.java
index adabbfc..d33fd8b 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ApplicationResource.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ApplicationResource.java
@@ -20,6 +20,10 @@
import org.apache.commons.lang3.Validate;
import org.apache.nifi.registry.event.EventService;
import org.apache.nifi.registry.hook.Event;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
+import org.apache.nifi.registry.revision.web.ClientIdParameter;
+import org.apache.nifi.registry.revision.web.LongParameter;
+import org.apache.nifi.registry.web.service.ServiceFacade;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -35,6 +39,9 @@
public class ApplicationResource {
+ public static final String CLIENT_ID = "clientId";
+ public static final String VERSION = "version";
+
public static final String PROXY_SCHEME_HTTP_HEADER = "X-ProxyScheme";
public static final String PROXY_HOST_HTTP_HEADER = "X-ProxyHost";
public static final String PROXY_PORT_HTTP_HEADER = "X-ProxyPort";
@@ -55,10 +62,14 @@
@Context
private UriInfo uriInfo;
+ protected final ServiceFacade serviceFacade;
private final EventService eventService;
- public ApplicationResource(final EventService eventService) {
+ public ApplicationResource(final ServiceFacade serviceFacade,
+ final EventService eventService) {
+ this.serviceFacade = serviceFacade;
this.eventService = eventService;
+ Validate.notNull(this.serviceFacade);
Validate.notNull(this.eventService);
}
@@ -196,4 +207,18 @@
return null;
}
+ /**
+ * Creates a RevisionInfo from the version and clientId parameters.
+ *
+ * @param version the version
+ * @param clientId the client id
+ * @return the RevisionInfo
+ */
+ protected RevisionInfo getRevisionInfo(final LongParameter version, final ClientIdParameter clientId) {
+ final RevisionInfo revisionInfo = new RevisionInfo();
+ revisionInfo.setVersion(version == null ? null : version.getLong());
+ revisionInfo.setClientId(clientId.getClientId());
+ return revisionInfo;
+ }
+
}
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AuthorizableApplicationResource.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AuthorizableApplicationResource.java
deleted file mode 100644
index 83240c7..0000000
--- a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/AuthorizableApplicationResource.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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.registry.web.api;
-
-import org.apache.nifi.registry.authorization.Resource;
-import org.apache.nifi.registry.bucket.BucketItem;
-import org.apache.nifi.registry.event.EventService;
-import org.apache.nifi.registry.security.authorization.AuthorizableLookup;
-import org.apache.nifi.registry.security.authorization.RequestAction;
-import org.apache.nifi.registry.security.authorization.resource.Authorizable;
-import org.apache.nifi.registry.security.authorization.resource.ResourceType;
-import org.apache.nifi.registry.service.AuthorizationService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Objects;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-public class AuthorizableApplicationResource extends ApplicationResource {
-
- private static final Logger logger = LoggerFactory.getLogger(AuthorizableApplicationResource.class);
-
- protected final AuthorizationService authorizationService;
- protected final AuthorizableLookup authorizableLookup;
-
- protected AuthorizableApplicationResource(
- AuthorizationService authorizationService,
- EventService eventService) {
- super(eventService);
- this.authorizationService = authorizationService;
- this.authorizableLookup = authorizationService.getAuthorizableLookup();
- }
-
- protected void authorizeBucketAccess(RequestAction actionType, String bucketIdentifier) {
- final Authorizable bucketAuthorizable = authorizableLookup.getBucketAuthorizable(bucketIdentifier);
- authorizationService.authorize(bucketAuthorizable, actionType);
- }
-
- protected void authorizeBucketItemAccess(RequestAction actionType, BucketItem bucketItem) {
- authorizeBucketAccess(actionType, bucketItem.getBucketIdentifier());
- }
-
- protected Set<String> getAuthorizedBucketIds(RequestAction actionType) {
- return authorizationService
- .getAuthorizedResources(actionType, ResourceType.Bucket)
- .stream()
- .map(AuthorizableApplicationResource::extractBucketIdFromResource)
- .filter(Objects::nonNull)
- .distinct()
- .collect(Collectors.toSet());
- }
-
- private static String extractBucketIdFromResource(Resource resource) {
-
- if (resource == null || resource.getIdentifier() == null || !resource.getIdentifier().startsWith("/buckets/")) {
- return null;
- }
-
- String[] pathComponents = resource.getIdentifier().split("/");
- if (pathComponents.length < 3) {
- return null;
- }
- return pathComponents[2];
- }
-
-}
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketBundleResource.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketBundleResource.java
index abfeddd..60d8df5 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketBundleResource.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketBundleResource.java
@@ -30,11 +30,7 @@
import org.apache.nifi.registry.extension.bundle.BundleType;
import org.apache.nifi.registry.extension.bundle.BundleTypeValues;
import org.apache.nifi.registry.extension.bundle.BundleVersion;
-import org.apache.nifi.registry.security.authorization.RequestAction;
-import org.apache.nifi.registry.service.AuthorizationService;
-import org.apache.nifi.registry.service.RegistryService;
-import org.apache.nifi.registry.web.link.LinkService;
-import org.apache.nifi.registry.web.security.PermissionsService;
+import org.apache.nifi.registry.web.service.ServiceFacade;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.slf4j.Logger;
@@ -61,25 +57,13 @@
description = "Create extension bundles scoped to an existing bucket in the registry. ",
authorizations = { @Authorization("Authorization") }
)
-public class BucketBundleResource extends AuthorizableApplicationResource {
+public class BucketBundleResource extends ApplicationResource {
private static final Logger LOGGER = LoggerFactory.getLogger(BucketBundleResource.class);
- private final RegistryService registryService;
- private final LinkService linkService;
- private final PermissionsService permissionsService;
-
@Autowired
- public BucketBundleResource(
- final RegistryService registryService,
- final LinkService linkService,
- final PermissionsService permissionsService,
- final AuthorizationService authorizationService,
- final EventService eventService) {
- super(authorizationService, eventService);
- this.registryService = registryService;
- this.linkService = linkService;
- this.permissionsService =permissionsService;
+ public BucketBundleResource(final ServiceFacade serviceFacade, final EventService eventService) {
+ super(serviceFacade, eventService);
}
@POST
@@ -124,22 +108,14 @@
@FormDataParam("sha256")
final String clientSha256) throws IOException {
- authorizeBucketAccess(RequestAction.WRITE, bucketId);
-
LOGGER.debug("Creating extension bundle version for bundle type {}", new Object[]{bundleType});
- final BundleVersion createdBundleVersion = registryService.createBundleVersion(
+ final BundleVersion createdBundleVersion = serviceFacade.createBundleVersion(
bucketId, bundleType, fileInputStream, clientSha256);
publish(EventFactory.extensionBundleCreated(createdBundleVersion.getBundle()));
publish(EventFactory.extensionBundleVersionCreated(createdBundleVersion));
- linkService.populateLinks(createdBundleVersion.getVersionMetadata());
- linkService.populateLinks(createdBundleVersion.getBundle());
- linkService.populateLinks(createdBundleVersion.getBucket());
-
- permissionsService.populateItemPermissions(createdBundleVersion.getBundle());
-
return Response.status(Response.Status.OK).entity(createdBundleVersion).build();
}
@@ -166,14 +142,9 @@
public Response getExtensionBundles(
@PathParam("bucketId")
@ApiParam(value = "The bucket identifier", required = true)
- final String bucketId
- ) {
- authorizeBucketAccess(RequestAction.READ, bucketId);
+ final String bucketId) {
- final List<Bundle> bundles = registryService.getBundlesByBucket(bucketId);
- permissionsService.populateItemPermissions(bundles);
- linkService.populateLinks(bundles);
-
+ final List<Bundle> bundles = serviceFacade.getBundlesByBucket(bucketId);
return Response.status(Response.Status.OK).entity(bundles).build();
}
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java
index ecf1b7a..8dd7290 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java
@@ -32,14 +32,11 @@
import org.apache.nifi.registry.flow.VersionedFlow;
import org.apache.nifi.registry.flow.VersionedFlowSnapshot;
import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata;
-import org.apache.nifi.registry.security.authorization.RequestAction;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
+import org.apache.nifi.registry.revision.web.ClientIdParameter;
+import org.apache.nifi.registry.revision.web.LongParameter;
import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils;
-import org.apache.nifi.registry.service.AuthorizationService;
-import org.apache.nifi.registry.service.RegistryService;
-import org.apache.nifi.registry.web.link.LinkService;
-import org.apache.nifi.registry.web.security.PermissionsService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.apache.nifi.registry.web.service.ServiceFacade;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -47,12 +44,14 @@
import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;
@@ -65,25 +64,11 @@
description = "Create flows scoped to an existing bucket in the registry.",
authorizations = { @Authorization("Authorization") }
)
-public class BucketFlowResource extends AuthorizableApplicationResource {
-
- private static final Logger logger = LoggerFactory.getLogger(BucketFlowResource.class);
-
- private final RegistryService registryService;
- private final LinkService linkService;
- private final PermissionsService permissionsService;
+public class BucketFlowResource extends ApplicationResource {
@Autowired
- public BucketFlowResource(
- final RegistryService registryService,
- final LinkService linkService,
- final PermissionsService permissionsService,
- final AuthorizationService authorizationService,
- final EventService eventService) {
- super(authorizationService, eventService);
- this.registryService = registryService;
- this.linkService = linkService;
- this.permissionsService =permissionsService;
+ public BucketFlowResource(final ServiceFacade serviceFacade, final EventService eventService) {
+ super(serviceFacade, eventService);
}
@POST
@@ -108,18 +93,14 @@
public Response createFlow(
@PathParam("bucketId")
@ApiParam("The bucket identifier")
- final String bucketId,
+ final String bucketId,
@ApiParam(value = "The details of the flow to create.", required = true)
- final VersionedFlow flow) {
+ final VersionedFlow flow) {
- authorizeBucketAccess(RequestAction.WRITE, bucketId);
verifyPathParamsMatchBody(bucketId, flow);
- final VersionedFlow createdFlow = registryService.createFlow(bucketId, flow);
+ final VersionedFlow createdFlow = serviceFacade.createFlow(bucketId, flow);
publish(EventFactory.flowCreated(createdFlow));
-
- permissionsService.populateItemPermissions(createdFlow);
- linkService.populateLinks(createdFlow);
return Response.status(Response.Status.OK).entity(createdFlow).build();
}
@@ -148,12 +129,7 @@
@ApiParam("The bucket identifier")
final String bucketId) {
- authorizeBucketAccess(RequestAction.READ, bucketId);
-
- final List<VersionedFlow> flows = registryService.getFlows(bucketId);
- permissionsService.populateItemPermissions(flows);
- linkService.populateLinks(flows);
-
+ final List<VersionedFlow> flows = serviceFacade.getFlows(bucketId);
return Response.status(Response.Status.OK).entity(flows).build();
}
@@ -185,12 +161,7 @@
@ApiParam("The flow identifier")
final String flowId) {
- authorizeBucketAccess(RequestAction.READ, bucketId);
-
- final VersionedFlow flow = registryService.getFlow(bucketId, flowId);
- permissionsService.populateItemPermissions(flow);
- linkService.populateLinks(flow);
-
+ final VersionedFlow flow = serviceFacade.getFlow(bucketId, flowId);
return Response.status(Response.Status.OK).entity(flow).build();
}
@@ -225,16 +196,12 @@
final VersionedFlow flow) {
verifyPathParamsMatchBody(bucketId, flowId, flow);
- authorizeBucketAccess(RequestAction.WRITE, bucketId);
// bucketId and flowId fields are optional in the body parameter, but required before calling the service layer
setBucketItemMetadataIfMissing(bucketId, flowId, flow);
- final VersionedFlow updatedFlow = registryService.updateFlow(flow);
+ final VersionedFlow updatedFlow = serviceFacade.updateFlow(flow);
publish(EventFactory.flowUpdated(updatedFlow));
- permissionsService.populateItemPermissions(updatedFlow);
- linkService.populateLinks(updatedFlow);
-
return Response.status(Response.Status.OK).entity(updatedFlow).build();
}
@@ -258,6 +225,13 @@
@ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404),
@ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) })
public Response deleteFlow(
+ @ApiParam(value = "The version is used to verify the client is working with the latest version of the entity.", required = true)
+ @QueryParam(VERSION)
+ final LongParameter version,
+ @ApiParam(value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.")
+ @QueryParam(CLIENT_ID)
+ @DefaultValue(StringUtils.EMPTY)
+ final ClientIdParameter clientId,
@PathParam("bucketId")
@ApiParam("The bucket identifier")
final String bucketId,
@@ -265,9 +239,10 @@
@ApiParam("The flow identifier")
final String flowId) {
- authorizeBucketAccess(RequestAction.DELETE, bucketId);
- final VersionedFlow deletedFlow = registryService.deleteFlow(bucketId, flowId);
+ final RevisionInfo revisionInfo = getRevisionInfo(version, clientId);
+ final VersionedFlow deletedFlow = serviceFacade.deleteFlow(bucketId, flowId, revisionInfo);
publish(EventFactory.flowDeleted(deletedFlow));
+
return Response.status(Response.Status.OK).entity(deletedFlow).build();
}
@@ -303,7 +278,6 @@
final VersionedFlowSnapshot snapshot) {
verifyPathParamsMatchBody(bucketId, flowId, snapshot);
- authorizeBucketAccess(RequestAction.WRITE, bucketId);
// bucketId and flowId fields are optional in the body parameter, but required before calling the service layer
setSnaphotMetadataIfMissing(bucketId, flowId, snapshot);
@@ -311,16 +285,9 @@
final String userIdentity = NiFiUserUtils.getNiFiUserIdentity();
snapshot.getSnapshotMetadata().setAuthor(userIdentity);
- final VersionedFlowSnapshot createdSnapshot = registryService.createFlowSnapshot(snapshot);
+ final VersionedFlowSnapshot createdSnapshot = serviceFacade.createFlowSnapshot(snapshot);
publish(EventFactory.flowVersionCreated(createdSnapshot));
- if (createdSnapshot.getSnapshotMetadata() != null) {
- linkService.populateLinks(createdSnapshot.getSnapshotMetadata());
- }
- if (createdSnapshot.getBucket() != null) {
- permissionsService.populateBucketPermissions(createdSnapshot.getBucket());
- linkService.populateLinks(createdSnapshot.getBucket());
- }
return Response.status(Response.Status.OK).entity(createdSnapshot).build();
}
@@ -352,13 +319,7 @@
@ApiParam("The flow identifier")
final String flowId) {
- authorizeBucketAccess(RequestAction.READ, bucketId);
-
- final SortedSet<VersionedFlowSnapshotMetadata> snapshots = registryService.getFlowSnapshots(bucketId, flowId);
- if (snapshots != null ) {
- linkService.populateLinks(snapshots);
- }
-
+ final SortedSet<VersionedFlowSnapshotMetadata> snapshots = serviceFacade.getFlowSnapshots(bucketId, flowId);
return Response.status(Response.Status.OK).entity(snapshots).build();
}
@@ -389,12 +350,7 @@
@ApiParam("The flow identifier")
final String flowId) {
- authorizeBucketAccess(RequestAction.READ, bucketId);
-
- final VersionedFlowSnapshotMetadata latestMetadata = registryService.getLatestFlowSnapshotMetadata(bucketId, flowId);
- final VersionedFlowSnapshot lastSnapshot = registryService.getFlowSnapshot(bucketId, flowId, latestMetadata.getVersion());
- populateLinksAndPermissions(lastSnapshot);
-
+ final VersionedFlowSnapshot lastSnapshot = serviceFacade.getLatestFlowSnapshot(bucketId, flowId);
return Response.status(Response.Status.OK).entity(lastSnapshot).build();
}
@@ -425,11 +381,7 @@
@ApiParam("The flow identifier")
final String flowId) {
- authorizeBucketAccess(RequestAction.READ, bucketId);
-
- final VersionedFlowSnapshotMetadata latest = registryService.getLatestFlowSnapshotMetadata(bucketId, flowId);
- linkService.populateLinks(latest);
-
+ final VersionedFlowSnapshotMetadata latest = serviceFacade.getLatestFlowSnapshotMetadata(bucketId, flowId);
return Response.status(Response.Status.OK).entity(latest).build();
}
@@ -463,11 +415,8 @@
@PathParam("versionNumber")
@ApiParam("The version number")
final Integer versionNumber) {
- authorizeBucketAccess(RequestAction.READ, bucketId);
- final VersionedFlowSnapshot snapshot = registryService.getFlowSnapshot(bucketId, flowId, versionNumber);
- populateLinksAndPermissions(snapshot);
-
+ final VersionedFlowSnapshot snapshot = serviceFacade.getFlowSnapshot(bucketId, flowId, versionNumber);
return Response.status(Response.Status.OK).entity(snapshot).build();
}
@@ -494,37 +443,21 @@
public Response getFlowDiff(
@PathParam("bucketId")
@ApiParam("The bucket identifier")
- final String bucketId,
+ final String bucketId,
@PathParam("flowId")
@ApiParam("The flow identifier")
- final String flowId,
+ final String flowId,
@PathParam("versionA")
@ApiParam("The first version number")
- final Integer versionNumberA,
+ final Integer versionNumberA,
@PathParam("versionB")
@ApiParam("The second version number")
- final Integer versionNumberB) {
- authorizeBucketAccess(RequestAction.READ, bucketId);
- VersionedFlowDifference result = registryService.getFlowDiff(bucketId, flowId, versionNumberA, versionNumberB);
+ final Integer versionNumberB) {
+
+ final VersionedFlowDifference result = serviceFacade.getFlowDiff(bucketId, flowId, versionNumberA, versionNumberB);
return Response.status(Response.Status.OK).entity(result).build();
}
- private void populateLinksAndPermissions(VersionedFlowSnapshot snapshot) {
- if (snapshot.getSnapshotMetadata() != null) {
- linkService.populateLinks(snapshot.getSnapshotMetadata());
- }
-
- if (snapshot.getFlow() != null) {
- linkService.populateLinks(snapshot.getFlow());
- }
-
- if (snapshot.getBucket() != null) {
- permissionsService.populateBucketPermissions(snapshot.getBucket());
- linkService.populateLinks(snapshot.getBucket());
- }
-
- }
-
private static void verifyPathParamsMatchBody(String bucketIdParam, BucketItem bodyBucketItem) throws BadRequestException {
if (StringUtils.isBlank(bucketIdParam)) {
throw new BadRequestException("Bucket id path parameter cannot be blank");
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java
index 34387e8..1692a29 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java
@@ -26,36 +26,29 @@
import io.swagger.annotations.ExtensionProperty;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.bucket.Bucket;
-import org.apache.nifi.registry.bucket.BucketItem;
import org.apache.nifi.registry.event.EventFactory;
import org.apache.nifi.registry.event.EventService;
import org.apache.nifi.registry.field.Fields;
-import org.apache.nifi.registry.security.authorization.RequestAction;
-import org.apache.nifi.registry.security.authorization.exception.AccessDeniedException;
-import org.apache.nifi.registry.security.authorization.resource.Authorizable;
-import org.apache.nifi.registry.service.AuthorizationService;
-import org.apache.nifi.registry.service.RegistryService;
-import org.apache.nifi.registry.web.link.LinkService;
-import org.apache.nifi.registry.web.security.PermissionsService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
+import org.apache.nifi.registry.revision.web.ClientIdParameter;
+import org.apache.nifi.registry.revision.web.LongParameter;
+import org.apache.nifi.registry.web.service.ServiceFacade;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
-import javax.ws.rs.core.Context;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriInfo;
-import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -67,30 +60,11 @@
"Search for and retrieve existing buckets.",
authorizations = { @Authorization("Authorization") }
)
-public class BucketResource extends AuthorizableApplicationResource {
-
- private static final Logger logger = LoggerFactory.getLogger(BucketResource.class);
-
- @Context
- UriInfo uriInfo;
-
- private final LinkService linkService;
-
- private final RegistryService registryService;
-
- private final PermissionsService permissionsService;
+public class BucketResource extends ApplicationResource {
@Autowired
- public BucketResource(
- final RegistryService registryService,
- final LinkService linkService,
- final PermissionsService permissionsService,
- final AuthorizationService authorizationService,
- final EventService eventService) {
- super(authorizationService, eventService);
- this.registryService = registryService;
- this.linkService = linkService;
- this.permissionsService = permissionsService;
+ public BucketResource(final ServiceFacade serviceFacade, final EventService eventService) {
+ super(serviceFacade, eventService);
}
@POST
@@ -112,13 +86,9 @@
public Response createBucket(
@ApiParam(value = "The bucket to create", required = true)
final Bucket bucket) {
- authorizeAccess(RequestAction.WRITE);
- final Bucket createdBucket = registryService.createBucket(bucket);
+ final Bucket createdBucket = serviceFacade.createBucket(bucket);
publish(EventFactory.bucketCreated(createdBucket));
-
- permissionsService.populateBucketPermissions(createdBucket);
- linkService.populateLinks(createdBucket);
return Response.status(Response.Status.OK).entity(createdBucket).build();
}
@@ -134,7 +104,7 @@
)
@ApiResponses({ @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401) })
public Response getBuckets() {
-
+ // ServiceFacade will determine which buckets the user is authorized for
// Note: We don't explicitly check for access to (READ, /buckets) because
// a user might have access to individual buckets without top-level access.
// For example, a user that has (READ, /buckets/bucket-id-1) but not access
@@ -142,18 +112,7 @@
// This has the side effect that a user with no access to any buckets
// gets an empty array returned from this endpoint instead of 403 as one
// might expect.
-
- final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
-
- if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
- // not authorized for any bucket, return empty list of items
- return Response.status(Response.Status.OK).entity(new ArrayList<BucketItem>()).build();
- }
-
- final List<Bucket> buckets = registryService.getBuckets(authorizedBucketIds);
- permissionsService.populateBucketPermissions(buckets);
- linkService.populateLinks(buckets);
-
+ final List<Bucket> buckets = serviceFacade.getBuckets();
return Response.status(Response.Status.OK).entity(buckets).build();
}
@@ -180,11 +139,7 @@
@ApiParam("The bucket identifier")
final String bucketId) {
- authorizeBucketAccess(RequestAction.READ, bucketId);
- final Bucket bucket = registryService.getBucket(bucketId);
- permissionsService.populateBucketPermissions(bucket);
- linkService.populateLinks(bucket);
-
+ final Bucket bucket = serviceFacade.getBucket(bucketId);
return Response.status(Response.Status.OK).entity(bucket).build();
}
@@ -211,9 +166,9 @@
public Response updateBucket(
@PathParam("bucketId")
@ApiParam("The bucket identifier")
- final String bucketId,
+ final String bucketId,
@ApiParam(value = "The updated bucket", required = true)
- final Bucket bucket) {
+ final Bucket bucket) {
if (StringUtils.isBlank(bucketId)) {
throw new BadRequestException("Bucket id cannot be blank");
@@ -229,13 +184,8 @@
bucket.setIdentifier(bucketId);
}
- authorizeBucketAccess(RequestAction.WRITE, bucketId);
-
- final Bucket updatedBucket = registryService.updateBucket(bucket);
+ final Bucket updatedBucket = serviceFacade.updateBucket(bucket);
publish(EventFactory.bucketUpdated(updatedBucket));
-
- permissionsService.populateBucketPermissions(updatedBucket);
- linkService.populateLinks(updatedBucket);
return Response.status(Response.Status.OK).entity(updatedBucket).build();
}
@@ -259,16 +209,23 @@
@ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403),
@ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404) })
public Response deleteBucket(
+ @ApiParam(value = "The version is used to verify the client is working with the latest version of the entity.", required = true)
+ @QueryParam(VERSION)
+ final LongParameter version,
+ @ApiParam(value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.")
+ @QueryParam(CLIENT_ID)
+ @DefaultValue(StringUtils.EMPTY)
+ final ClientIdParameter clientId,
@PathParam("bucketId")
@ApiParam("The bucket identifier")
- final String bucketId) {
+ final String bucketId) {
if (StringUtils.isBlank(bucketId)) {
throw new BadRequestException("Bucket id cannot be blank");
}
- authorizeBucketAccess(RequestAction.DELETE, bucketId);
- final Bucket deletedBucket = registryService.deleteBucket(bucketId);
+ final RevisionInfo revisionInfo = getRevisionInfo(version, clientId);
+ final Bucket deletedBucket = serviceFacade.deleteBucket(bucketId, revisionInfo);
publish(EventFactory.bucketDeleted(deletedBucket));
return Response.status(Response.Status.OK).entity(deletedBucket).build();
@@ -284,14 +241,9 @@
response = Fields.class
)
public Response getAvailableBucketFields() {
- final Set<String> bucketFields = registryService.getBucketFields();
+ final Set<String> bucketFields = serviceFacade.getBucketFields();
final Fields fields = new Fields(bucketFields);
return Response.status(Response.Status.OK).entity(fields).build();
}
- private void authorizeAccess(RequestAction actionType) throws AccessDeniedException {
- final Authorizable bucketsAuthorizable = authorizableLookup.getBucketsAuthorizable();
- authorizationService.authorize(bucketsAuthorizable, actionType);
- }
-
}
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BundleResource.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BundleResource.java
index 48532fb..1edc0a7 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BundleResource.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BundleResource.java
@@ -24,7 +24,6 @@
import io.swagger.annotations.Authorization;
import io.swagger.annotations.Extension;
import io.swagger.annotations.ExtensionProperty;
-import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.event.EventFactory;
import org.apache.nifi.registry.event.EventService;
import org.apache.nifi.registry.extension.bundle.Bundle;
@@ -33,11 +32,8 @@
import org.apache.nifi.registry.extension.bundle.BundleVersionFilterParams;
import org.apache.nifi.registry.extension.bundle.BundleVersionMetadata;
import org.apache.nifi.registry.extension.component.ExtensionMetadata;
-import org.apache.nifi.registry.security.authorization.RequestAction;
-import org.apache.nifi.registry.service.AuthorizationService;
-import org.apache.nifi.registry.service.RegistryService;
-import org.apache.nifi.registry.web.link.LinkService;
-import org.apache.nifi.registry.web.security.PermissionsService;
+import org.apache.nifi.registry.web.service.ServiceFacade;
+import org.apache.nifi.registry.web.service.StreamingContent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -51,10 +47,7 @@
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
-import java.util.Set;
import java.util.SortedSet;
@Component
@@ -64,23 +57,13 @@
description = "Gets metadata about extension bundles and their versions. ",
authorizations = { @Authorization("Authorization") }
)
-public class BundleResource extends AuthorizableApplicationResource {
+public class BundleResource extends ApplicationResource {
public static final String CONTENT_DISPOSITION_HEADER = "content-disposition";
- private final RegistryService registryService;
- private final LinkService linkService;
- private final PermissionsService permissionsService;
@Autowired
- public BundleResource(final RegistryService registryService,
- final LinkService linkService,
- final PermissionsService permissionsService,
- final AuthorizationService authorizationService,
- final EventService eventService) {
- super(authorizationService, eventService);
- this.registryService = registryService;
- this.linkService = linkService;
- this.permissionsService = permissionsService;
+ public BundleResource(final ServiceFacade serviceFacade, final EventService eventService) {
+ super(serviceFacade, eventService);
}
// ---------- Extension Bundles ----------
@@ -111,21 +94,10 @@
"such as 'nifi-%' to select all bundles where the artifactId starts with 'nifi-'.")
final String artifactId) {
- final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
- if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
- // not authorized for any bucket, return empty list of items
- return Response.status(Response.Status.OK).entity(new ArrayList<>()).build();
- }
-
final BundleFilterParams filterParams = BundleFilterParams.of(bucketName, groupId, artifactId);
- List<Bundle> bundles = registryService.getBundles(authorizedBucketIds, filterParams);
- if (bundles == null) {
- bundles = Collections.emptyList();
- }
- permissionsService.populateItemPermissions(bundles);
- linkService.populateLinks(bundles);
-
+ // Service facade will return only bundles from authorized buckets
+ final List<Bundle> bundles = serviceFacade.getBundles(filterParams);
return Response.status(Response.Status.OK).entity(bundles).build();
}
@@ -155,10 +127,7 @@
@ApiParam("The extension bundle identifier")
final String bundleId) {
- final Bundle bundle = getBundleWithBucketReadAuthorization(bundleId);
- permissionsService.populateItemPermissions(bundle);
- linkService.populateLinks(bundle);
-
+ final Bundle bundle = serviceFacade.getBundle(bundleId);
return Response.status(Response.Status.OK).entity(bundle).build();
}
@@ -188,14 +157,8 @@
@ApiParam("The extension bundle identifier")
final String bundleId) {
- final Bundle bundle = getBundleWithBucketReadAuthorization(bundleId);
-
- final Bundle deletedBundle = registryService.deleteBundle(bundle);
+ final Bundle deletedBundle = serviceFacade.deleteBundle(bundleId);
publish(EventFactory.extensionBundleDeleted(deletedBundle));
-
- permissionsService.populateItemPermissions(deletedBundle);
- linkService.populateLinks(deletedBundle);
-
return Response.status(Response.Status.OK).entity(deletedBundle).build();
}
@@ -228,16 +191,8 @@
final String version
) {
- final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
- if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
- // not authorized for any bucket, return empty list of items
- return Response.status(Response.Status.OK).entity(new ArrayList<>()).build();
- }
-
final BundleVersionFilterParams filterParams = BundleVersionFilterParams.of(groupId, artifactId, version);
- final SortedSet<BundleVersionMetadata> bundleVersions = registryService.getBundleVersions(authorizedBucketIds, filterParams);
- linkService.populateLinks(bundleVersions);
-
+ final SortedSet<BundleVersionMetadata> bundleVersions = serviceFacade.getBundleVersions(filterParams);
return Response.status(Response.Status.OK).entity(bundleVersions).build();
}
@@ -268,10 +223,7 @@
@ApiParam("The extension bundle identifier")
final String bundleId) {
- final Bundle bundle = getBundleWithBucketReadAuthorization(bundleId);
- final SortedSet<BundleVersionMetadata> bundleVersions = registryService.getBundleVersions(bundle.getIdentifier());
- linkService.populateLinks(bundleVersions);
-
+ final SortedSet<BundleVersionMetadata> bundleVersions = serviceFacade.getBundleVersions(bundleId);
return Response.status(Response.Status.OK).entity(bundleVersions).build();
}
@@ -304,10 +256,7 @@
@ApiParam("The version of the bundle")
final String version) {
- final Bundle bundle = getBundleWithBucketReadAuthorization(bundleId);
- final BundleVersion bundleVersion = registryService.getBundleVersion(bundle.getBucketIdentifier(), bundleId, version);
- linkService.populateLinks(bundleVersion);
-
+ final BundleVersion bundleVersion = serviceFacade.getBundleVersion(bundleId, version);
return Response.ok(bundleVersion).build();
}
@@ -340,13 +289,13 @@
@ApiParam("The version of the bundle")
final String version) {
- final Bundle bundle = getBundleWithBucketReadAuthorization(bundleId);
- final BundleVersion bundleVersion = registryService.getBundleVersion(bundle.getBucketIdentifier(), bundleId, version);
+ final StreamingContent streamingContent = serviceFacade.getBundleVersionContent(bundleId, version);
- final StreamingOutput streamingOutput = (output) -> registryService.writeBundleVersionContent(bundleVersion, output);
+ final String filename = streamingContent.getFilename();
+ final StreamingOutput output = streamingContent.getOutput();
- return Response.ok(streamingOutput)
- .header(CONTENT_DISPOSITION_HEADER,"attachment; filename = " + bundleVersion.getFilename())
+ return Response.ok(output)
+ .header(CONTENT_DISPOSITION_HEADER,"attachment; filename = " + filename)
.build();
}
@@ -379,13 +328,8 @@
@ApiParam("The version of the bundle")
final String version) {
- final Bundle bundle = getBundleWithBucketReadAuthorization(bundleId);
- final BundleVersion bundleVersion = registryService.getBundleVersion(bundle.getBucketIdentifier(), bundleId, version);
-
- final BundleVersion deletedBundleVersion = registryService.deleteBundleVersion(bundleVersion);
+ final BundleVersion deletedBundleVersion = serviceFacade.deleteBundleVersion(bundleId, version);
publish(EventFactory.extensionBundleVersionDeleted(deletedBundleVersion));
- linkService.populateLinks(deletedBundleVersion);
-
return Response.status(Response.Status.OK).entity(deletedBundleVersion).build();
}
@@ -419,11 +363,7 @@
@ApiParam("The version of the bundle")
final String version) {
- final Bundle bundle = getBundleWithBucketReadAuthorization(bundleId);
- final BundleVersion bundleVersion = registryService.getBundleVersion(bundle.getBucketIdentifier(), bundleId, version);
-
- final SortedSet<ExtensionMetadata> extensions = registryService.getExtensionMetadata(bundleVersion);
- linkService.populateLinks(extensions);
+ final SortedSet<ExtensionMetadata> extensions = serviceFacade.getExtensionMetadata(bundleId, version);
return Response.ok(extensions).build();
}
@@ -461,10 +401,8 @@
final String name
) {
- final Bundle bundle = getBundleWithBucketReadAuthorization(bundleId);
- final BundleVersion bundleVersion = registryService.getBundleVersion(bundle.getBucketIdentifier(), bundleId, version);
-
- final org.apache.nifi.registry.extension.component.manifest.Extension extension = registryService.getExtension(bundleVersion, name);
+ final org.apache.nifi.registry.extension.component.manifest.Extension extension =
+ serviceFacade.getExtension(bundleId, version, name);
return Response.ok(extension).build();
}
@@ -499,10 +437,7 @@
@ApiParam("The fully qualified name of the extension")
final String name
) {
- final Bundle bundle = getBundleWithBucketReadAuthorization(bundleId);
- final BundleVersion bundleVersion = registryService.getBundleVersion(bundle.getBucketIdentifier(), bundleId, version);
-
- final StreamingOutput streamingOutput = (output) -> registryService.writeExtensionDocs(bundleVersion, name, output);
+ final StreamingOutput streamingOutput = serviceFacade.getExtensionDocs(bundleId, version, name);
return Response.ok(streamingOutput).build();
}
@@ -537,29 +472,8 @@
@ApiParam("The fully qualified name of the extension")
final String name
) {
- final Bundle bundle = getBundleWithBucketReadAuthorization(bundleId);
- final BundleVersion bundleVersion = registryService.getBundleVersion(bundle.getBucketIdentifier(), bundleId, version);
-
- final StreamingOutput streamingOutput = (output) -> registryService.writeAdditionalDetailsDocs(bundleVersion, name, output);
+ final StreamingOutput streamingOutput = serviceFacade.getAdditionalDetailsDocs(bundleId, version, name);
return Response.ok(streamingOutput).build();
}
- /**
- * Retrieves the extension bundle with the given id and ensures the current user has authorization to read the bucket it belongs to.
- *
- * @param bundleId the bundle id
- * @return the extension bundle
- */
- private Bundle getBundleWithBucketReadAuthorization(final String bundleId) {
- final Bundle bundle = registryService.getBundle(bundleId);
-
- // this should never happen, but if somehow the back-end didn't populate the bucket id let's make sure the flow isn't returned
- if (StringUtils.isBlank(bundle.getBucketIdentifier())) {
- throw new IllegalStateException("Unable to authorize access because bucket identifier is null or blank");
- }
-
- authorizeBucketAccess(RequestAction.READ, bundle.getBucketIdentifier());
- return bundle;
- }
-
}
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ConfigResource.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ConfigResource.java
index 48f3518..37482c6 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ConfigResource.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ConfigResource.java
@@ -25,12 +25,7 @@
import io.swagger.annotations.ExtensionProperty;
import org.apache.nifi.registry.RegistryConfiguration;
import org.apache.nifi.registry.event.EventService;
-import org.apache.nifi.registry.security.authorization.Authorizer;
-import org.apache.nifi.registry.security.authorization.AuthorizerCapabilityDetection;
-import org.apache.nifi.registry.security.authorization.RequestAction;
-import org.apache.nifi.registry.security.authorization.exception.AccessDeniedException;
-import org.apache.nifi.registry.security.authorization.resource.Authorizable;
-import org.apache.nifi.registry.service.AuthorizationService;
+import org.apache.nifi.registry.web.service.ServiceFacade;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -48,13 +43,13 @@
description = "Retrieves the configuration for this NiFi Registry.",
authorizations = { @Authorization("Authorization") }
)
-public class ConfigResource extends AuthorizableApplicationResource {
+public class ConfigResource extends ApplicationResource {
@Autowired
public ConfigResource(
- final AuthorizationService authorizationService,
+ final ServiceFacade serviceFacade,
final EventService eventService) {
- super(authorizationService, eventService);
+ super(serviceFacade, eventService);
}
@GET
@@ -74,36 +69,7 @@
@ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401),
@ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401) })
public Response getConfiguration() {
-
- final RegistryConfiguration config = new RegistryConfiguration();
-
- boolean hasAnyConfigurationAccess = false;
- AccessDeniedException lastAccessDeniedException = null;
- final Authorizer authorizer = authorizationService.getAuthorizer();
- try {
- final Authorizable policyAuthorizer = authorizableLookup.getPoliciesAuthorizable();
- authorizationService.authorize(policyAuthorizer, RequestAction.READ);
- config.setSupportsManagedAuthorizer(AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer));
- config.setSupportsConfigurableAuthorizer(AuthorizerCapabilityDetection.isConfigurableAccessPolicyProvider(authorizer));
- hasAnyConfigurationAccess = true;
- } catch (AccessDeniedException e) {
- lastAccessDeniedException = e;
- }
-
- try {
- authorizationService.authorize(authorizableLookup.getTenantsAuthorizable(), RequestAction.READ);
- config.setSupportsConfigurableUsersAndGroups(AuthorizerCapabilityDetection.isConfigurableUserGroupProvider(authorizer));
- hasAnyConfigurationAccess = true;
- } catch (AccessDeniedException e) {
- lastAccessDeniedException = e;
- }
-
- if (!hasAnyConfigurationAccess) {
- // If the user doesn't have access to any configuration, then throw the exception.
- // Otherwise, return what they can access.
- throw lastAccessDeniedException;
- }
-
+ final RegistryConfiguration config = serviceFacade.getRegistryConfiguration();
return Response.status(Response.Status.OK).entity(config).build();
}
}
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ExtensionRepoResource.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ExtensionRepoResource.java
index 1297dad..140794b 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ExtensionRepoResource.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ExtensionRepoResource.java
@@ -25,10 +25,8 @@
import io.swagger.annotations.Extension;
import io.swagger.annotations.ExtensionProperty;
import org.apache.commons.lang3.StringUtils;
-import org.apache.nifi.registry.bucket.Bucket;
import org.apache.nifi.registry.event.EventService;
import org.apache.nifi.registry.exception.ResourceNotFoundException;
-import org.apache.nifi.registry.extension.bundle.BundleVersion;
import org.apache.nifi.registry.extension.bundle.BundleVersionFilterParams;
import org.apache.nifi.registry.extension.bundle.BundleVersionMetadata;
import org.apache.nifi.registry.extension.component.ExtensionMetadata;
@@ -38,10 +36,8 @@
import org.apache.nifi.registry.extension.repo.ExtensionRepoGroup;
import org.apache.nifi.registry.extension.repo.ExtensionRepoVersion;
import org.apache.nifi.registry.extension.repo.ExtensionRepoVersionSummary;
-import org.apache.nifi.registry.security.authorization.RequestAction;
-import org.apache.nifi.registry.service.AuthorizationService;
-import org.apache.nifi.registry.service.RegistryService;
-import org.apache.nifi.registry.web.link.LinkService;
+import org.apache.nifi.registry.web.service.ServiceFacade;
+import org.apache.nifi.registry.web.service.StreamingContent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -50,13 +46,10 @@
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
-import javax.ws.rs.core.Link;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
-import java.util.ArrayList;
import java.util.List;
-import java.util.Set;
import java.util.SortedSet;
@Component
@@ -66,21 +59,13 @@
description = "Interact with extension bundles via the hierarchy of bucket/group/artifact/version. ",
authorizations = { @Authorization("Authorization") }
)
-public class ExtensionRepoResource extends AuthorizableApplicationResource {
+public class ExtensionRepoResource extends ApplicationResource {
public static final String CONTENT_DISPOSITION_HEADER = "content-disposition";
- private final RegistryService registryService;
- private final LinkService linkService;
@Autowired
- public ExtensionRepoResource(
- final RegistryService registryService,
- final LinkService linkService,
- final AuthorizationService authorizationService,
- final EventService eventService) {
- super(authorizationService, eventService);
- this.registryService = registryService;
- this.linkService = linkService;
+ public ExtensionRepoResource(final ServiceFacade serviceFacade, final EventService eventService) {
+ super(serviceFacade, eventService);
}
@GET
@@ -99,15 +84,7 @@
@ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404),
@ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) })
public Response getExtensionRepoBuckets() {
-
- final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
- if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
- // not authorized for any bucket, return empty list of items
- return Response.status(Response.Status.OK).entity(new ArrayList<>()).build();
- }
-
- final SortedSet<ExtensionRepoBucket> repoBuckets = registryService.getExtensionRepoBuckets(authorizedBucketIds);
- linkService.populateFullLinks(repoBuckets, getBaseUri());
+ final SortedSet<ExtensionRepoBucket> repoBuckets = serviceFacade.getExtensionRepoBuckets(getBaseUri());
return Response.status(Response.Status.OK).entity(repoBuckets).build();
}
@@ -137,11 +114,7 @@
@ApiParam("The bucket name")
final String bucketName
) {
- final Bucket bucket = registryService.getBucketByName(bucketName);
- authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
-
- final SortedSet<ExtensionRepoGroup> repoGroups = registryService.getExtensionRepoGroups(bucket);
- linkService.populateFullLinks(repoGroups, getBaseUri());
+ final SortedSet<ExtensionRepoGroup> repoGroups = serviceFacade.getExtensionRepoGroups(getBaseUri(), bucketName);
return Response.status(Response.Status.OK).entity(repoGroups).build();
}
@@ -174,11 +147,7 @@
@ApiParam("The group id")
final String groupId
) {
- final Bucket bucket = registryService.getBucketByName(bucketName);
- authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
-
- final SortedSet<ExtensionRepoArtifact> repoArtifacts = registryService.getExtensionRepoArtifacts(bucket, groupId);
- linkService.populateFullLinks(repoArtifacts, getBaseUri());
+ final SortedSet<ExtensionRepoArtifact> repoArtifacts = serviceFacade.getExtensionRepoArtifacts(getBaseUri(), bucketName, groupId);
return Response.status(Response.Status.OK).entity(repoArtifacts).build();
}
@@ -214,11 +183,8 @@
@ApiParam("The artifact identifier")
final String artifactId
) {
- final Bucket bucket = registryService.getBucketByName(bucketName);
- authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
-
- final SortedSet<ExtensionRepoVersionSummary> repoVersions = registryService.getExtensionRepoVersions(bucket, groupId, artifactId);
- linkService.populateFullLinks(repoVersions, getBaseUri());
+ final SortedSet<ExtensionRepoVersionSummary> repoVersions = serviceFacade.getExtensionRepoVersions(
+ getBaseUri(), bucketName, groupId, artifactId);
return Response.status(Response.Status.OK).entity(repoVersions).build();
}
@@ -256,41 +222,8 @@
@ApiParam("The version")
final String version
) {
- final Bucket bucket = registryService.getBucketByName(bucketName);
- authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
-
- final BundleVersion bundleVersion = registryService.getBundleVersion(bucket.getIdentifier(), groupId, artifactId, version);
-
- final String extensionsUri = generateResourceUri(
- "extension-repository",
- bundleVersion.getBucket().getName(),
- bundleVersion.getBundle().getGroupId(),
- bundleVersion.getBundle().getArtifactId(),
- bundleVersion.getVersionMetadata().getVersion(),
- "extensions");
-
- final String downloadUri = generateResourceUri(
- "extension-repository",
- bundleVersion.getBucket().getName(),
- bundleVersion.getBundle().getGroupId(),
- bundleVersion.getBundle().getArtifactId(),
- bundleVersion.getVersionMetadata().getVersion(),
- "content");
-
- final String sha256Uri = generateResourceUri(
- "extension-repository",
- bundleVersion.getBucket().getName(),
- bundleVersion.getBundle().getGroupId(),
- bundleVersion.getBundle().getArtifactId(),
- bundleVersion.getVersionMetadata().getVersion(),
- "sha256");
-
- final ExtensionRepoVersion repoVersion = new ExtensionRepoVersion();
- repoVersion.setExtensionsLink(Link.fromUri(extensionsUri).rel("extensions").build());
- repoVersion.setDownloadLink(Link.fromUri(downloadUri).rel("content").build());
- repoVersion.setSha256Link(Link.fromUri(sha256Uri).rel("sha256").build());
- repoVersion.setSha256Supplied(bundleVersion.getVersionMetadata().getSha256Supplied());
-
+ final ExtensionRepoVersion repoVersion = serviceFacade.getExtensionRepoVersion(
+ getBaseUri(), bucketName, groupId, artifactId, version);
return Response.ok(repoVersion).build();
}
@@ -329,15 +262,10 @@
@ApiParam("The version")
final String version
) {
- final Bucket bucket = registryService.getBucketByName(bucketName);
- authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
- final BundleVersion bundleVersion = registryService.getBundleVersion(bucket.getIdentifier(), groupId, artifactId, version);
- final SortedSet<ExtensionMetadata> extensions = registryService.getExtensionMetadata(bundleVersion);
-
- final List<ExtensionRepoExtensionMetadata> extensionRepoExtensions = new ArrayList<>(extensions.size());
- extensions.forEach(e -> extensionRepoExtensions.add(new ExtensionRepoExtensionMetadata(e)));
- linkService.populateFullLinks(extensionRepoExtensions, getBaseUri());
+ final List<ExtensionRepoExtensionMetadata> extensionRepoExtensions =
+ serviceFacade.getExtensionRepoExtensions(
+ getBaseUri(), bucketName, groupId, artifactId, version);
return Response.ok(extensionRepoExtensions).build();
}
@@ -380,11 +308,9 @@
@ApiParam("The fully qualified name of the extension")
final String name
) {
- final Bucket bucket = registryService.getBucketByName(bucketName);
- authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
-
- final BundleVersion bundleVersion = registryService.getBundleVersion(bucket.getIdentifier(), groupId, artifactId, version);
- final org.apache.nifi.registry.extension.component.manifest.Extension extension = registryService.getExtension(bundleVersion, name);
+ final org.apache.nifi.registry.extension.component.manifest.Extension extension =
+ serviceFacade.getExtensionRepoExtension(
+ getBaseUri(), bucketName, groupId, artifactId, version, name);
return Response.ok(extension).build();
}
@@ -426,11 +352,8 @@
@ApiParam("The fully qualified name of the extension")
final String name
) {
- final Bucket bucket = registryService.getBucketByName(bucketName);
- authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
-
- final BundleVersion bundleVersion = registryService.getBundleVersion(bucket.getIdentifier(), groupId, artifactId, version);
- final StreamingOutput streamingOutput = (output) -> registryService.writeExtensionDocs(bundleVersion, name, output);
+ final StreamingOutput streamingOutput = serviceFacade.getExtensionRepoExtensionDocs(
+ getBaseUri(), bucketName, groupId, artifactId, version, name);
return Response.ok(streamingOutput).build();
}
@@ -472,11 +395,8 @@
@ApiParam("The fully qualified name of the extension")
final String name
) {
- final Bucket bucket = registryService.getBucketByName(bucketName);
- authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
-
- final BundleVersion bundleVersion = registryService.getBundleVersion(bucket.getIdentifier(), groupId, artifactId, version);
- final StreamingOutput streamingOutput = (output) -> registryService.writeAdditionalDetailsDocs(bundleVersion, name, output);
+ final StreamingOutput streamingOutput = serviceFacade.getExtensionRepoExtensionAdditionalDocs(
+ getBaseUri(), bucketName, groupId, artifactId, version, name);
return Response.ok(streamingOutput).build();
}
@@ -514,14 +434,14 @@
@ApiParam("The version")
final String version
) {
- final Bucket bucket = registryService.getBucketByName(bucketName);
- authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
+ final StreamingContent streamingContent = serviceFacade.getExtensionRepoVersionContent(
+ bucketName, groupId, artifactId, version);
- final BundleVersion bundleVersion = registryService.getBundleVersion(bucket.getIdentifier(), groupId, artifactId, version);
- final StreamingOutput streamingOutput = (output) -> registryService.writeBundleVersionContent(bundleVersion, output);
+ final String filename = streamingContent.getFilename();
+ final StreamingOutput streamingOutput = streamingContent.getOutput();
return Response.ok(streamingOutput)
- .header(CONTENT_DISPOSITION_HEADER,"attachment; filename = " + bundleVersion.getFilename())
+ .header(CONTENT_DISPOSITION_HEADER,"attachment; filename = " + filename)
.build();
}
@@ -560,11 +480,7 @@
@ApiParam("The version")
final String version
) {
- final Bucket bucket = registryService.getBucketByName(bucketName);
- authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
-
- final BundleVersion bundleVersion = registryService.getBundleVersion(bucket.getIdentifier(), groupId, artifactId, version);
- final String sha256Hex = bundleVersion.getVersionMetadata().getSha256();
+ final String sha256Hex = serviceFacade.getExtensionRepoVersionSha256(bucketName, groupId, artifactId, version);
return Response.ok(sha256Hex, MediaType.TEXT_PLAIN).build();
}
@@ -596,12 +512,6 @@
@ApiParam("The version")
final String version
) {
- final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
- if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
- // not authorized for any bucket, return empty list of items
- return Response.status(Response.Status.OK).entity(new ArrayList<>()).build();
- }
-
// Since we are using the filter params which are optional in the service layer, we need to validate these path params here
if (StringUtils.isBlank(groupId)) {
@@ -618,7 +528,7 @@
final BundleVersionFilterParams filterParams = BundleVersionFilterParams.of(groupId, artifactId, version);
- final SortedSet<BundleVersionMetadata> bundleVersions = registryService.getBundleVersions(authorizedBucketIds, filterParams);
+ final SortedSet<BundleVersionMetadata> bundleVersions = serviceFacade.getBundleVersions(filterParams);
if (bundleVersions.isEmpty()) {
throw new ResourceNotFoundException("An extension bundle version does not exist with the specific group, artifact, and version");
} else {
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ExtensionResource.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ExtensionResource.java
index 8f95453..52dbe71 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ExtensionResource.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ExtensionResource.java
@@ -31,10 +31,7 @@
import org.apache.nifi.registry.extension.component.TagCount;
import org.apache.nifi.registry.extension.component.manifest.ExtensionType;
import org.apache.nifi.registry.extension.component.manifest.ProvidedServiceAPI;
-import org.apache.nifi.registry.security.authorization.RequestAction;
-import org.apache.nifi.registry.service.AuthorizationService;
-import org.apache.nifi.registry.service.RegistryService;
-import org.apache.nifi.registry.web.link.LinkService;
+import org.apache.nifi.registry.web.service.ServiceFacade;
import org.springframework.stereotype.Component;
import javax.ws.rs.Consumes;
@@ -44,7 +41,6 @@
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.Set;
import java.util.SortedSet;
@@ -56,18 +52,10 @@
description = "Find and retrieve extensions. ",
authorizations = { @Authorization("Authorization") }
)
-public class ExtensionResource extends AuthorizableApplicationResource {
+public class ExtensionResource extends ApplicationResource {
- private final RegistryService registryService;
- private final LinkService linkService;
-
- public ExtensionResource(final AuthorizationService authorizationService,
- final EventService eventService,
- final RegistryService registryService,
- final LinkService linkService) {
- super(authorizationService, eventService);
- this.registryService = registryService;
- this.linkService = linkService;
+ public ExtensionResource(final ServiceFacade serviceFacade, final EventService eventService) {
+ super(serviceFacade, eventService);
}
@GET
@@ -98,20 +86,13 @@
final Set<String> tags
) {
- final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
- if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
- // not authorized for any bucket, return empty list of items
- return Response.status(Response.Status.OK).entity(new ArrayList<>()).build();
- }
-
final ExtensionFilterParams filterParams = new ExtensionFilterParams.Builder()
.bundleType(bundleType)
.extensionType(extensionType)
.addTags(tags == null ? Collections.emptyList() : tags)
.build();
- final SortedSet<ExtensionMetadata> extensionMetadata = registryService.getExtensionMetadata(authorizedBucketIds, filterParams);
- linkService.populateLinks(extensionMetadata);
+ final SortedSet<ExtensionMetadata> extensionMetadata = serviceFacade.getExtensionMetadata(filterParams);
final ExtensionMetadataContainer container = new ExtensionMetadataContainer();
container.setExtensions(extensionMetadata);
@@ -152,21 +133,13 @@
@ApiParam(value = "The version of the bundle containing the service API class", required = true)
final String version
) {
-
- final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
- if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
- // not authorized for any bucket, return empty list of items
- return Response.status(Response.Status.OK).entity(new ArrayList<>()).build();
- }
-
final ProvidedServiceAPI serviceAPI = new ProvidedServiceAPI();
serviceAPI.setClassName(className);
serviceAPI.setGroupId(groupId);
serviceAPI.setArtifactId(artifactId);
serviceAPI.setVersion(version);
- final SortedSet<ExtensionMetadata> extensionMetadata = registryService.getExtensionMetadata(authorizedBucketIds, serviceAPI);
- linkService.populateLinks(extensionMetadata);
+ final SortedSet<ExtensionMetadata> extensionMetadata = serviceFacade.getExtensionMetadata(serviceAPI);
final ExtensionMetadataContainer container = new ExtensionMetadataContainer();
container.setExtensions(extensionMetadata);
@@ -193,7 +166,7 @@
@ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404),
@ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) })
public Response getTags() {
- final SortedSet<TagCount> tags = registryService.getExtensionTags();
+ final SortedSet<TagCount> tags = serviceFacade.getExtensionTags();
return Response.status(Response.Status.OK).entity(tags).build();
}
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/FlowResource.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/FlowResource.java
index ec42130..c805988 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/FlowResource.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/FlowResource.java
@@ -24,18 +24,12 @@
import io.swagger.annotations.Authorization;
import io.swagger.annotations.Extension;
import io.swagger.annotations.ExtensionProperty;
-import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.registry.event.EventService;
import org.apache.nifi.registry.field.Fields;
import org.apache.nifi.registry.flow.VersionedFlow;
import org.apache.nifi.registry.flow.VersionedFlowSnapshot;
import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata;
-import org.apache.nifi.registry.security.authorization.RequestAction;
-import org.apache.nifi.registry.security.authorization.exception.AccessDeniedException;
-import org.apache.nifi.registry.service.AuthorizationService;
-import org.apache.nifi.registry.service.RegistryService;
-import org.apache.nifi.registry.web.link.LinkService;
-import org.apache.nifi.registry.web.security.PermissionsService;
+import org.apache.nifi.registry.web.service.ServiceFacade;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -56,22 +50,11 @@
description = "Gets metadata about flows.",
authorizations = { @Authorization("Authorization") }
)
-public class FlowResource extends AuthorizableApplicationResource {
-
- private final RegistryService registryService;
- private final LinkService linkService;
- private final PermissionsService permissionsService;
+public class FlowResource extends ApplicationResource {
@Autowired
- public FlowResource(final RegistryService registryService,
- final LinkService linkService,
- final PermissionsService permissionsService,
- final AuthorizationService authorizationService,
- final EventService eventService) {
- super(authorizationService, eventService);
- this.registryService = registryService;
- this.linkService = linkService;
- this.permissionsService = permissionsService;
+ public FlowResource(final ServiceFacade serviceFacade, final EventService eventService) {
+ super(serviceFacade, eventService);
}
@GET
@@ -84,7 +67,7 @@
response = Fields.class
)
public Response getAvailableFlowFields() {
- final Set<String> flowFields = registryService.getFlowFields();
+ final Set<String> flowFields = serviceFacade.getFlowFields();
final Fields fields = new Fields(flowFields);
return Response.status(Response.Status.OK).entity(fields).build();
}
@@ -113,20 +96,9 @@
public Response getFlow(
@PathParam("flowId")
@ApiParam("The flow identifier")
- final String flowId) {
+ final String flowId) {
- final VersionedFlow flow = registryService.getFlow(flowId);
-
- // this should never happen, but if somehow the back-end didn't populate the bucket id let's make sure the flow isn't returned
- if (StringUtils.isBlank(flow.getBucketIdentifier())) {
- throw new IllegalStateException("Unable to authorize access because bucket identifier is null or blank");
- }
-
- authorizeBucketAccess(RequestAction.READ, flow.getBucketIdentifier());
-
- permissionsService.populateItemPermissions(flow);
- linkService.populateLinks(flow);
-
+ final VersionedFlow flow = serviceFacade.getFlow(flowId);
return Response.status(Response.Status.OK).entity(flow).build();
}
@@ -154,22 +126,9 @@
public Response getFlowVersions(
@PathParam("flowId")
@ApiParam("The flow identifier")
- final String flowId) {
+ final String flowId) {
- final VersionedFlow flow = registryService.getFlow(flowId);
-
- final String bucketId = flow.getBucketIdentifier();
- if (StringUtils.isBlank(bucketId)) {
- throw new IllegalStateException("Unable to authorize access because bucket identifier is null or blank");
- }
-
- authorizeBucketAccess(RequestAction.READ, bucketId);
-
- final SortedSet<VersionedFlowSnapshotMetadata> snapshots = registryService.getFlowSnapshots(bucketId, flowId);
- if (snapshots != null ) {
- linkService.populateLinks(snapshots);
- }
-
+ final SortedSet<VersionedFlowSnapshotMetadata> snapshots = serviceFacade.getFlowSnapshots(flowId);
return Response.status(Response.Status.OK).entity(snapshots).build();
}
@@ -197,22 +156,12 @@
public Response getFlowVersion(
@PathParam("flowId")
@ApiParam("The flow identifier")
- final String flowId,
+ final String flowId,
@PathParam("versionNumber")
@ApiParam("The version number")
- final Integer versionNumber) {
+ final Integer versionNumber) {
- final VersionedFlowSnapshotMetadata latestMetadata = registryService.getLatestFlowSnapshotMetadata(flowId);
-
- final String bucketId = latestMetadata.getBucketIdentifier();
- if (StringUtils.isBlank(bucketId)) {
- throw new IllegalStateException("Unable to authorize access because bucket identifier is null or blank");
- }
-
- authorizeBucketAccess(RequestAction.READ, bucketId);
-
- final VersionedFlowSnapshot snapshot = registryService.getFlowSnapshot(bucketId, flowId, versionNumber);
- populateLinksAndPermissions(snapshot);
+ final VersionedFlowSnapshot snapshot = serviceFacade.getFlowSnapshot(flowId, versionNumber);
return Response.status(Response.Status.OK).entity(snapshot).build();
}
@@ -239,20 +188,9 @@
public Response getLatestFlowVersion(
@PathParam("flowId")
@ApiParam("The flow identifier")
- final String flowId) {
+ final String flowId) {
- final VersionedFlowSnapshotMetadata latestMetadata = registryService.getLatestFlowSnapshotMetadata(flowId);
-
- final String bucketId = latestMetadata.getBucketIdentifier();
- if (StringUtils.isBlank(bucketId)) {
- throw new IllegalStateException("Unable to authorize access because bucket identifier is null or blank");
- }
-
- authorizeBucketAccess(RequestAction.READ, bucketId);
-
- final VersionedFlowSnapshot lastSnapshot = registryService.getFlowSnapshot(bucketId, flowId, latestMetadata.getVersion());
- populateLinksAndPermissions(lastSnapshot);
-
+ final VersionedFlowSnapshot lastSnapshot = serviceFacade.getLatestFlowSnapshot(flowId);
return Response.status(Response.Status.OK).entity(lastSnapshot).build();
}
@@ -279,43 +217,10 @@
public Response getLatestFlowVersionMetadata(
@PathParam("flowId")
@ApiParam("The flow identifier")
- final String flowId) {
+ final String flowId) {
- final VersionedFlowSnapshotMetadata latestMetadata = registryService.getLatestFlowSnapshotMetadata(flowId);
-
- final String bucketId = latestMetadata.getBucketIdentifier();
- if (StringUtils.isBlank(bucketId)) {
- throw new IllegalStateException("Unable to authorize access because bucket identifier is null or blank");
- }
-
- authorizeBucketAccess(RequestAction.READ, bucketId);
-
- linkService.populateLinks(latestMetadata);
+ final VersionedFlowSnapshotMetadata latestMetadata = serviceFacade.getLatestFlowSnapshotMetadata(flowId);
return Response.status(Response.Status.OK).entity(latestMetadata).build();
}
- // override the base implementation so we can provide a different error message that doesn't include the bucket id
- protected void authorizeBucketAccess(RequestAction action, String bucketId) {
- try {
- super.authorizeBucketAccess(RequestAction.READ, bucketId);
- } catch (AccessDeniedException e) {
- throw new AccessDeniedException("User not authorized to view the specified flow.", e);
- }
- }
-
- private void populateLinksAndPermissions(VersionedFlowSnapshot snapshot) {
- if (snapshot.getSnapshotMetadata() != null) {
- linkService.populateLinks(snapshot.getSnapshotMetadata());
- }
-
- if (snapshot.getFlow() != null) {
- linkService.populateLinks(snapshot.getFlow());
- }
-
- if (snapshot.getBucket() != null) {
- permissionsService.populateBucketPermissions(snapshot.getBucket());
- linkService.populateLinks(snapshot.getBucket());
- }
-
- }
}
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ItemResource.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ItemResource.java
index 56ee286..9548074 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ItemResource.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ItemResource.java
@@ -27,13 +27,7 @@
import org.apache.nifi.registry.bucket.BucketItem;
import org.apache.nifi.registry.event.EventService;
import org.apache.nifi.registry.field.Fields;
-import org.apache.nifi.registry.security.authorization.RequestAction;
-import org.apache.nifi.registry.service.AuthorizationService;
-import org.apache.nifi.registry.service.RegistryService;
-import org.apache.nifi.registry.web.link.LinkService;
-import org.apache.nifi.registry.web.security.PermissionsService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.apache.nifi.registry.web.service.ServiceFacade;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -46,8 +40,6 @@
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -58,28 +50,14 @@
description = "Retrieve items across all buckets for which the user is authorized.",
authorizations = { @Authorization("Authorization") }
)
-public class ItemResource extends AuthorizableApplicationResource {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(ItemResource.class);
+public class ItemResource extends ApplicationResource {
@Context
UriInfo uriInfo;
- private final LinkService linkService;
- private final PermissionsService permissionsService;
- private final RegistryService registryService;
-
@Autowired
- public ItemResource(
- final RegistryService registryService,
- final LinkService linkService,
- final PermissionsService permissionsService,
- final AuthorizationService authorizationService,
- final EventService eventService) {
- super(authorizationService, eventService);
- this.registryService = registryService;
- this.linkService = linkService;
- this.permissionsService = permissionsService;
+ public ItemResource(final ServiceFacade serviceFacade, final EventService eventService) {
+ super(serviceFacade, eventService);
}
@@ -95,7 +73,7 @@
)
@ApiResponses({ @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401) })
public Response getItems() {
-
+ // Service facade with return only items from authorized buckets
// Note: We don't explicitly check for access to (READ, /buckets) or
// (READ, /items ) because a user might have access to individual buckets
// without top-level access. For example, a user that has
@@ -103,20 +81,7 @@
// get a 403 error returned from this endpoint. This has the side effect
// that a user with no access to any buckets gets an empty array returned
// from this endpoint instead of 403 as one might expect.
-
- final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
- if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
- // not authorized for any bucket, return empty list of items
- return Response.status(Response.Status.OK).entity(new ArrayList<BucketItem>()).build();
- }
-
- List<BucketItem> items = registryService.getBucketItems(authorizedBucketIds);
- if (items == null) {
- items = Collections.emptyList();
- }
- permissionsService.populateItemPermissions(items);
- linkService.populateLinks(items);
-
+ final List<BucketItem> items = serviceFacade.getBucketItems();
return Response.status(Response.Status.OK).entity(items).build();
}
@@ -146,12 +111,7 @@
@ApiParam("The bucket identifier")
final String bucketId) {
- authorizeBucketAccess(RequestAction.READ, bucketId);
-
- final List<BucketItem> items = registryService.getBucketItems(bucketId);
- permissionsService.populateItemPermissions(items);
- linkService.populateLinks(items);
-
+ final List<BucketItem> items = serviceFacade.getBucketItems(bucketId);
return Response.status(Response.Status.OK).entity(items).build();
}
@@ -165,7 +125,7 @@
response = Fields.class
)
public Response getAvailableBucketItemFields() {
- final Set<String> bucketFields = registryService.getBucketItemFields();
+ final Set<String> bucketFields = serviceFacade.getBucketItemFields();
final Fields fields = new Fields(bucketFields);
return Response.status(Response.Status.OK).entity(fields).build();
}
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java
index d9d3521..5d7052b 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/TenantResource.java
@@ -29,26 +29,24 @@
import org.apache.nifi.registry.authorization.UserGroup;
import org.apache.nifi.registry.event.EventFactory;
import org.apache.nifi.registry.event.EventService;
-import org.apache.nifi.registry.exception.ResourceNotFoundException;
-import org.apache.nifi.registry.security.authorization.Authorizer;
-import org.apache.nifi.registry.security.authorization.AuthorizerCapabilityDetection;
-import org.apache.nifi.registry.security.authorization.RequestAction;
-import org.apache.nifi.registry.security.authorization.resource.Authorizable;
-import org.apache.nifi.registry.service.AuthorizationService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
+import org.apache.nifi.registry.revision.web.ClientIdParameter;
+import org.apache.nifi.registry.revision.web.LongParameter;
+import org.apache.nifi.registry.web.service.ServiceFacade;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@@ -65,16 +63,12 @@
description = "Endpoint for managing users and user groups.",
authorizations = { @Authorization("Authorization") }
)
-public class TenantResource extends AuthorizableApplicationResource {
-
- private static final Logger logger = LoggerFactory.getLogger(TenantResource.class);
-
- private Authorizer authorizer;
+public class TenantResource extends ApplicationResource {
@Autowired
- public TenantResource(AuthorizationService authorizationService, EventService eventService) {
- super(authorizationService, eventService);
- authorizer = authorizationService.getAuthorizer();
+ public TenantResource(final ServiceFacade serviceFacade,
+ final EventService eventService) {
+ super(serviceFacade, eventService);
}
@@ -109,25 +103,11 @@
@ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) })
public Response createUser(
@Context
- final HttpServletRequest httpServletRequest,
+ final HttpServletRequest httpServletRequest,
@ApiParam(value = "The user configuration details.", required = true)
- final User requestUser) {
+ final User requestUser) {
- verifyAuthorizerSupportsConfigurableUserGroups();
-
- if (requestUser == null) {
- throw new IllegalArgumentException("User details must be specified when creating a new user.");
- }
- if (requestUser.getIdentifier() != null) {
- throw new IllegalArgumentException("User identifier cannot be specified when creating a new user.");
- }
- if (StringUtils.isBlank(requestUser.getIdentity())) {
- throw new IllegalArgumentException("User identity must be specified when creating a new user.");
- }
-
- authorizeAccess(RequestAction.WRITE);
-
- User createdUser = authorizationService.createUser(requestUser);
+ final User createdUser = serviceFacade.createUser(requestUser);
publish(EventFactory.userCreated(createdUser));
String locationUri = generateUserUri(createdUser);
@@ -160,12 +140,8 @@
@ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403),
@ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) })
public Response getUsers() {
- verifyAuthorizerIsManaged();
-
- authorizeAccess(RequestAction.READ);
-
// get all the users
- final List<User> users = authorizationService.getUsers();
+ final List<User> users = serviceFacade.getUsers();
// generate the response
return generateOkResponse(users).build();
@@ -200,15 +176,7 @@
public Response getUser(
@ApiParam(value = "The user id.", required = true)
@PathParam("id") final String identifier) {
- verifyAuthorizerIsManaged();
- authorizeAccess(RequestAction.READ);
-
- final User user = authorizationService.getUser(identifier);
- if (user == null) {
- logger.warn("The specified user id [{}] does not exist.", identifier);
-
- throw new ResourceNotFoundException("The specified user ID does not exist in this registry.");
- }
+ final User user = serviceFacade.getUser(identifier);
return generateOkResponse(user).build();
}
@@ -249,9 +217,6 @@
@ApiParam(value = "The user configuration details.", required = true)
final User requestUser) {
- verifyAuthorizerSupportsConfigurableUserGroups();
- authorizeAccess(RequestAction.WRITE);
-
if (requestUser == null) {
throw new IllegalArgumentException("User details must be specified when updating a user.");
}
@@ -260,14 +225,8 @@
+ "user id of the requested resource (%s).", requestUser.getIdentifier(), identifier));
}
- final User updatedUser = authorizationService.updateUser(requestUser);
- if (updatedUser == null) {
- logger.warn("The specified user id [{}] does not exist.", identifier);
-
- throw new ResourceNotFoundException("The specified user ID does not exist in this registry.");
- }
+ final User updatedUser = serviceFacade.updateUser(requestUser);
publish(EventFactory.userUpdated(updatedUser));
-
return generateOkResponse(updatedUser).build();
}
@@ -300,22 +259,21 @@
@ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) })
public Response removeUser(
@Context
- final HttpServletRequest httpServletRequest,
+ final HttpServletRequest httpServletRequest,
+ @ApiParam(value = "The version is used to verify the client is working with the latest version of the entity.", required = true)
+ @QueryParam(VERSION)
+ final LongParameter version,
+ @ApiParam(value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.")
+ @QueryParam(CLIENT_ID)
+ @DefaultValue(StringUtils.EMPTY)
+ final ClientIdParameter clientId,
@ApiParam(value = "The user id.", required = true)
@PathParam("id")
- final String identifier) {
+ final String identifier) {
- verifyAuthorizerSupportsConfigurableUserGroups();
- authorizeAccess(RequestAction.DELETE);
-
- final User user = authorizationService.deleteUser(identifier);
- if (user == null) {
- logger.warn("The specified user id [{}] does not exist.", identifier);
-
- throw new ResourceNotFoundException("The specified user ID does not exist in this registry.");
- }
+ final RevisionInfo revisionInfo = getRevisionInfo(version, clientId);
+ final User user = serviceFacade.deleteUser(identifier, revisionInfo);
publish(EventFactory.userDeleted(user));
-
return generateOkResponse(user).build();
}
@@ -351,27 +309,14 @@
@ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) })
public Response createUserGroup(
@Context
- final HttpServletRequest httpServletRequest,
+ final HttpServletRequest httpServletRequest,
@ApiParam(value = "The user group configuration details.", required = true)
- final UserGroup requestUserGroup) {
+ final UserGroup requestUserGroup) {
- verifyAuthorizerSupportsConfigurableUserGroups();
- authorizeAccess(RequestAction.WRITE);
-
- if (requestUserGroup == null) {
- throw new IllegalArgumentException("User group details must be specified when creating a new group.");
- }
- if (requestUserGroup.getIdentifier() != null) {
- throw new IllegalArgumentException("User group ID cannot be specified when creating a new group.");
- }
- if (StringUtils.isBlank(requestUserGroup.getIdentity())) {
- throw new IllegalArgumentException("User group identity must be specified when creating a new group.");
- }
-
- UserGroup createdGroup = authorizationService.createUserGroup(requestUserGroup);
+ final UserGroup createdGroup = serviceFacade.createUserGroup(requestUserGroup);
publish(EventFactory.userGroupCreated(createdGroup));
- String locationUri = generateUserGroupUri(createdGroup);
+ final String locationUri = generateUserGroupUri(createdGroup);
return generateCreatedResponse(URI.create(locationUri), createdGroup).build();
}
@@ -402,10 +347,7 @@
@ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404),
@ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) })
public Response getUserGroups() {
- verifyAuthorizerIsManaged();
- authorizeAccess(RequestAction.READ);
-
- final List<UserGroup> userGroups = authorizationService.getUserGroups();
+ final List<UserGroup> userGroups = serviceFacade.getUserGroups();
return generateOkResponse(userGroups).build();
}
@@ -438,16 +380,7 @@
public Response getUserGroup(
@ApiParam(value = "The user group id.", required = true)
@PathParam("id") final String identifier) {
- verifyAuthorizerIsManaged();
- authorizeAccess(RequestAction.READ);
-
- final UserGroup userGroup = authorizationService.getUserGroup(identifier);
- if (userGroup == null) {
- logger.warn("The specified user group id [{}] does not exist.", identifier);
-
- throw new ResourceNotFoundException("The specified user group ID does not exist in this registry.");
- }
-
+ final UserGroup userGroup = serviceFacade.getUserGroup(identifier);
return generateOkResponse(userGroup).build();
}
@@ -488,8 +421,6 @@
@ApiParam(value = "The user group configuration details.", required = true)
final UserGroup requestUserGroup) {
- verifyAuthorizerSupportsConfigurableUserGroups();
-
if (requestUserGroup == null) {
throw new IllegalArgumentException("User group details must be specified to update a user group.");
}
@@ -498,16 +429,8 @@
+ "user group id of the requested resource (%s).", requestUserGroup.getIdentifier(), identifier));
}
- authorizeAccess(RequestAction.WRITE);
-
- UserGroup updatedUserGroup = authorizationService.updateUserGroup(requestUserGroup);
- if (updatedUserGroup == null) {
- logger.warn("The specified user group id [{}] does not exist.", identifier);
-
- throw new ResourceNotFoundException("The specified user group ID does not exist in this registry.");
- }
+ final UserGroup updatedUserGroup = serviceFacade.updateUserGroup(requestUserGroup);
publish(EventFactory.userGroupUpdated(updatedUserGroup));
-
return generateOkResponse(updatedUserGroup).build();
}
@@ -540,42 +463,24 @@
@ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) })
public Response removeUserGroup(
@Context
- final HttpServletRequest httpServletRequest,
+ final HttpServletRequest httpServletRequest,
+ @ApiParam(value = "The version is used to verify the client is working with the latest version of the entity.", required = true)
+ @QueryParam(VERSION)
+ final LongParameter version,
+ @ApiParam(value = "If the client id is not specified, new one will be generated. This value (whether specified or generated) is included in the response.")
+ @QueryParam(CLIENT_ID)
+ @DefaultValue(StringUtils.EMPTY)
+ final ClientIdParameter clientId,
@ApiParam(value = "The user group id.", required = true)
@PathParam("id")
- final String identifier) {
- verifyAuthorizerSupportsConfigurableUserGroups();
- authorizeAccess(RequestAction.DELETE);
+ final String identifier) {
- final UserGroup userGroup = authorizationService.deleteUserGroup(identifier);
- if (userGroup == null) {
- logger.warn("The specified user group id [{}] does not exist.", identifier);
-
- throw new ResourceNotFoundException("The specified user group ID does not exist in this registry.");
- }
+ final RevisionInfo revisionInfo = getRevisionInfo(version, clientId);
+ final UserGroup userGroup = serviceFacade.deleteUserGroup(identifier, revisionInfo);
publish(EventFactory.userGroupDeleted(userGroup));
-
return generateOkResponse(userGroup).build();
}
-
- private void verifyAuthorizerIsManaged() {
- if (!AuthorizerCapabilityDetection.isManagedAuthorizer(authorizer)) {
- throw new IllegalStateException(AuthorizationService.MSG_NON_MANAGED_AUTHORIZER);
- }
- }
-
- private void verifyAuthorizerSupportsConfigurableUserGroups() {
- if (!AuthorizerCapabilityDetection.isConfigurableUserGroupProvider(authorizer)) {
- throw new IllegalStateException(AuthorizationService.MSG_NON_CONFIGURABLE_USERS);
- }
- }
-
- private void authorizeAccess(RequestAction actionType) {
- final Authorizable tenantsAuthorizable = authorizableLookup.getTenantsAuthorizable();
- authorizationService.authorize(tenantsAuthorizable, actionType);
- }
-
private String generateUserUri(final User user) {
return generateResourceUri("tenants", "users", user.getIdentifier());
}
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/InvalidRevisionExceptionMapper.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/InvalidRevisionExceptionMapper.java
new file mode 100644
index 0000000..e9fd628
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/mapper/InvalidRevisionExceptionMapper.java
@@ -0,0 +1,48 @@
+/*
+ * 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.registry.web.mapper;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.registry.revision.api.InvalidRevisionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Maps invalid revision exceptions into client responses.
+ */
+@Provider
+public class InvalidRevisionExceptionMapper implements ExceptionMapper<InvalidRevisionException> {
+
+ private static final Logger logger = LoggerFactory.getLogger(InvalidRevisionExceptionMapper.class);
+
+ @Override
+ public Response toResponse(final InvalidRevisionException exception) {
+ // log the error
+ logger.info(String.format("%s. Returning %s response.", exception, Response.Status.BAD_REQUEST));
+
+ if (logger.isDebugEnabled()) {
+ logger.debug(StringUtils.EMPTY, exception);
+ }
+
+ return Response.status(Response.Status.BAD_REQUEST).entity(exception.getMessage()).type("text/plain").build();
+ }
+
+}
\ No newline at end of file
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java
index b792830..5bbaa8d 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/NiFiRegistrySecurityConfig.java
@@ -17,6 +17,7 @@
package org.apache.nifi.registry.web.security;
import org.apache.nifi.registry.properties.NiFiRegistryProperties;
+import org.apache.nifi.registry.security.authorization.Authorizer;
import org.apache.nifi.registry.security.authorization.resource.ResourceType;
import org.apache.nifi.registry.service.AuthorizationService;
import org.apache.nifi.registry.web.security.authentication.AnonymousIdentityFilter;
@@ -63,6 +64,9 @@
@Autowired
private AuthorizationService authorizationService;
+ @Autowired
+ private Authorizer authorizer;
+
private AnonymousIdentityFilter anonymousAuthenticationFilter = new AnonymousIdentityFilter();
@Autowired
@@ -142,7 +146,7 @@
private IdentityAuthenticationProvider x509AuthenticationProvider() {
if (x509AuthenticationProvider == null) {
- x509AuthenticationProvider = new X509IdentityAuthenticationProvider(properties, authorizationService.getAuthorizer(), x509IdentityProvider);
+ x509AuthenticationProvider = new X509IdentityAuthenticationProvider(properties, authorizer, x509IdentityProvider);
}
return x509AuthenticationProvider;
}
@@ -156,7 +160,7 @@
private IdentityAuthenticationProvider jwtAuthenticationProvider() {
if (jwtAuthenticationProvider == null) {
- jwtAuthenticationProvider = new IdentityAuthenticationProvider(properties, authorizationService.getAuthorizer(), jwtIdentityProvider);
+ jwtAuthenticationProvider = new IdentityAuthenticationProvider(properties, authorizer, jwtIdentityProvider);
}
return jwtAuthenticationProvider;
}
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/PermissionsService.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/PermissionsService.java
index 1e00ee1..a15fea3 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/PermissionsService.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/security/PermissionsService.java
@@ -16,9 +16,9 @@
*/
package org.apache.nifi.registry.web.security;
+import org.apache.nifi.registry.authorization.Permissions;
import org.apache.nifi.registry.bucket.Bucket;
import org.apache.nifi.registry.bucket.BucketItem;
-import org.apache.nifi.registry.authorization.Permissions;
import org.apache.nifi.registry.security.authorization.AuthorizableLookup;
import org.apache.nifi.registry.security.authorization.resource.Authorizable;
import org.apache.nifi.registry.service.AuthorizationService;
@@ -64,29 +64,24 @@
}
private void populateBucketPermissions(final Bucket bucket, final Permissions knownPermissions) {
-
if (bucket == null) {
return;
}
Permissions bucketPermissions = createPermissionsForBucketId(bucket.getIdentifier(), knownPermissions);
bucket.setPermissions(bucketPermissions);
-
}
private void populateItemPermissions(final BucketItem bucketItem, final Permissions knownPermissions) {
-
if (bucketItem == null) {
return;
}
Permissions bucketItemPermissions = createPermissionsForBucketId(bucketItem.getBucketIdentifier(), knownPermissions);
bucketItem.setPermissions(bucketItemPermissions);
-
}
private Permissions createPermissionsForBucketId(String bucketId, final Permissions knownPermissions) {
-
Authorizable bucketResource = authorizableLookup.getBucketAuthorizable(bucketId);
Permissions permissions = knownPermissions == null
@@ -94,7 +89,6 @@
: authorizationService.getPermissionsForResource(bucketResource, knownPermissions);
return permissions;
-
}
}
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/service/RevisionConfiguration.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/service/RevisionConfiguration.java
new file mode 100644
index 0000000..cc0edf9
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/service/RevisionConfiguration.java
@@ -0,0 +1,43 @@
+/*
+ * 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.registry.web.service;
+
+import org.apache.nifi.registry.revision.api.RevisionManager;
+import org.apache.nifi.registry.revision.entity.RevisableEntityService;
+import org.apache.nifi.registry.revision.entity.StandardRevisableEntityService;
+import org.apache.nifi.registry.revision.jdbc.JdbcRevisionManager;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+/**
+ * Creates the beans needed for revision management.
+ */
+@Configuration
+public class RevisionConfiguration {
+
+ @Bean
+ public synchronized RevisionManager getRevisionManager(final JdbcTemplate jdbcTemplate) {
+ return new JdbcRevisionManager(jdbcTemplate);
+ }
+
+ @Bean
+ public synchronized RevisableEntityService getRevisableEntityService(final RevisionManager revisionManager) {
+ return new StandardRevisableEntityService(revisionManager);
+ }
+
+}
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/service/ServiceFacade.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/service/ServiceFacade.java
new file mode 100644
index 0000000..b1faac4
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/service/ServiceFacade.java
@@ -0,0 +1,235 @@
+/*
+ * 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.registry.web.service;
+
+import org.apache.nifi.registry.RegistryConfiguration;
+import org.apache.nifi.registry.authorization.AccessPolicy;
+import org.apache.nifi.registry.authorization.CurrentUser;
+import org.apache.nifi.registry.authorization.Resource;
+import org.apache.nifi.registry.authorization.User;
+import org.apache.nifi.registry.authorization.UserGroup;
+import org.apache.nifi.registry.bucket.Bucket;
+import org.apache.nifi.registry.bucket.BucketItem;
+import org.apache.nifi.registry.diff.VersionedFlowDifference;
+import org.apache.nifi.registry.extension.bundle.Bundle;
+import org.apache.nifi.registry.extension.bundle.BundleFilterParams;
+import org.apache.nifi.registry.extension.bundle.BundleType;
+import org.apache.nifi.registry.extension.bundle.BundleVersion;
+import org.apache.nifi.registry.extension.bundle.BundleVersionFilterParams;
+import org.apache.nifi.registry.extension.bundle.BundleVersionMetadata;
+import org.apache.nifi.registry.extension.component.ExtensionFilterParams;
+import org.apache.nifi.registry.extension.component.ExtensionMetadata;
+import org.apache.nifi.registry.extension.component.TagCount;
+import org.apache.nifi.registry.extension.component.manifest.Extension;
+import org.apache.nifi.registry.extension.component.manifest.ProvidedServiceAPI;
+import org.apache.nifi.registry.extension.repo.ExtensionRepoArtifact;
+import org.apache.nifi.registry.extension.repo.ExtensionRepoBucket;
+import org.apache.nifi.registry.extension.repo.ExtensionRepoExtensionMetadata;
+import org.apache.nifi.registry.extension.repo.ExtensionRepoGroup;
+import org.apache.nifi.registry.extension.repo.ExtensionRepoVersion;
+import org.apache.nifi.registry.extension.repo.ExtensionRepoVersionSummary;
+import org.apache.nifi.registry.flow.VersionedFlow;
+import org.apache.nifi.registry.flow.VersionedFlowSnapshot;
+import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
+import org.apache.nifi.registry.security.authorization.RequestAction;
+
+import javax.ws.rs.core.StreamingOutput;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+
+public interface ServiceFacade {
+
+ // ---------------------- Bucket methods ----------------------------------------------
+
+ Bucket createBucket(Bucket bucket);
+
+ Bucket getBucket(String bucketIdentifier);
+
+ List<Bucket> getBuckets();
+
+ Bucket updateBucket(Bucket bucket);
+
+ Bucket deleteBucket(String bucketIdentifier, RevisionInfo revisionInfo);
+
+ // ---------------------- BucketItem methods ----------------------------------------------
+
+ List<BucketItem> getBucketItems(String bucketIdentifier);
+
+ List<BucketItem> getBucketItems();
+
+ // ---------------------- Flow methods ----------------------------------------------
+
+ VersionedFlow createFlow(String bucketIdentifier, VersionedFlow versionedFlow);
+
+ VersionedFlow getFlow(String bucketIdentifier, String flowIdentifier);
+
+ VersionedFlow getFlow(String flowIdentifier);
+
+ List<VersionedFlow> getFlows(String bucketId);
+
+ VersionedFlow updateFlow(VersionedFlow versionedFlow);
+
+ VersionedFlow deleteFlow(String bucketIdentifier, String flowIdentifier, RevisionInfo revisionInfo);
+
+ // ---------------------- Flow Snapshot methods ----------------------------------------------
+
+ VersionedFlowSnapshot createFlowSnapshot(VersionedFlowSnapshot flowSnapshot);
+
+ VersionedFlowSnapshot getFlowSnapshot(String bucketIdentifier, String flowIdentifier, Integer version);
+
+ VersionedFlowSnapshot getFlowSnapshot(String flowIdentifier, Integer version);
+
+ VersionedFlowSnapshot getLatestFlowSnapshot(String bucketIdentifier, String flowIdentifier);
+
+ VersionedFlowSnapshot getLatestFlowSnapshot(String flowIdentifier);
+
+ SortedSet<VersionedFlowSnapshotMetadata> getFlowSnapshots(String bucketIdentifier, String flowIdentifier);
+
+ SortedSet<VersionedFlowSnapshotMetadata> getFlowSnapshots(String flowIdentifier);
+
+ VersionedFlowSnapshotMetadata getLatestFlowSnapshotMetadata(String bucketIdentifier, String flowIdentifier);
+
+ VersionedFlowSnapshotMetadata getLatestFlowSnapshotMetadata(String flowIdentifier);
+
+ VersionedFlowDifference getFlowDiff(String bucketIdentifier, String flowIdentifier, Integer versionA, Integer versionB);
+
+ // ---------------------- Bundle methods ----------------------------------------------
+
+ List<Bundle> getBundles(BundleFilterParams filterParams);
+
+ List<Bundle> getBundlesByBucket(String bucketIdentifier);
+
+ Bundle getBundle(String bundleIdentifier);
+
+ Bundle deleteBundle(String bundleIdentifier);
+
+ // ---------------------- Bundle Version methods ----------------------------------------------
+
+ BundleVersion createBundleVersion(String bucketIdentifier, BundleType bundleType, InputStream inputStream, String clientSha256) throws IOException;
+
+ SortedSet<BundleVersionMetadata> getBundleVersions(BundleVersionFilterParams filterParams);
+
+ SortedSet<BundleVersionMetadata> getBundleVersions(String bundleIdentifier);
+
+ BundleVersion getBundleVersion(String bundleId, String version);
+
+ StreamingContent getBundleVersionContent(String bundleId, String version);
+
+ BundleVersion deleteBundleVersion(String bundleId, String version);
+
+ // ---------------------- Extension methods ----------------------------------------------
+
+ SortedSet<ExtensionMetadata> getExtensionMetadata(ExtensionFilterParams filterParams);
+
+ SortedSet<ExtensionMetadata> getExtensionMetadata(ProvidedServiceAPI serviceAPI);
+
+ SortedSet<ExtensionMetadata> getExtensionMetadata(String bundleIdentifier, String version);
+
+ Extension getExtension(String bundleIdentifier, String version, String name);
+
+ StreamingOutput getExtensionDocs(String bundleIdentifier, String version, String name);
+
+ StreamingOutput getAdditionalDetailsDocs(String bundleIdentifier, String version, String name);
+
+ SortedSet<TagCount> getExtensionTags();
+
+ // ---------------------- Extension Repository methods ----------------------------------------------
+
+ SortedSet<ExtensionRepoBucket> getExtensionRepoBuckets(URI baseUri);
+
+ SortedSet<ExtensionRepoGroup> getExtensionRepoGroups(URI baseUri, String bucketName);
+
+ SortedSet<ExtensionRepoArtifact> getExtensionRepoArtifacts(URI baseUri, String bucketName, String groupId);
+
+ SortedSet<ExtensionRepoVersionSummary> getExtensionRepoVersions(URI baseUri, String bucketName, String groupId, String artifactId);
+
+ ExtensionRepoVersion getExtensionRepoVersion(URI baseUri, String bucketName, String groupId, String artifactId, String version);
+
+ StreamingContent getExtensionRepoVersionContent(String bucketName, String groupId, String artifactId, String version);
+
+ String getExtensionRepoVersionSha256(String bucketName, String groupId, String artifactId, String version);
+
+ List<ExtensionRepoExtensionMetadata> getExtensionRepoExtensions(URI baseUri, String bucketName, String groupId, String artifactId, String version);
+
+ Extension getExtensionRepoExtension(URI baseUri, String bucketName, String groupId, String artifactId, String version, String extensionName);
+
+ StreamingOutput getExtensionRepoExtensionDocs(URI baseUri, String bucketName, String groupId, String artifactId, String version, String extensionName);
+
+ StreamingOutput getExtensionRepoExtensionAdditionalDocs(URI baseUri, String bucketName, String groupId, String artifactId, String version, String extensionName);
+
+ // ---------------------- Field methods ---------------------------------------------
+
+ Set<String> getBucketFields();
+
+ Set<String> getBucketItemFields();
+
+ Set<String> getFlowFields();
+
+ // ---------------------- User methods ----------------------------------------------
+
+ User createUser(User user);
+
+ List<User> getUsers();
+
+ User getUser(String identifier);
+
+ User updateUser(User user);
+
+ User deleteUser(String identifier, RevisionInfo revisionInfo);
+
+ // ---------------------- User Group methods --------------------------------------
+
+ UserGroup createUserGroup(UserGroup userGroup);
+
+ List<UserGroup> getUserGroups();
+
+ UserGroup getUserGroup(String identifier);
+
+ UserGroup updateUserGroup(UserGroup userGroup);
+
+ UserGroup deleteUserGroup(String identifier, RevisionInfo revisionInfo);
+
+ // ---------------------- Access Policy methods ----------------------------------------
+
+ AccessPolicy createAccessPolicy(AccessPolicy accessPolicy);
+
+ AccessPolicy getAccessPolicy(String identifier);
+
+ AccessPolicy getAccessPolicy(String resource, RequestAction action);
+
+ List<AccessPolicy> getAccessPolicies();
+
+ AccessPolicy updateAccessPolicy(AccessPolicy accessPolicy);
+
+ AccessPolicy deleteAccessPolicy(String identifier, RevisionInfo revisionInfo);
+
+ List<Resource> getResources();
+
+ // ---------------------- Permission methods ----------------------------------------
+
+ CurrentUser getCurrentUser();
+
+ // ---------------------- Configuration methods ------------------------------------
+
+ RegistryConfiguration getRegistryConfiguration();
+
+}
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/service/StandardServiceFacade.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/service/StandardServiceFacade.java
new file mode 100644
index 0000000..7c908e4
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/service/StandardServiceFacade.java
@@ -0,0 +1,1180 @@
+/*
+ * 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.registry.web.service;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.registry.RegistryConfiguration;
+import org.apache.nifi.registry.authorization.AccessPolicy;
+import org.apache.nifi.registry.authorization.CurrentUser;
+import org.apache.nifi.registry.authorization.Resource;
+import org.apache.nifi.registry.authorization.User;
+import org.apache.nifi.registry.authorization.UserGroup;
+import org.apache.nifi.registry.bucket.Bucket;
+import org.apache.nifi.registry.bucket.BucketItem;
+import org.apache.nifi.registry.diff.VersionedFlowDifference;
+import org.apache.nifi.registry.extension.bundle.Bundle;
+import org.apache.nifi.registry.extension.bundle.BundleFilterParams;
+import org.apache.nifi.registry.extension.bundle.BundleType;
+import org.apache.nifi.registry.extension.bundle.BundleVersion;
+import org.apache.nifi.registry.extension.bundle.BundleVersionFilterParams;
+import org.apache.nifi.registry.extension.bundle.BundleVersionMetadata;
+import org.apache.nifi.registry.extension.component.ExtensionFilterParams;
+import org.apache.nifi.registry.extension.component.ExtensionMetadata;
+import org.apache.nifi.registry.extension.component.TagCount;
+import org.apache.nifi.registry.extension.component.manifest.Extension;
+import org.apache.nifi.registry.extension.component.manifest.ProvidedServiceAPI;
+import org.apache.nifi.registry.extension.repo.ExtensionRepoArtifact;
+import org.apache.nifi.registry.extension.repo.ExtensionRepoBucket;
+import org.apache.nifi.registry.extension.repo.ExtensionRepoExtensionMetadata;
+import org.apache.nifi.registry.extension.repo.ExtensionRepoGroup;
+import org.apache.nifi.registry.extension.repo.ExtensionRepoVersion;
+import org.apache.nifi.registry.extension.repo.ExtensionRepoVersionSummary;
+import org.apache.nifi.registry.flow.VersionedFlow;
+import org.apache.nifi.registry.flow.VersionedFlowSnapshot;
+import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata;
+import org.apache.nifi.registry.revision.api.InvalidRevisionException;
+import org.apache.nifi.registry.revision.entity.RevisableEntity;
+import org.apache.nifi.registry.revision.entity.RevisableEntityService;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
+import org.apache.nifi.registry.security.authorization.AuthorizableLookup;
+import org.apache.nifi.registry.security.authorization.RequestAction;
+import org.apache.nifi.registry.security.authorization.exception.AccessDeniedException;
+import org.apache.nifi.registry.security.authorization.resource.Authorizable;
+import org.apache.nifi.registry.security.authorization.resource.ResourceType;
+import org.apache.nifi.registry.security.authorization.user.NiFiUserUtils;
+import org.apache.nifi.registry.service.AuthorizationService;
+import org.apache.nifi.registry.service.RegistryService;
+import org.apache.nifi.registry.service.extension.ExtensionService;
+import org.apache.nifi.registry.web.link.LinkService;
+import org.apache.nifi.registry.web.security.PermissionsService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Isolation;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.ws.rs.core.Link;
+import javax.ws.rs.core.StreamingOutput;
+import javax.ws.rs.core.UriBuilder;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.UUID;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+/**
+ * A wrapper around the service layer that applies validation, authorization, and revision management to all services.
+ *
+ * All REST resources should access the service layer through this facade.
+ */
+@Service
+@Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Throwable.class)
+public class StandardServiceFacade implements ServiceFacade {
+
+ private static final String INVALID_REVISION_MSG = "The %s you attempted to %s with id '%s' is out of date with the server. " +
+ "You may need to refresh your client and try again.";
+
+ public static final String USER_GROUP_ENTITY_TYPE = "User Group";
+ public static final String USER_ENTITY_TYPE = "User";
+ public static final String ACCESS_POLICY_ENTITY_TYPE = "Access Policy";
+ public static final String VERSIONED_FLOW_ENTITY_TYPE = "Versioned Flow";
+ public static final String BUCKET_ENTITY_TYPE = "Bucket";
+
+ private final RegistryService registryService;
+ private final ExtensionService extensionService;
+ private final AuthorizationService authorizationService;
+ private final AuthorizableLookup authorizableLookup;
+ private final RevisableEntityService entityService;
+ private final PermissionsService permissionsService;
+ private final LinkService linkService;
+
+ @Autowired
+ public StandardServiceFacade(final RegistryService registryService,
+ final ExtensionService extensionService,
+ final AuthorizationService authorizationService,
+ final AuthorizableLookup authorizableLookup,
+ final RevisableEntityService entityService,
+ final PermissionsService permissionsService,
+ final LinkService linkService) {
+ this.registryService = registryService;
+ this.extensionService = extensionService;
+ this.authorizationService = authorizationService;
+ this.authorizableLookup = authorizableLookup;
+ this.entityService = entityService;
+ this.permissionsService = permissionsService;
+ this.linkService = linkService;
+ }
+
+ private String currentUserIdentity() {
+ return NiFiUserUtils.getNiFiUserIdentity();
+ }
+
+ // ---------------------- Bucket methods ----------------------------------------------
+
+ @Override
+ public Bucket createBucket(final Bucket bucket) {
+ authorizeBucketsAccess(RequestAction.WRITE);
+ validateCreationOfRevisableEntity(bucket, BUCKET_ENTITY_TYPE);
+
+ bucket.setIdentifier(UUID.randomUUID().toString());
+
+ final Bucket createdBucket = createRevisableEntity(bucket, BUCKET_ENTITY_TYPE, currentUserIdentity(),
+ () -> registryService.createBucket(bucket));
+ permissionsService.populateBucketPermissions(createdBucket);
+ linkService.populateLinks(createdBucket);
+ return createdBucket;
+ }
+
+ @Override
+ public Bucket getBucket(final String bucketIdentifier) {
+ authorizeBucketAccess(RequestAction.READ, bucketIdentifier);
+
+ final Bucket bucket = entityService.get(() -> registryService.getBucket(bucketIdentifier));
+ permissionsService.populateBucketPermissions(bucket);
+ linkService.populateLinks(bucket);
+ return bucket;
+ }
+
+ @Override
+ public List<Bucket> getBuckets() {
+ final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
+ if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
+ // not authorized for any bucket, return empty list of items
+ return Collections.emptyList();
+ }
+
+ final List<Bucket> buckets = entityService.getEntities(() -> registryService.getBuckets(authorizedBucketIds));
+ permissionsService.populateBucketPermissions(buckets);
+ linkService.populateLinks(buckets);
+ return buckets;
+ }
+
+ @Override
+ public Bucket updateBucket(final Bucket bucket) {
+ authorizeBucketAccess(RequestAction.WRITE, bucket.getIdentifier());
+ validateUpdateOfRevisableEntity(bucket, BUCKET_ENTITY_TYPE);
+
+ // verify outside of the revisable update so ResourceNotFoundException will be thrown instead of InvalidRevisionException
+ registryService.verifyBucketExists(bucket.getIdentifier());
+
+ final Bucket updatedBucket = updateRevisableEntity(bucket, BUCKET_ENTITY_TYPE, currentUserIdentity(),
+ () -> registryService.updateBucket(bucket));
+ permissionsService.populateBucketPermissions(updatedBucket);
+ linkService.populateLinks(updatedBucket);
+ return updatedBucket;
+ }
+
+ @Override
+ public Bucket deleteBucket(final String bucketIdentifier, final RevisionInfo revisionInfo) {
+ authorizeBucketAccess(RequestAction.DELETE, bucketIdentifier);
+ validateDeleteOfRevisableEntity(bucketIdentifier, revisionInfo, BUCKET_ENTITY_TYPE);
+
+ // verify outside of the revisable update so ResourceNotFoundException will be thrown instead of InvalidRevisionException
+ registryService.verifyBucketExists(bucketIdentifier);
+
+ return deleteRevisableEntity(bucketIdentifier, BUCKET_ENTITY_TYPE, revisionInfo,
+ () -> registryService.deleteBucket(bucketIdentifier));
+ }
+
+ // ---------------------- BucketItem methods ----------------------------------------------
+
+ @Override
+ public List<BucketItem> getBucketItems(final String bucketIdentifier) {
+ authorizeBucketAccess(RequestAction.READ, bucketIdentifier);
+
+ final List<BucketItem> items = registryService.getBucketItems(bucketIdentifier);
+ entityService.populateRevisions(items);
+ permissionsService.populateItemPermissions(items);
+ linkService.populateLinks(items);
+ return items;
+ }
+
+ @Override
+ public List<BucketItem> getBucketItems() {
+ final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
+ if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
+ // not authorized for any bucket, return empty list of items
+ return new ArrayList<>();
+ }
+
+ final List<BucketItem> items = registryService.getBucketItems(authorizedBucketIds);
+ entityService.populateRevisions(items);
+ permissionsService.populateItemPermissions(items);
+ linkService.populateLinks(items);
+ return items;
+ }
+
+ // ---------------------- Flow methods ----------------------------------------------
+
+ @Override
+ public VersionedFlow createFlow(final String bucketIdentifier, final VersionedFlow versionedFlow) {
+ authorizeBucketAccess(RequestAction.WRITE, bucketIdentifier);
+ validateCreationOfRevisableEntity(versionedFlow, VERSIONED_FLOW_ENTITY_TYPE);
+
+ versionedFlow.setIdentifier(UUID.randomUUID().toString());
+
+ final VersionedFlow createdFlow = createRevisableEntity(versionedFlow, VERSIONED_FLOW_ENTITY_TYPE, currentUserIdentity(),
+ () -> registryService.createFlow(bucketIdentifier, versionedFlow));
+ permissionsService.populateItemPermissions(createdFlow);
+ linkService.populateLinks(createdFlow);
+ return createdFlow;
+ }
+
+ @Override
+ public VersionedFlow getFlow(final String bucketIdentifier, final String flowIdentifier) {
+ authorizeBucketAccess(RequestAction.READ, bucketIdentifier);
+
+ final VersionedFlow flow = entityService.get(
+ () -> registryService.getFlow(bucketIdentifier, flowIdentifier));
+ permissionsService.populateItemPermissions(flow);
+ linkService.populateLinks(flow);
+ return flow;
+ }
+
+ @Override
+ public VersionedFlow getFlow(final String flowIdentifier) {
+ final VersionedFlow flow = entityService.get(() -> registryService.getFlow(flowIdentifier));
+ authorizeBucketAccess(RequestAction.READ, flow);
+
+ permissionsService.populateItemPermissions(flow);
+ linkService.populateLinks(flow);
+ return flow;
+ }
+
+ @Override
+ public List<VersionedFlow> getFlows(final String bucketIdentifier) {
+ authorizeBucketAccess(RequestAction.READ, bucketIdentifier);
+
+ final List<VersionedFlow> flows = entityService.getEntities(() -> registryService.getFlows(bucketIdentifier));
+ permissionsService.populateItemPermissions(flows);
+ linkService.populateLinks(flows);
+ return flows;
+ }
+
+ @Override
+ public VersionedFlow updateFlow(final VersionedFlow versionedFlow) {
+ authorizeBucketAccess(RequestAction.WRITE, versionedFlow);
+ validateUpdateOfRevisableEntity(versionedFlow, VERSIONED_FLOW_ENTITY_TYPE);
+
+ // verify outside of the revisable update so ResourceNotFoundException will be thrown instead of InvalidRevisionException
+ registryService.verifyFlowExists(versionedFlow.getIdentifier());
+
+ final VersionedFlow updatedFlow = updateRevisableEntity(versionedFlow, VERSIONED_FLOW_ENTITY_TYPE, currentUserIdentity(),
+ () -> registryService.updateFlow(versionedFlow));
+ permissionsService.populateItemPermissions(updatedFlow);
+ linkService.populateLinks(updatedFlow);
+ return updatedFlow;
+ }
+
+ @Override
+ public VersionedFlow deleteFlow(final String bucketIdentifier, final String flowIdentifier, final RevisionInfo revisionInfo) {
+ authorizeBucketAccess(RequestAction.DELETE, bucketIdentifier);
+ validateDeleteOfRevisableEntity(flowIdentifier, revisionInfo, VERSIONED_FLOW_ENTITY_TYPE);
+
+ // verify outside of the revisable update so ResourceNotFoundException will be thrown instead of InvalidRevisionException
+ registryService.verifyFlowExists(flowIdentifier);
+
+ return deleteRevisableEntity(flowIdentifier, VERSIONED_FLOW_ENTITY_TYPE, revisionInfo,
+ () -> registryService.deleteFlow(bucketIdentifier, flowIdentifier));
+ }
+
+ // ---------------------- Flow Snapshot methods ----------------------------------------------
+
+ @Override
+ public VersionedFlowSnapshot createFlowSnapshot(final VersionedFlowSnapshot flowSnapshot) {
+ authorizeBucketAccess(RequestAction.WRITE, flowSnapshot);
+
+ final VersionedFlowSnapshot createdSnapshot = registryService.createFlowSnapshot(flowSnapshot);
+ populateLinksAndPermissions(createdSnapshot);
+ return createdSnapshot;
+ }
+
+ @Override
+ public VersionedFlowSnapshot getFlowSnapshot(final String bucketIdentifier, final String flowIdentifier, final Integer version) {
+ authorizeBucketAccess(RequestAction.READ, bucketIdentifier);
+
+ final VersionedFlowSnapshot snapshot = registryService.getFlowSnapshot(bucketIdentifier, flowIdentifier, version);
+ populateLinksAndPermissions(snapshot);
+ return snapshot;
+ }
+
+ @Override
+ public VersionedFlowSnapshot getFlowSnapshot(final String flowIdentifier, final Integer version) {
+ final VersionedFlowSnapshotMetadata latestMetadata = registryService.getLatestFlowSnapshotMetadata(flowIdentifier);
+ authorizeBucketAccess(RequestAction.READ, latestMetadata);
+
+ final String bucketIdentifier = latestMetadata.getBucketIdentifier();
+ final VersionedFlowSnapshot snapshot = registryService.getFlowSnapshot(bucketIdentifier, flowIdentifier, version);
+ populateLinksAndPermissions(snapshot);
+ return snapshot;
+ }
+
+ @Override
+ public VersionedFlowSnapshot getLatestFlowSnapshot(final String bucketIdentifier, final String flowIdentifier) {
+ authorizeBucketAccess(RequestAction.READ, bucketIdentifier);
+
+ final VersionedFlowSnapshotMetadata latestMetadata = getLatestFlowSnapshotMetadata(bucketIdentifier, flowIdentifier);
+ final VersionedFlowSnapshot lastSnapshot = getFlowSnapshot(bucketIdentifier, flowIdentifier, latestMetadata.getVersion());
+ populateLinksAndPermissions(lastSnapshot);
+ return lastSnapshot;
+ }
+
+ @Override
+ public VersionedFlowSnapshot getLatestFlowSnapshot(final String flowIdentifier) {
+ final VersionedFlowSnapshotMetadata latestMetadata = registryService.getLatestFlowSnapshotMetadata(flowIdentifier);
+ authorizeBucketAccess(RequestAction.READ, latestMetadata);
+
+ final String bucketIdentifier = latestMetadata.getBucketIdentifier();
+ final Integer latestVersion = latestMetadata.getVersion();
+
+ final VersionedFlowSnapshot lastSnapshot = registryService.getFlowSnapshot(bucketIdentifier, flowIdentifier, latestVersion);
+ populateLinksAndPermissions(lastSnapshot);
+ return lastSnapshot;
+ }
+
+ @Override
+ public SortedSet<VersionedFlowSnapshotMetadata> getFlowSnapshots(final String bucketIdentifier, final String flowIdentifier) {
+ authorizeBucketAccess(RequestAction.READ, bucketIdentifier);
+
+ final SortedSet<VersionedFlowSnapshotMetadata> snapshots = registryService.getFlowSnapshots(bucketIdentifier, flowIdentifier);
+ linkService.populateLinks(snapshots);
+ return snapshots;
+ }
+
+ @Override
+ public SortedSet<VersionedFlowSnapshotMetadata> getFlowSnapshots(final String flowIdentifier) {
+ final VersionedFlow flow = registryService.getFlow(flowIdentifier);
+ authorizeBucketAccess(RequestAction.READ, flow);
+
+ final String bucketIdentifier = flow.getBucketIdentifier();
+ final SortedSet<VersionedFlowSnapshotMetadata> snapshots = registryService.getFlowSnapshots(bucketIdentifier, flowIdentifier);
+ linkService.populateLinks(snapshots);
+ return snapshots;
+ }
+
+ @Override
+ public VersionedFlowSnapshotMetadata getLatestFlowSnapshotMetadata(final String bucketIdentifier, final String flowIdentifier) {
+ authorizeBucketAccess(RequestAction.READ, bucketIdentifier);
+
+ final VersionedFlowSnapshotMetadata latest = registryService.getLatestFlowSnapshotMetadata(bucketIdentifier, flowIdentifier);
+ linkService.populateLinks(latest);
+ return latest;
+ }
+
+ @Override
+ public VersionedFlowSnapshotMetadata getLatestFlowSnapshotMetadata(final String flowIdentifier) {
+ final VersionedFlowSnapshotMetadata latest = registryService.getLatestFlowSnapshotMetadata(flowIdentifier);
+ authorizeBucketAccess(RequestAction.READ, latest);
+
+ linkService.populateLinks(latest);
+ return latest;
+ }
+
+ @Override
+ public VersionedFlowDifference getFlowDiff(final String bucketIdentifier, final String flowIdentifier, final Integer versionA, final Integer versionB) {
+ authorizeBucketAccess(RequestAction.READ, bucketIdentifier);
+ return registryService.getFlowDiff(bucketIdentifier, flowIdentifier, versionA, versionB);
+ }
+
+ private void populateLinksAndPermissions(final VersionedFlowSnapshot snapshot) {
+ if (snapshot == null) {
+ return;
+ }
+
+ if (snapshot.getSnapshotMetadata() != null) {
+ linkService.populateLinks(snapshot.getSnapshotMetadata());
+ }
+
+ if (snapshot.getFlow() != null) {
+ linkService.populateLinks(snapshot.getFlow());
+ }
+
+ if (snapshot.getBucket() != null) {
+ permissionsService.populateBucketPermissions(snapshot.getBucket());
+ linkService.populateLinks(snapshot.getBucket());
+ }
+ }
+
+ // ---------------------- Bundle methods ----------------------------------------------
+
+ @Override
+ public List<Bundle> getBundles(final BundleFilterParams filterParams) {
+ final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
+ if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
+ // not authorized for any bucket, return empty list of items
+ return new ArrayList<>();
+ }
+
+ final List<Bundle> bundles = extensionService.getBundles(authorizedBucketIds, filterParams);
+ permissionsService.populateItemPermissions(bundles);
+ linkService.populateLinks(bundles);
+ return bundles;
+ }
+
+ @Override
+ public List<Bundle> getBundlesByBucket(final String bucketIdentifier) {
+ authorizeBucketAccess(RequestAction.READ, bucketIdentifier);
+
+ final List<Bundle> bundles = extensionService.getBundlesByBucket(bucketIdentifier);
+ permissionsService.populateItemPermissions(bundles);
+ linkService.populateLinks(bundles);
+ return bundles;
+ }
+
+ @Override
+ public Bundle getBundle(final String bundleIdentifier) {
+ final Bundle bundle = extensionService.getBundle(bundleIdentifier);
+ authorizeBucketAccess(RequestAction.READ, bundle);
+
+ permissionsService.populateItemPermissions(bundle);
+ linkService.populateLinks(bundle);
+ return bundle;
+ }
+
+ @Override
+ public Bundle deleteBundle(final String bundleIdentifier) {
+ final Bundle bundle = extensionService.getBundle(bundleIdentifier);
+ authorizeBucketAccess(RequestAction.READ, bundle);
+ authorizeBucketAccess(RequestAction.DELETE, bundle);
+
+ final Bundle deletedBundle = extensionService.deleteBundle(bundle);
+ permissionsService.populateItemPermissions(deletedBundle);
+ linkService.populateLinks(deletedBundle);
+ return deletedBundle;
+ }
+
+ // ---------------------- Bundle Version methods ----------------------------------------------
+
+ @Override
+ public BundleVersion createBundleVersion(final String bucketIdentifier, final BundleType bundleType,
+ final InputStream inputStream, final String clientSha256) throws IOException {
+
+ authorizeBucketAccess(RequestAction.WRITE, bucketIdentifier);
+
+ final BundleVersion createdBundleVersion = extensionService.createBundleVersion(
+ bucketIdentifier, bundleType, inputStream, clientSha256);
+
+ linkService.populateLinks(createdBundleVersion.getVersionMetadata());
+ linkService.populateLinks(createdBundleVersion.getBundle());
+ linkService.populateLinks(createdBundleVersion.getBucket());
+
+ permissionsService.populateItemPermissions(createdBundleVersion.getBundle());
+
+ return createdBundleVersion;
+ }
+
+ @Override
+ public SortedSet<BundleVersionMetadata> getBundleVersions(final BundleVersionFilterParams filterParams) {
+ final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
+ if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
+ // not authorized for any bucket, return empty list of items
+ return Collections.emptySortedSet();
+ }
+
+ final SortedSet<BundleVersionMetadata> bundleVersions = extensionService.getBundleVersions(authorizedBucketIds, filterParams);
+ linkService.populateLinks(bundleVersions);
+ return bundleVersions;
+ }
+
+ @Override
+ public SortedSet<BundleVersionMetadata> getBundleVersions(final String bundleIdentifier) {
+ final Bundle bundle = extensionService.getBundle(bundleIdentifier);
+ authorizeBucketAccess(RequestAction.READ, bundle);
+
+ final SortedSet<BundleVersionMetadata> bundleVersions = extensionService.getBundleVersions(bundleIdentifier);
+ linkService.populateLinks(bundleVersions);
+ return bundleVersions;
+ }
+
+ @Override
+ public BundleVersion getBundleVersion(final String bundleIdentifier, final String version) {
+ final Bundle bundle = extensionService.getBundle(bundleIdentifier);
+ authorizeBucketAccess(RequestAction.READ, bundle);
+
+ final String bucketIdentifier = bundle.getBucketIdentifier();
+ final BundleVersion bundleVersion = extensionService.getBundleVersion(bucketIdentifier, bundleIdentifier, version);
+ linkService.populateLinks(bundleVersion);
+ return bundleVersion;
+ }
+
+ @Override
+ public StreamingContent getBundleVersionContent(final String bundleIdentifier, final String version) {
+ final Bundle bundle = extensionService.getBundle(bundleIdentifier);
+ authorizeBucketAccess(RequestAction.READ, bundle);
+
+ final String bucketIdentifier = bundle.getBucketIdentifier();
+ final BundleVersion bundleVersion = extensionService.getBundleVersion(bucketIdentifier, bundleIdentifier, version);
+
+ final StreamingOutput streamingOutput = (output) -> extensionService.writeBundleVersionContent(bundleVersion, output);
+ return new StreamingContent(streamingOutput, bundleVersion.getFilename());
+ }
+
+ @Override
+ public BundleVersion deleteBundleVersion(final String bundleIdentifier, final String version) {
+ final Bundle bundle = extensionService.getBundle(bundleIdentifier);
+ authorizeBucketAccess(RequestAction.READ, bundle);
+ authorizeBucketAccess(RequestAction.DELETE, bundle);
+
+ final String bucketIdentifier = bundle.getBucketIdentifier();
+ final BundleVersion bundleVersion = extensionService.getBundleVersion(bucketIdentifier, bundleIdentifier, version);
+
+ final BundleVersion deletedBundleVersion = extensionService.deleteBundleVersion(bundleVersion);
+ linkService.populateLinks(deletedBundleVersion);
+ return deletedBundleVersion;
+ }
+
+ // ---------------------- Extension methods ----------------------------------------------
+
+ @Override
+ public SortedSet<ExtensionMetadata> getExtensionMetadata(final ExtensionFilterParams filterParams) {
+ final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
+ if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
+ // not authorized for any bucket, return empty list of items
+ return Collections.emptySortedSet();
+ }
+
+ final SortedSet<ExtensionMetadata> metadata = extensionService.getExtensionMetadata(authorizedBucketIds, filterParams);
+ linkService.populateLinks(metadata);
+ return metadata;
+ }
+
+ @Override
+ public SortedSet<ExtensionMetadata> getExtensionMetadata(final ProvidedServiceAPI serviceAPI) {
+ final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
+ if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
+ // not authorized for any bucket, return empty list of items
+ return Collections.emptySortedSet();
+ }
+
+ final SortedSet<ExtensionMetadata> metadata = extensionService.getExtensionMetadata(authorizedBucketIds, serviceAPI);
+ linkService.populateLinks(metadata);
+ return metadata;
+ }
+
+ @Override
+ public SortedSet<ExtensionMetadata> getExtensionMetadata(final String bundleIdentifier, final String version) {
+ final Bundle bundle = extensionService.getBundle(bundleIdentifier);
+ authorizeBucketAccess(RequestAction.READ, bundle);
+
+ final String bucketIdentifier = bundle.getBucketIdentifier();
+ final BundleVersion bundleVersion = extensionService.getBundleVersion(bucketIdentifier, bundleIdentifier, version);
+
+ final SortedSet<ExtensionMetadata> extensions = extensionService.getExtensionMetadata(bundleVersion);
+ linkService.populateLinks(extensions);
+ return extensions;
+ }
+
+ @Override
+ public Extension getExtension(final String bundleIdentifier, final String version, final String name) {
+ final Bundle bundle = extensionService.getBundle(bundleIdentifier);
+ authorizeBucketAccess(RequestAction.READ, bundle);
+
+ final String bucketIdentifier = bundle.getBucketIdentifier();
+ final BundleVersion bundleVersion = extensionService.getBundleVersion(bucketIdentifier, bundleIdentifier, version);
+ return extensionService.getExtension(bundleVersion, name);
+ }
+
+ @Override
+ public StreamingOutput getExtensionDocs(final String bundleIdentifier, final String version, final String name) {
+ final Bundle bundle = extensionService.getBundle(bundleIdentifier);
+ authorizeBucketAccess(RequestAction.READ, bundle);
+
+ final String bucketIdentifier = bundle.getBucketIdentifier();
+ final BundleVersion bundleVersion = extensionService.getBundleVersion(bucketIdentifier, bundleIdentifier, version);
+
+ final StreamingOutput streamingOutput = (output) -> extensionService.writeExtensionDocs(bundleVersion, name, output);
+ return streamingOutput;
+ }
+
+ @Override
+ public StreamingOutput getAdditionalDetailsDocs(final String bundleIdentifier, final String version, final String name) {
+ final Bundle bundle = extensionService.getBundle(bundleIdentifier);
+ authorizeBucketAccess(RequestAction.READ, bundle);
+
+ final String bucketIdentifier = bundle.getBucketIdentifier();
+ final BundleVersion bundleVersion = extensionService.getBundleVersion(bucketIdentifier, bundleIdentifier, version);
+
+ final StreamingOutput streamingOutput = (output) -> extensionService.writeAdditionalDetailsDocs(bundleVersion, name, output);
+ return streamingOutput;
+ }
+
+ @Override
+ public SortedSet<TagCount> getExtensionTags() {
+ return extensionService.getExtensionTags();
+ }
+
+ // ---------------------- Extension Repository methods ----------------------------------------------
+
+ @Override
+ public SortedSet<ExtensionRepoBucket> getExtensionRepoBuckets(final URI baseUri) {
+ final Set<String> authorizedBucketIds = getAuthorizedBucketIds(RequestAction.READ);
+ if (authorizedBucketIds == null || authorizedBucketIds.isEmpty()) {
+ // not authorized for any bucket, return empty list of items
+ return Collections.emptySortedSet();
+ }
+
+ final SortedSet<ExtensionRepoBucket> repoBuckets = extensionService.getExtensionRepoBuckets(authorizedBucketIds);
+ linkService.populateFullLinks(repoBuckets, baseUri);
+ return repoBuckets;
+ }
+
+ @Override
+ public SortedSet<ExtensionRepoGroup> getExtensionRepoGroups(final URI baseUri, final String bucketName) {
+ final Bucket bucket = registryService.getBucketByName(bucketName);
+ authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
+
+ final SortedSet<ExtensionRepoGroup> repoGroups = extensionService.getExtensionRepoGroups(bucket);
+ linkService.populateFullLinks(repoGroups, baseUri);
+ return extensionService.getExtensionRepoGroups(bucket);
+ }
+
+ @Override
+ public SortedSet<ExtensionRepoArtifact> getExtensionRepoArtifacts(final URI baseUri, final String bucketName, final String groupId) {
+ final Bucket bucket = registryService.getBucketByName(bucketName);
+ authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
+
+ final SortedSet<ExtensionRepoArtifact> repoArtifacts = extensionService.getExtensionRepoArtifacts(bucket, groupId);
+ linkService.populateFullLinks(repoArtifacts, baseUri);
+ return repoArtifacts;
+ }
+
+ @Override
+ public SortedSet<ExtensionRepoVersionSummary> getExtensionRepoVersions(final URI baseUri, final String bucketName, final String groupId,
+ final String artifactId) {
+ final Bucket bucket = registryService.getBucketByName(bucketName);
+ authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
+
+ final SortedSet<ExtensionRepoVersionSummary> repoVersions = extensionService.getExtensionRepoVersions(bucket, groupId, artifactId);
+ linkService.populateFullLinks(repoVersions, baseUri);
+ return repoVersions;
+ }
+
+ @Override
+ public ExtensionRepoVersion getExtensionRepoVersion(final URI baseUri, final String bucketName, final String groupId,
+ final String artifactId, final String version) {
+ final Bucket bucket = registryService.getBucketByName(bucketName);
+ authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
+
+ final BundleVersion bundleVersion = extensionService.getBundleVersion(bucket.getIdentifier(), groupId, artifactId, version);
+
+ final String extensionsUri = generateResourceUri(baseUri,
+ "extension-repository",
+ bundleVersion.getBucket().getName(),
+ bundleVersion.getBundle().getGroupId(),
+ bundleVersion.getBundle().getArtifactId(),
+ bundleVersion.getVersionMetadata().getVersion(),
+ "extensions");
+
+ final String downloadUri = generateResourceUri(baseUri,
+ "extension-repository",
+ bundleVersion.getBucket().getName(),
+ bundleVersion.getBundle().getGroupId(),
+ bundleVersion.getBundle().getArtifactId(),
+ bundleVersion.getVersionMetadata().getVersion(),
+ "content");
+
+ final String sha256Uri = generateResourceUri(baseUri,
+ "extension-repository",
+ bundleVersion.getBucket().getName(),
+ bundleVersion.getBundle().getGroupId(),
+ bundleVersion.getBundle().getArtifactId(),
+ bundleVersion.getVersionMetadata().getVersion(),
+ "sha256");
+
+ final ExtensionRepoVersion repoVersion = new ExtensionRepoVersion();
+ repoVersion.setExtensionsLink(Link.fromUri(extensionsUri).rel("extensions").build());
+ repoVersion.setDownloadLink(Link.fromUri(downloadUri).rel("content").build());
+ repoVersion.setSha256Link(Link.fromUri(sha256Uri).rel("sha256").build());
+ repoVersion.setSha256Supplied(bundleVersion.getVersionMetadata().getSha256Supplied());
+ return repoVersion;
+ }
+
+ @Override
+ public StreamingContent getExtensionRepoVersionContent(final String bucketName, final String groupId, final String artifactId,
+ final String version) {
+ final Bucket bucket = registryService.getBucketByName(bucketName);
+ authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
+
+ final BundleVersion bundleVersion = extensionService.getBundleVersion(bucket.getIdentifier(), groupId, artifactId, version);
+ final StreamingOutput streamingOutput = (output) -> extensionService.writeBundleVersionContent(bundleVersion, output);
+ return new StreamingContent(streamingOutput, bundleVersion.getFilename());
+ }
+
+ @Override
+ public String getExtensionRepoVersionSha256(final String bucketName, final String groupId, final String artifactId,
+ final String version) {
+ final Bucket bucket = registryService.getBucketByName(bucketName);
+ authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
+
+ final BundleVersion bundleVersion = extensionService.getBundleVersion(bucket.getIdentifier(), groupId, artifactId, version);
+ final String sha256Hex = bundleVersion.getVersionMetadata().getSha256();
+ return sha256Hex;
+ }
+
+ @Override
+ public List<ExtensionRepoExtensionMetadata> getExtensionRepoExtensions(final URI baseUri, final String bucketName, final String groupId,
+ final String artifactId, final String version) {
+ final Bucket bucket = registryService.getBucketByName(bucketName);
+ authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
+
+ final BundleVersion bundleVersion = extensionService.getBundleVersion(bucket.getIdentifier(), groupId, artifactId, version);
+ final SortedSet<ExtensionMetadata> extensions = extensionService.getExtensionMetadata(bundleVersion);
+
+ final List<ExtensionRepoExtensionMetadata> extensionRepoExtensions = new ArrayList<>(extensions.size());
+ extensions.forEach(e -> extensionRepoExtensions.add(new ExtensionRepoExtensionMetadata(e)));
+ linkService.populateFullLinks(extensionRepoExtensions, baseUri);
+ return extensionRepoExtensions;
+ }
+
+ @Override
+ public Extension getExtensionRepoExtension(final URI baseUri, final String bucketName, final String groupId,
+ final String artifactId, final String version, final String extensionName) {
+ final Bucket bucket = registryService.getBucketByName(bucketName);
+ authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
+
+ final BundleVersion bundleVersion = extensionService.getBundleVersion(bucket.getIdentifier(), groupId, artifactId, version);
+ final Extension extension = extensionService.getExtension(bundleVersion, extensionName);
+ return extension;
+ }
+
+ @Override
+ public StreamingOutput getExtensionRepoExtensionDocs(final URI baseUri, final String bucketName, final String groupId,
+ final String artifactId, final String version, final String extensionName) {
+ final Bucket bucket = registryService.getBucketByName(bucketName);
+ authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
+
+ final BundleVersion bundleVersion = extensionService.getBundleVersion(bucket.getIdentifier(), groupId, artifactId, version);
+ final StreamingOutput streamingOutput = (output) -> extensionService.writeExtensionDocs(bundleVersion, extensionName, output);
+ return streamingOutput;
+ }
+
+ @Override
+ public StreamingOutput getExtensionRepoExtensionAdditionalDocs(final URI baseUri, final String bucketName, final String groupId,
+ final String artifactId, final String version, final String extensionName) {
+ final Bucket bucket = registryService.getBucketByName(bucketName);
+ authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier());
+
+ final BundleVersion bundleVersion = extensionService.getBundleVersion(bucket.getIdentifier(), groupId, artifactId, version);
+ final StreamingOutput streamingOutput = (output) -> extensionService.writeAdditionalDetailsDocs(bundleVersion, extensionName, output);
+ return streamingOutput;
+ }
+
+
+ // ---------------------- Field methods ----------------------------------------------
+
+ @Override
+ public Set<String> getBucketFields() {
+ return registryService.getBucketFields();
+ }
+
+ @Override
+ public Set<String> getBucketItemFields() {
+ return registryService.getBucketItemFields();
+ }
+
+ @Override
+ public Set<String> getFlowFields() {
+ return registryService.getFlowFields();
+ }
+
+ // ---------------------- User methods ----------------------------------------------
+
+ @Override
+ public User createUser(final User user) {
+ verifyAuthorizerSupportsConfigurableUserGroups();
+ authorizeTenantsAccess(RequestAction.WRITE);
+ validateCreationOfRevisableEntity(user, USER_ENTITY_TYPE);
+
+ user.setIdentifier(UUID.randomUUID().toString());
+ return createRevisableEntity(user, USER_ENTITY_TYPE, currentUserIdentity(), () -> authorizationService.createUser(user));
+ }
+
+ @Override
+ public List<User> getUsers() {
+ verifyAuthorizerIsManaged();
+ authorizeTenantsAccess(RequestAction.READ);
+ return entityService.getEntities(() -> authorizationService.getUsers());
+ }
+
+ @Override
+ public User getUser(final String identifier) {
+ verifyAuthorizerIsManaged();
+ authorizeTenantsAccess(RequestAction.READ);
+ return entityService.get(() -> authorizationService.getUser(identifier));
+ }
+
+ @Override
+ public User updateUser(final User user) {
+ verifyAuthorizerSupportsConfigurableUserGroups();
+ authorizeTenantsAccess(RequestAction.WRITE);
+ validateUpdateOfRevisableEntity(user, USER_ENTITY_TYPE);
+
+ // verify outside of the revisable update so ResourceNotFoundException will be thrown instead of InvalidRevisionException
+ authorizationService.verifyUserExists(user.getIdentifier());
+
+ return updateRevisableEntity(user, USER_ENTITY_TYPE, currentUserIdentity(), () -> authorizationService.updateUser(user));
+ }
+
+ @Override
+ public User deleteUser(final String identifier, final RevisionInfo revisionInfo) {
+ verifyAuthorizerSupportsConfigurableUserGroups();
+ authorizeTenantsAccess(RequestAction.DELETE);
+ validateDeleteOfRevisableEntity(identifier, revisionInfo, USER_ENTITY_TYPE);
+
+ // verify outside of the revisable update so ResourceNotFoundException will be thrown instead of InvalidRevisionException
+ authorizationService.verifyUserExists(identifier);
+
+ return deleteRevisableEntity(identifier, USER_ENTITY_TYPE, revisionInfo, () -> authorizationService.deleteUser(identifier));
+ }
+
+ // ---------------------- UserGroup methods ----------------------------------------------
+
+ @Override
+ public UserGroup createUserGroup(final UserGroup userGroup) {
+ verifyAuthorizerSupportsConfigurableUserGroups();
+ authorizeTenantsAccess(RequestAction.WRITE);
+ validateCreationOfRevisableEntity(userGroup, USER_GROUP_ENTITY_TYPE);
+
+ userGroup.setIdentifier(UUID.randomUUID().toString());
+ return createRevisableEntity(userGroup, USER_GROUP_ENTITY_TYPE, currentUserIdentity(),
+ () -> authorizationService.createUserGroup(userGroup));
+ }
+
+ @Override
+ public List<UserGroup> getUserGroups() {
+ verifyAuthorizerIsManaged();
+ authorizeTenantsAccess(RequestAction.READ);
+ return entityService.getEntities(() -> authorizationService.getUserGroups());
+ }
+
+ @Override
+ public UserGroup getUserGroup(final String identifier) {
+ verifyAuthorizerIsManaged();
+ authorizeTenantsAccess(RequestAction.READ);
+ return entityService.get(() -> authorizationService.getUserGroup(identifier));
+ }
+
+ @Override
+ public UserGroup updateUserGroup(final UserGroup userGroup) {
+ verifyAuthorizerSupportsConfigurableUserGroups();
+ authorizeTenantsAccess(RequestAction.WRITE);
+ validateUpdateOfRevisableEntity(userGroup, USER_GROUP_ENTITY_TYPE);
+
+ // verify outside of the revisable update so ResourceNotFoundException will be thrown instead of InvalidRevisionException
+ authorizationService.verifyUserGroupExists(userGroup.getIdentifier());
+
+ return updateRevisableEntity(userGroup, USER_GROUP_ENTITY_TYPE, currentUserIdentity(),
+ () -> authorizationService.updateUserGroup(userGroup));
+ }
+
+ @Override
+ public UserGroup deleteUserGroup(final String identifier, final RevisionInfo revisionInfo) {
+ verifyAuthorizerSupportsConfigurableUserGroups();
+ authorizeTenantsAccess(RequestAction.DELETE);
+ validateDeleteOfRevisableEntity(identifier, revisionInfo, USER_GROUP_ENTITY_TYPE);
+
+ // verify outside of the revisable update so ResourceNotFoundException will be thrown instead of InvalidRevisionException
+ authorizationService.verifyUserGroupExists(identifier);
+
+ return deleteRevisableEntity(identifier, USER_GROUP_ENTITY_TYPE, revisionInfo,
+ () -> authorizationService.deleteUserGroup(identifier));
+ }
+
+ // ---------------------- AccessPolicy methods ----------------------------------------------
+
+ @Override
+ public AccessPolicy createAccessPolicy(final AccessPolicy accessPolicy) {
+ verifyAuthorizerSupportsConfigurablePolicies();
+ authorizePoliciesAccess(RequestAction.WRITE);
+ validateCreationOfRevisableEntity(accessPolicy, ACCESS_POLICY_ENTITY_TYPE);
+
+ accessPolicy.setIdentifier(UUID.randomUUID().toString());
+ return createRevisableEntity(accessPolicy, ACCESS_POLICY_ENTITY_TYPE, currentUserIdentity(),
+ () -> authorizationService.createAccessPolicy(accessPolicy));
+ }
+
+ @Override
+ public AccessPolicy getAccessPolicy(final String identifier) {
+ verifyAuthorizerIsManaged();
+ authorizePoliciesAccess(RequestAction.READ);
+ return entityService.get(() -> authorizationService.getAccessPolicy(identifier));
+ }
+
+ @Override
+ public AccessPolicy getAccessPolicy(final String resource, final RequestAction action) {
+ verifyAuthorizerIsManaged();
+ authorizePoliciesAccess(RequestAction.READ);
+ return entityService.get(() -> authorizationService.getAccessPolicy(resource, action));
+ }
+
+ @Override
+ public List<AccessPolicy> getAccessPolicies() {
+ verifyAuthorizerIsManaged();
+ authorizePoliciesAccess(RequestAction.READ);
+ return entityService.getEntities(() -> authorizationService.getAccessPolicies());
+ }
+
+ @Override
+ public AccessPolicy updateAccessPolicy(final AccessPolicy accessPolicy) {
+ verifyAuthorizerSupportsConfigurablePolicies();
+ authorizePoliciesAccess(RequestAction.WRITE);
+ validateUpdateOfRevisableEntity(accessPolicy, ACCESS_POLICY_ENTITY_TYPE);
+
+ // verify outside of the revisable update so ResourceNotFoundException will be thrown instead of InvalidRevisionException
+ authorizationService.verifyAccessPolicyExists(accessPolicy.getIdentifier());
+
+ return updateRevisableEntity(accessPolicy, ACCESS_POLICY_ENTITY_TYPE, currentUserIdentity(),
+ () -> authorizationService.updateAccessPolicy(accessPolicy));
+ }
+
+ @Override
+ public AccessPolicy deleteAccessPolicy(final String identifier, final RevisionInfo revisionInfo) {
+ verifyAuthorizerSupportsConfigurablePolicies();
+ authorizePoliciesAccess(RequestAction.DELETE);
+ validateDeleteOfRevisableEntity(identifier, revisionInfo, ACCESS_POLICY_ENTITY_TYPE);
+
+ // verify outside of the revisable update so ResourceNotFoundException will be thrown instead of InvalidRevisionException
+ authorizationService.verifyAccessPolicyExists(identifier);
+
+ return deleteRevisableEntity(identifier, ACCESS_POLICY_ENTITY_TYPE, revisionInfo,
+ () -> authorizationService.deleteAccessPolicy(identifier));
+ }
+
+ @Override
+ public List<Resource> getResources() {
+ authorizePoliciesAccess(RequestAction.READ);
+ return authorizationService.getResources();
+ }
+
+ // ---------------------- Permission methods -----------------------------
+
+ @Override
+ public CurrentUser getCurrentUser() {
+ return authorizationService.getCurrentUser();
+ }
+
+
+ // ---------------------- Authorization methods -----------------------------
+
+ private void verifyAuthorizerIsManaged() {
+ authorizationService.verifyAuthorizerIsManaged();
+ }
+
+ private void verifyAuthorizerSupportsConfigurablePolicies() {
+ authorizationService.verifyAuthorizerSupportsConfigurablePolicies();
+ }
+
+ private void verifyAuthorizerSupportsConfigurableUserGroups() {
+ authorizationService.verifyAuthorizerSupportsConfigurableUserGroups();
+ }
+
+ // ---------------------- Configuration methods -----------------------------
+
+ @Override
+ public RegistryConfiguration getRegistryConfiguration() {
+ final RegistryConfiguration config = new RegistryConfiguration();
+
+ boolean hasAnyConfigurationAccess = false;
+ AccessDeniedException lastAccessDeniedException = null;
+ try {
+ final Authorizable policyAuthorizer = authorizableLookup.getPoliciesAuthorizable();
+ authorizationService.authorize(policyAuthorizer, RequestAction.READ);
+ config.setSupportsManagedAuthorizer(authorizationService.isManagedAuthorizer());
+ config.setSupportsConfigurableAuthorizer(authorizationService.isConfigurableAccessPolicyProvider());
+ hasAnyConfigurationAccess = true;
+ } catch (AccessDeniedException e) {
+ lastAccessDeniedException = e;
+ }
+
+ try {
+ authorizationService.authorize(authorizableLookup.getTenantsAuthorizable(), RequestAction.READ);
+ config.setSupportsConfigurableUsersAndGroups(authorizationService.isConfigurableUserGroupProvider());
+ hasAnyConfigurationAccess = true;
+ } catch (AccessDeniedException e) {
+ lastAccessDeniedException = e;
+ }
+
+ if (!hasAnyConfigurationAccess) {
+ // If the user doesn't have access to any configuration, then throw the exception.
+ // Otherwise, return what they can access.
+ throw lastAccessDeniedException;
+ }
+
+ return config;
+ }
+
+ // ---------------------- Helper methods -------------------------------------
+
+ private void authorizeBucketsAccess(RequestAction actionType) throws AccessDeniedException {
+ final Authorizable bucketsAuthorizable = authorizableLookup.getBucketsAuthorizable();
+ authorizationService.authorize(bucketsAuthorizable, actionType);
+ }
+
+ private void authorizeBucketAccess(final RequestAction actionType, final String bucketIdentifier) {
+ if (StringUtils.isBlank(bucketIdentifier)) {
+ throw new IllegalArgumentException("Unable to authorize access because bucket identifier is null or blank");
+ }
+
+ final Authorizable bucketAuthorizable = authorizableLookup.getBucketAuthorizable(bucketIdentifier);
+ authorizationService.authorize(bucketAuthorizable, actionType);
+ }
+
+ private void authorizeBucketAccess(final RequestAction action, final Bundle bundle) {
+ // this should never happen, but if somehow the back-end didn't populate the bucket id let's make sure the bundle isn't returned
+ if (bundle == null) {
+ throw new IllegalStateException("Unable to authorize access because bucket identifier is null or blank");
+ }
+
+ authorizeBucketAccess(action, bundle.getBucketIdentifier());
+ }
+
+ private void authorizeBucketAccess(final RequestAction action, final VersionedFlow flow) {
+ // this should never happen, but if somehow the back-end didn't populate the bucket id let's make sure the flow isn't returned
+ if (flow == null) {
+ throw new IllegalStateException("Unable to authorize access because bucket identifier is null or blank");
+ }
+
+ authorizeBucketAccess(action, flow.getBucketIdentifier());
+ }
+
+ private void authorizeBucketAccess(final RequestAction action, final VersionedFlowSnapshot flowSnapshot) {
+ // this should never happen, but if somehow the back-end didn't populate the bucket id let's make sure the flow snapshot isn't returned
+ if (flowSnapshot == null || flowSnapshot.getSnapshotMetadata() == null) {
+ throw new IllegalStateException("Unable to authorize access because bucket identifier is null or blank");
+ }
+
+ authorizeBucketAccess(action, flowSnapshot.getSnapshotMetadata().getBucketIdentifier());
+ }
+
+ private void authorizeBucketAccess(final RequestAction action, final VersionedFlowSnapshotMetadata flowSnapshotMetadata) {
+ // this should never happen, but if somehow the back-end didn't populate the bucket id let's make sure the flow snapshot isn't returned
+ if (flowSnapshotMetadata == null) {
+ throw new IllegalStateException("Unable to authorize access because bucket identifier is null or blank");
+ }
+
+ authorizeBucketAccess(action, flowSnapshotMetadata.getBucketIdentifier());
+ }
+
+ private Set<String> getAuthorizedBucketIds(final RequestAction actionType) {
+ return authorizationService.getAuthorizedResources(actionType, ResourceType.Bucket)
+ .stream()
+ .map(StandardServiceFacade::extractBucketIdFromResource)
+ .filter(Objects::nonNull)
+ .distinct()
+ .collect(Collectors.toSet());
+ }
+
+ private static String extractBucketIdFromResource(Resource resource) {
+ if (resource == null || resource.getIdentifier() == null || !resource.getIdentifier().startsWith("/buckets/")) {
+ return null;
+ }
+
+ final String[] pathComponents = resource.getIdentifier().split("/");
+ if (pathComponents.length < 3) {
+ return null;
+ }
+ return pathComponents[2];
+ }
+
+ private String generateResourceUri(final URI baseUri, final String... path) {
+ final URI fullUri = UriBuilder.fromUri(baseUri).segment(path).build();
+ return fullUri.toString();
+ }
+
+ private void authorizePoliciesAccess(final RequestAction actionType) {
+ final Authorizable policiesAuthorizable = authorizableLookup.getPoliciesAuthorizable();
+ authorizationService.authorize(policiesAuthorizable, actionType);
+ }
+
+ private void authorizeTenantsAccess(RequestAction actionType) {
+ final Authorizable tenantsAuthorizable = authorizableLookup.getTenantsAuthorizable();
+ authorizationService.authorize(tenantsAuthorizable, actionType);
+ }
+
+ private void validateCreationOfRevisableEntity(final RevisableEntity entity, final String entityTypeName) {
+ if (entity == null) {
+ throw new IllegalArgumentException(entityTypeName + " cannot be null");
+ }
+ if (entity.getIdentifier() != null) {
+ throw new IllegalArgumentException(entityTypeName + " identifier cannot be specified when creating a new "
+ + entityTypeName.toLowerCase() + ".");
+ }
+
+ if (entity.getRevision() == null
+ || entity.getRevision().getVersion() == null
+ || entity.getRevision().getVersion().longValue() != 0) {
+ throw new IllegalArgumentException("A revision of 0 must be specified when creating a new " + entityTypeName + ".");
+ }
+ }
+
+ private void validateUpdateOfRevisableEntity(final RevisableEntity entity, final String entityTypeName) {
+ if (entity == null) {
+ throw new IllegalArgumentException(entityTypeName + " cannot be null");
+ }
+
+ if (entity.getRevision() == null || entity.getRevision().getVersion() == null) {
+ throw new IllegalArgumentException("Revision info must be specified.");
+ }
+ }
+
+ private void validateDeleteOfRevisableEntity(final String identifier, final RevisionInfo revision, final String entityTypeName) {
+ if (identifier == null || identifier.trim().isEmpty()) {
+ throw new IllegalArgumentException(entityTypeName + " identifier is required");
+ }
+
+ if (revision == null || revision.getVersion() == null) {
+ throw new IllegalArgumentException("Revision info must be specified.");
+ }
+ }
+
+ private <T extends RevisableEntity> T createRevisableEntity(final T requestEntity, final String entityTypeName,
+ final String creatorIdentity, final Supplier<T> createEntity) {
+ try {
+ return entityService.create(requestEntity, creatorIdentity, createEntity);
+ } catch (InvalidRevisionException e) {
+ final String msg = String.format(INVALID_REVISION_MSG, entityTypeName, "create", requestEntity.getIdentifier());
+ throw new InvalidRevisionException(msg, e);
+ }
+ }
+
+ private <T extends RevisableEntity> T updateRevisableEntity(final T requestEntity, final String entityTypeName,
+ final String updaterIdentity, final Supplier<T> updateEntity) {
+ try {
+ return entityService.update(requestEntity, updaterIdentity, updateEntity);
+ } catch (InvalidRevisionException e) {
+ final String msg = String.format(INVALID_REVISION_MSG, entityTypeName, "update", requestEntity.getIdentifier());
+ throw new InvalidRevisionException(msg, e);
+ }
+ }
+
+ private <T extends RevisableEntity> T deleteRevisableEntity(final String entityIdentifier, final String entityTypeName,
+ final RevisionInfo revisionInfo, final Supplier<T> deleteEntity) {
+ try {
+ return entityService.delete(entityIdentifier, revisionInfo, deleteEntity);
+ } catch (InvalidRevisionException e) {
+ final String msg = String.format(INVALID_REVISION_MSG, entityTypeName, "delete", entityIdentifier);
+ throw new InvalidRevisionException(msg, e);
+ }
+ }
+
+}
diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/service/StreamingContent.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/service/StreamingContent.java
new file mode 100644
index 0000000..ed4d3e8
--- /dev/null
+++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/service/StreamingContent.java
@@ -0,0 +1,39 @@
+/*
+ * 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.registry.web.service;
+
+import javax.ws.rs.core.StreamingOutput;
+
+public class StreamingContent {
+
+ private final StreamingOutput output;
+
+ private final String filename;
+
+ public StreamingContent(final StreamingOutput output, final String filename) {
+ this.output = output;
+ this.filename = filename;
+ }
+
+ public StreamingOutput getOutput() {
+ return output;
+ }
+
+ public String getFilename() {
+ return filename;
+ }
+}
diff --git a/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/BucketsIT.java b/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/BucketsIT.java
index 7edd73e..2892d03 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/BucketsIT.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/BucketsIT.java
@@ -17,6 +17,7 @@
package org.apache.nifi.registry.web.api;
import org.apache.nifi.registry.bucket.Bucket;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
import org.junit.Test;
import org.skyscreamer.jsonassert.JSONAssert;
import org.springframework.test.annotation.IfProfileValue;
@@ -26,6 +27,8 @@
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import java.util.UUID;
+
import static org.apache.nifi.registry.web.api.IntegrationTestUtils.assertBucketsEqual;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -105,6 +108,8 @@
@Test
public void testCreateBucketGetBucket() throws Exception {
+ final String clientId = UUID.randomUUID().toString();
+ final RevisionInfo initialRevision = new RevisionInfo(clientId, 0L);
// Given:
@@ -112,6 +117,7 @@
final Bucket bucket = new Bucket();
bucket.setName("Integration Test Bucket");
bucket.setDescription("A bucket created by an integration test.");
+ bucket.setRevision(initialRevision);
// When: a bucket is created on the server
@@ -127,6 +133,9 @@
assertTrue(createdBucket.getCreatedTimestamp() - testStartTime > 0L); // both server and client in same JVM, so there shouldn't be skew
assertNotNull(createdBucket.getLink());
assertNotNull(createdBucket.getLink().getUri());
+ assertNotNull(createdBucket.getRevision());
+ assertEquals(initialRevision.getVersion() + 1, createdBucket.getRevision().getVersion().longValue());
+ assertEquals(initialRevision.getClientId(), createdBucket.getRevision().getClientId());
// And when /buckets is queried, then the newly created bucket is returned in the list
@@ -153,16 +162,23 @@
.request()
.get(Bucket.class);
assertBucketsEqual(createdBucket, bucketById, true);
+ assertNotNull(bucketById.getRevision());
+ assertEquals(initialRevision.getVersion() + 1, bucketById.getRevision().getVersion().longValue());
+ assertEquals(initialRevision.getClientId(), bucketById.getRevision().getClientId());
}
@Test
public void testUpdateBucket() throws Exception {
+ final String clientId = UUID.randomUUID().toString();
+ final RevisionInfo initialRevision = new RevisionInfo(clientId, 0L);
// Given: a bucket exists on the server
final Bucket bucket = new Bucket();
bucket.setName("Integration Test Bucket");
bucket.setDescription("A bucket created by an integration test.");
+ bucket.setRevision(initialRevision);
+
Bucket createdBucket = client
.target(createURL("buckets"))
.request()
@@ -181,17 +197,55 @@
// Then: the server returns the updated bucket
assertBucketsEqual(createdBucket, updatedBucket, true);
+ }
+ @Test
+ public void testUpdateBucketWithIncorrectRevision() throws Exception {
+ final String clientId = UUID.randomUUID().toString();
+ final RevisionInfo initialRevision = new RevisionInfo(clientId, 0L);
+
+ // Given: a bucket exists on the server
+
+ final Bucket bucket = new Bucket();
+ bucket.setName("Integration Test Bucket");
+ bucket.setDescription("A bucket created by an integration test.");
+ bucket.setRevision(initialRevision);
+
+ Bucket createdBucket = client
+ .target(createURL("buckets"))
+ .request()
+ .post(Entity.entity(bucket, MediaType.APPLICATION_JSON), Bucket.class);
+
+ // When: the bucket is modified by the client and updated on the server
+
+ createdBucket.setName("Renamed Bucket");
+ createdBucket.setDescription("This bucket has been updated by an integration test.");
+
+ // Change version to incorrect number and don't send a client id
+ createdBucket.getRevision().setClientId(null);
+ createdBucket.getRevision().setVersion(99L);
+
+ final Response response = client
+ .target(createURL("buckets/" + createdBucket.getIdentifier()))
+ .request()
+ .put(Entity.entity(createdBucket, MediaType.APPLICATION_JSON));
+
+ // Then: we get a bad request for sending a wrong revision
+
+ assertEquals(400, response.getStatus());
}
@Test
public void testDeleteBucket() throws Exception {
+ final String clientId = UUID.randomUUID().toString();
+ final RevisionInfo initialRevision = new RevisionInfo(clientId, 0L);
// Given: a bucket has been created
final Bucket bucket = new Bucket();
bucket.setName("Integration Test Bucket");
bucket.setDescription("A bucket created by an integration test.");
+ bucket.setRevision(initialRevision);
Bucket createdBucket = client
.target(createURL("buckets"))
@@ -202,6 +256,7 @@
final Bucket deletedBucket = client
.target(createURL("buckets/" + createdBucket.getIdentifier()))
+ .queryParam("version", createdBucket.getRevision().getVersion().longValue())
.request()
.delete(Bucket.class);
@@ -220,6 +275,36 @@
}
@Test
+ public void testDeleteBucketWithIncorrectRevision() throws Exception {
+ final String clientId = UUID.randomUUID().toString();
+ final RevisionInfo initialRevision = new RevisionInfo(clientId, 0L);
+
+ // Given: a bucket has been created
+
+ final Bucket bucket = new Bucket();
+ bucket.setName("Integration Test Bucket");
+ bucket.setDescription("A bucket created by an integration test.");
+ bucket.setRevision(initialRevision);
+
+ Bucket createdBucket = client
+ .target(createURL("buckets"))
+ .request()
+ .post(Entity.entity(bucket, MediaType.APPLICATION_JSON), Bucket.class);
+
+ // When: that bucket deleted
+
+ final Response response = client
+ .target(createURL("buckets/" + createdBucket.getIdentifier()))
+ .queryParam("version", 99L)
+ .request()
+ .delete();
+
+ // Then: we get a bad request for sending the wrong revision version
+
+ assertEquals(400, response.getStatus());
+ }
+
+ @Test
public void getBucketFields() throws Exception {
// Given: the server is configured to return this fixed response
diff --git a/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/DBFlowStorageIT.java b/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/DBFlowStorageIT.java
index b438eaa..5afccc3 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/DBFlowStorageIT.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/DBFlowStorageIT.java
@@ -26,6 +26,7 @@
import org.apache.nifi.registry.flow.VersionedFlowSnapshot;
import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata;
import org.apache.nifi.registry.flow.VersionedProcessGroup;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -82,16 +83,20 @@
@Test
public void testAll() throws IOException, NiFiRegistryException {
+ final RevisionInfo initialRevision = new RevisionInfo("DBFlowStorageIT", 0L);
+
// Create two buckets...
final Bucket b1 = new Bucket();
b1.setName("b1");
+ b1.setRevision(initialRevision);
final Bucket createdB1 = client.getBucketClient().create(b1);
assertNotNull(createdB1);
final Bucket b2 = new Bucket();
b2.setName("b2");
+ b2.setRevision(initialRevision);
final Bucket createdB2 = client.getBucketClient().create(b2);
assertNotNull(createdB2);
@@ -101,6 +106,7 @@
final VersionedFlow f1 = new VersionedFlow();
f1.setName("f1");
f1.setBucketIdentifier(createdB1.getIdentifier());
+ f1.setRevision(initialRevision);
final VersionedFlow createdF1 = client.getFlowClient().create(f1);
assertNotNull(createdF1);
@@ -108,6 +114,7 @@
final VersionedFlow f2 = new VersionedFlow();
f2.setName("f2");
f2.setBucketIdentifier(createdB2.getIdentifier());
+ f2.setRevision(initialRevision);
final VersionedFlow createdF2 = client.getFlowClient().create(f2);
assertNotNull(createdF2);
@@ -144,7 +151,7 @@
// Verify deleting a flow...
- client.getFlowClient().delete(createdB1.getIdentifier(), createdF1.getIdentifier());
+ client.getFlowClient().delete(createdB1.getIdentifier(), createdF1.getIdentifier(), createdF1.getRevision());
// All versions of f1 should be deleted
try {
diff --git a/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/FlowsIT.java b/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/FlowsIT.java
index 5770a15..4471494 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/FlowsIT.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/FlowsIT.java
@@ -21,6 +21,7 @@
import org.apache.nifi.registry.flow.VersionedFlowSnapshot;
import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata;
import org.apache.nifi.registry.flow.VersionedProcessGroup;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
import org.junit.Assert;
import org.junit.Test;
import org.skyscreamer.jsonassert.JSONAssert;
@@ -108,6 +109,7 @@
@Test
public void testCreateFlowGetFlow() throws Exception {
+ final RevisionInfo initialRevision = new RevisionInfo("FlowsIT", 0L);
// Given: an empty bucket with id "3" (see FlowsIT.sql)
@@ -120,6 +122,7 @@
flow.setBucketIdentifier(bucketId);
flow.setName("Test Flow");
flow.setDescription("This is a flow created by an integration test.");
+ flow.setRevision(initialRevision);
final VersionedFlow createdFlow = client
.target(createURL("buckets/{bucketId}/flows"))
@@ -138,6 +141,9 @@
assertEquals(createdFlow.getCreatedTimestamp(), createdFlow.getModifiedTimestamp());
assertNotNull(createdFlow.getLink());
assertNotNull(createdFlow.getLink().getUri());
+ assertNotNull(createdFlow.getRevision());
+ assertEquals(initialRevision.getClientId(), createdFlow.getRevision().getClientId());
+ assertEquals(initialRevision.getVersion() + 1, createdFlow.getRevision().getVersion().longValue());
// And when .../flows is queried, then the newly created flow is returned in the list
@@ -167,11 +173,15 @@
.request()
.get(VersionedFlow.class);
assertFlowsEqual(createdFlow, flowById, true);
+ assertNotNull(flowById.getRevision());
+ assertEquals(initialRevision.getClientId(), flowById.getRevision().getClientId());
+ assertEquals(initialRevision.getVersion() + 1, flowById.getRevision().getVersion().longValue());
}
@Test
public void testUpdateFlow() throws Exception {
+ final RevisionInfo initialRevision = new RevisionInfo("FlowsIT", 0L);
// Given: a flow exists on the server
@@ -180,6 +190,8 @@
flow.setBucketIdentifier(bucketId);
flow.setName("Test Flow");
flow.setDescription("This is a flow created by an integration test.");
+ flow.setRevision(initialRevision);
+
final VersionedFlow createdFlow = client
.target(createURL("buckets/{bucketId}/flows"))
.resolveTemplate("bucketId", bucketId)
@@ -203,11 +215,11 @@
assertTrue(updatedFlow.getModifiedTimestamp() > createdFlow.getModifiedTimestamp());
createdFlow.setModifiedTimestamp(updatedFlow.getModifiedTimestamp());
assertFlowsEqual(createdFlow, updatedFlow, true);
-
}
@Test
- public void testDeleteBucket() throws Exception {
+ public void testUpdateFlowWithIncorrectRevision() throws Exception {
+ final RevisionInfo initialRevision = new RevisionInfo("FlowsIT", 0L);
// Given: a flow exists on the server
@@ -216,6 +228,47 @@
flow.setBucketIdentifier(bucketId);
flow.setName("Test Flow");
flow.setDescription("This is a flow created by an integration test.");
+ flow.setRevision(initialRevision);
+
+ final VersionedFlow createdFlow = client
+ .target(createURL("buckets/{bucketId}/flows"))
+ .resolveTemplate("bucketId", bucketId)
+ .request()
+ .post(Entity.entity(flow, MediaType.APPLICATION_JSON), VersionedFlow.class);
+
+ // When: the flow revision has no clientId and the incorrect version
+
+ createdFlow.setName("Renamed Flow");
+ createdFlow.setDescription("This flow has been updated by an integration test.");
+ createdFlow.getRevision().setClientId(null);
+ createdFlow.getRevision().setVersion(99L);
+
+ final Response response = client
+ .target(createURL("buckets/{bucketId}/flows/{flowId}"))
+ .resolveTemplate("bucketId", bucketId)
+ .resolveTemplate("flowId", createdFlow.getIdentifier())
+ .request()
+ .put(Entity.entity(createdFlow, MediaType.APPLICATION_JSON));
+
+ // Then: 400 bad request because of the incorrect version sent
+
+ assertEquals(400, response.getStatus());
+
+ }
+
+ @Test
+ public void testDeleteFlow() throws Exception {
+ final RevisionInfo initialRevision = new RevisionInfo("FlowsIT", 0L);
+
+ // Given: a flow exists on the server
+
+ final String bucketId = "3";
+ final VersionedFlow flow = new VersionedFlow();
+ flow.setBucketIdentifier(bucketId);
+ flow.setName("Test Flow");
+ flow.setDescription("This is a flow created by an integration test.");
+ flow.setRevision(initialRevision);
+
final VersionedFlow createdFlow = client
.target(createURL("buckets/{bucketId}/flows"))
.resolveTemplate("bucketId", bucketId)
@@ -228,6 +281,7 @@
.target(createURL("buckets/{bucketId}/flows/{flowId}"))
.resolveTemplate("bucketId", bucketId)
.resolveTemplate("flowId", createdFlow.getIdentifier())
+ .queryParam("version", createdFlow.getRevision().getVersion().longValue())
.request()
.delete(VersionedFlow.class);
@@ -248,6 +302,40 @@
}
@Test
+ public void testDeleteFlowWithIncorrectRevision() throws Exception {
+ final RevisionInfo initialRevision = new RevisionInfo("FlowsIT", 0L);
+
+ // Given: a flow exists on the server
+
+ final String bucketId = "3";
+ final VersionedFlow flow = new VersionedFlow();
+ flow.setBucketIdentifier(bucketId);
+ flow.setName("Test Flow");
+ flow.setDescription("This is a flow created by an integration test.");
+ flow.setRevision(initialRevision);
+
+ final VersionedFlow createdFlow = client
+ .target(createURL("buckets/{bucketId}/flows"))
+ .resolveTemplate("bucketId", bucketId)
+ .request()
+ .post(Entity.entity(flow, MediaType.APPLICATION_JSON), VersionedFlow.class);
+
+ // When: the flow is deleted
+
+ final Response response = client
+ .target(createURL("buckets/{bucketId}/flows/{flowId}"))
+ .resolveTemplate("bucketId", bucketId)
+ .resolveTemplate("flowId", createdFlow.getIdentifier())
+ .queryParam("version", 99L)
+ .request()
+ .delete();
+
+ // Then: 400 bad request because of the incorrect version sent
+
+ assertEquals(400, response.getStatus());
+ }
+
+ @Test
public void testGetFlowVersionsEmpty() throws Exception {
// Given: a Bucket "2" containing a flow "3" with no snapshots (see FlowsIT.sql)
@@ -312,6 +400,7 @@
@Test
public void testCreateFlowVersionGetFlowVersion() throws Exception {
+ final RevisionInfo initialRevision = new RevisionInfo("FlowsIT", 0L);
// Given: an empty Bucket "3" (see FlowsIT.sql) with a newly created flow
@@ -321,6 +410,8 @@
flow.setBucketIdentifier(bucketId);
flow.setName("Test Flow for creating snapshots");
flow.setDescription("This is a randomly named flow created by an integration test for the purpose of holding snapshots.");
+ flow.setRevision(initialRevision);
+
final VersionedFlow createdFlow = client
.target(createURL("buckets/{bucketId}/flows"))
.resolveTemplate("bucketId", bucketId)
@@ -422,6 +513,7 @@
flow.setBucketIdentifier(bucketId);
flow.setName(flowName);
flow.setDescription("This is a flow created by an integration test.");
+ flow.setRevision(new RevisionInfo("FlowsIT", 0L));
// saving this flow to bucket 3 should work because bucket 3 is empty
diff --git a/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureFileIT.java b/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureFileIT.java
index 095d258..7f81215 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureFileIT.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureFileIT.java
@@ -21,6 +21,7 @@
import org.apache.nifi.registry.authorization.Tenant;
import org.apache.nifi.registry.authorization.User;
import org.apache.nifi.registry.authorization.UserGroup;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.skyscreamer.jsonassert.JSONAssert;
@@ -33,6 +34,8 @@
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import java.util.UUID;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -108,8 +111,13 @@
// Given: the server has been configured with FileUserGroupProvider, which is configurable,
// and: the initial admin client wants to create a tenant
+ Long initialVersion = new Long(0);
+ String clientId = UUID.randomUUID().toString();
+ RevisionInfo revisionInfo = new RevisionInfo(clientId, initialVersion);
+
Tenant tenant = new Tenant();
tenant.setIdentity("New User");
+ tenant.setRevision(revisionInfo);
// When: the POST /tenants/users endpoint is accessed
final Response createUserResponse = client
@@ -121,6 +129,9 @@
assertEquals(201, createUserResponse.getStatus());
User actualUser = createUserResponse.readEntity(User.class);
assertNotNull(actualUser.getIdentifier());
+ assertNotNull(actualUser.getRevision());
+ assertNotNull(actualUser.getRevision().getVersion());
+
try {
assertEquals(tenant.getIdentity(), actualUser.getIdentity());
assertEquals(true, actualUser.getConfigurable());
@@ -129,7 +140,8 @@
assertEquals(new ResourcePermissions(), actualUser.getResourcePermissions());
} finally {
// cleanup user for other tests
- client.target(createURL("tenants/users/" + actualUser.getIdentifier()))
+ final long version = actualUser.getRevision().getVersion();
+ client.target(createURL("tenants/users/" + actualUser.getIdentifier() + "?version=" + version))
.request()
.delete();
}
@@ -141,8 +153,13 @@
// Given: the server has been configured with FileUserGroupProvider, which is configurable,
// and: the initial admin client wants to create a tenant
+ Long initialVersion = new Long(0);
+ String clientId = UUID.randomUUID().toString();
+ RevisionInfo revisionInfo = new RevisionInfo(clientId, initialVersion);
+
Tenant tenant = new Tenant();
tenant.setIdentity("New Group");
+ tenant.setRevision(revisionInfo);
// When: the POST /tenants/user-groups endpoint is used
final Response createUserGroupResponse = client
@@ -154,6 +171,9 @@
assertEquals(201, createUserGroupResponse.getStatus());
UserGroup actualUserGroup = createUserGroupResponse.readEntity(UserGroup.class);
assertNotNull(actualUserGroup.getIdentifier());
+ assertNotNull(actualUserGroup.getRevision());
+ assertNotNull(actualUserGroup.getRevision().getVersion());
+
try {
assertEquals(tenant.getIdentity(), actualUserGroup.getIdentity());
assertEquals(true, actualUserGroup.getConfigurable());
@@ -162,7 +182,8 @@
assertEquals(new ResourcePermissions(), actualUserGroup.getResourcePermissions());
} finally {
// cleanup user for other tests
- client.target(createURL("tenants/user-groups/" + actualUserGroup.getIdentifier()))
+ final long version = actualUserGroup.getRevision().getVersion();
+ client.target(createURL("tenants/user-groups/" + actualUserGroup.getIdentifier() + "?version=" + version))
.request()
.delete();
}
diff --git a/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureLdapIT.java b/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureLdapIT.java
index c6172a1..4147b3d 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureLdapIT.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureLdapIT.java
@@ -28,6 +28,7 @@
import org.apache.nifi.registry.properties.AESSensitivePropertyProvider;
import org.apache.nifi.registry.properties.NiFiRegistryProperties;
import org.apache.nifi.registry.properties.SensitivePropertyProvider;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
import org.apache.nifi.registry.security.authorization.Authorizer;
import org.apache.nifi.registry.security.authorization.AuthorizerFactory;
import org.apache.nifi.registry.security.crypto.BootstrapFileCryptoKeyProvider;
@@ -38,6 +39,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.skyscreamer.jsonassert.JSONAssert;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
@@ -59,6 +62,7 @@
import java.util.Base64;
import java.util.List;
import java.util.Set;
+import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -83,6 +87,8 @@
@Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = "classpath:db/clearDB.sql")
public class SecureLdapIT extends IntegrationTestBase {
+ private static Logger LOGGER = LoggerFactory.getLogger(SecureLdapIT.class);
+
private static final String tokenLoginPath = "access/token/login";
private static final String tokenIdentityProviderPath = "access/token/identity-provider";
@@ -95,7 +101,10 @@
@Primary
@Bean
@DependsOn({"directoryServer"}) // Can't load LdapUserGroupProvider until the embedded LDAP server, which creates the "directoryServer" bean, is running
- public static Authorizer getAuthorizer(@Autowired NiFiRegistryProperties properties, ExtensionManager extensionManager, RegistryService registryService) throws Exception {
+ public static Authorizer getAuthorizer(
+ @Autowired NiFiRegistryProperties properties,
+ ExtensionManager extensionManager,
+ RegistryService registryService) throws Exception {
if (authorizerFactory == null) {
authorizerFactory = new AuthorizerFactory(properties, extensionManager, sensitivePropertyProvider(), registryService);
}
@@ -367,11 +376,15 @@
@Test
public void testCreateTenantFails() throws Exception {
+ Long initialVersion = new Long(0);
+ String clientId = UUID.randomUUID().toString();
+ RevisionInfo initialRevisionInfo = new RevisionInfo(clientId, initialVersion);
// Given: the server has been configured with the LdapUserGroupProvider, which is non-configurable,
// and: the client wants to create a tenant
Tenant tenant = new Tenant();
tenant.setIdentity("new_tenant");
+ tenant.setRevision(initialRevisionInfo);
// When: the POST /tenants/users endpoint is accessed
final Response createUserResponse = client
@@ -396,6 +409,9 @@
@Test
public void testAccessPolicyCreation() throws Exception {
+ Long initialVersion = new Long(0);
+ String clientId = UUID.randomUUID().toString();
+ RevisionInfo initialRevisionInfo = new RevisionInfo(clientId, initialVersion);
// Given: the server has been configured with an initial admin "nifiadmin" and a user with no accessPolicies "nobel"
String nobelId = getTenantIdentifierByIdentity("nobel");
@@ -425,6 +441,8 @@
final Bucket bucket = new Bucket();
bucket.setName("Integration Test Bucket");
bucket.setDescription("A bucket created by an integration test.");
+ bucket.setRevision(new RevisionInfo(null, 0L));
+
Response adminCreatesBucketResponse = client
.target(createURL("buckets"))
.request()
@@ -453,6 +471,8 @@
readPolicy.setResource("/buckets/" + createdBucket.getIdentifier());
readPolicy.setAction("read");
readPolicy.addUserGroups(Arrays.asList(new Tenant(chemistsId, "chemists")));
+ readPolicy.setRevision(initialRevisionInfo);
+
Response adminGrantsReadAccessResponse = client
.target(createURL("policies"))
.request()
@@ -496,6 +516,8 @@
writePolicy.setResource("/buckets/" + createdBucket.getIdentifier());
writePolicy.setAction("write");
writePolicy.addUsers(Arrays.asList(new Tenant(nobelId, "nobel")));
+ writePolicy.setRevision(initialRevisionInfo);
+
Response adminGrantsWriteAccessResponse = client
.target(createURL("policies"))
.request()
@@ -609,9 +631,8 @@
.map(AccessPolicy::getIdentifier)
.collect(Collectors.toSet());
- Set<String> policiesToDelete = currentAccessPolicies.stream()
+ Set<AccessPolicy> policiesToDelete = currentAccessPolicies.stream()
.filter(p -> !policiesToRestore.contains(p.getIdentifier()))
- .map(AccessPolicy::getIdentifier)
.collect(Collectors.toSet());
for (AccessPolicy originalPolicy : accessPoliciesSnapshot) {
@@ -638,14 +659,17 @@
}
- for (String id : policiesToDelete) {
+ for (AccessPolicy policyToDelete : policiesToDelete) {
try {
- client.target(createURL("policies/" + id))
+ final RevisionInfo revisionInfo = policyToDelete.getRevision();
+ final Long version = revisionInfo == null ? 0 : revisionInfo.getVersion();
+ client.target(createURL("policies/" + policyToDelete.getIdentifier()))
+ .queryParam("version", version.longValue())
.request()
.header("Authorization", "Bearer " + adminAuthToken)
.delete();
} catch (Exception e) {
- // do nothing
+ LOGGER.error("Error cleaning up policies after test due to: " + e.getMessage(), e);
}
}
diff --git a/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureNiFiRegistryClientIT.java b/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureNiFiRegistryClientIT.java
index 62aa098..dfffd7d 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureNiFiRegistryClientIT.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/SecureNiFiRegistryClientIT.java
@@ -32,6 +32,7 @@
import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata;
import org.apache.nifi.registry.flow.VersionedProcessGroup;
import org.apache.nifi.registry.authorization.CurrentUser;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -105,23 +106,28 @@
final Bucket bucket = new Bucket();
bucket.setName("Bucket 1 " + System.currentTimeMillis());
bucket.setDescription("This is bucket 1");
+ bucket.setRevision(new RevisionInfo(null, 0L));
final BucketClient bucketClient = client.getBucketClient();
final Bucket createdBucket = bucketClient.create(bucket);
Assert.assertNotNull(createdBucket);
Assert.assertNotNull(createdBucket.getIdentifier());
+ Assert.assertNotNull(createdBucket.getRevision());
final List<Bucket> buckets = bucketClient.getAll();
Assert.assertEquals(4, buckets.size());
+ buckets.forEach(b -> Assert.assertNotNull(b.getRevision()));
final VersionedFlow flow = new VersionedFlow();
flow.setBucketIdentifier(createdBucket.getIdentifier());
flow.setName("Flow 1 - " + System.currentTimeMillis());
+ flow.setRevision(new RevisionInfo(null, 0L));
final FlowClient flowClient = client.getFlowClient();
final VersionedFlow createdFlow = flowClient.create(flow);
Assert.assertNotNull(createdFlow);
Assert.assertNotNull(createdFlow.getIdentifier());
+ Assert.assertNotNull(createdFlow.getRevision());
final VersionedFlowSnapshotMetadata snapshotMetadata = new VersionedFlowSnapshotMetadata();
snapshotMetadata.setBucketIdentifier(createdFlow.getBucketIdentifier());
@@ -160,6 +166,7 @@
final Bucket bucket = new Bucket();
bucket.setName("Bucket 1");
bucket.setDescription("This is bucket 1");
+ bucket.setRevision(new RevisionInfo(null, 0L));
try {
bucketClient.create(bucket);
diff --git a/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/TenantResourceTest.java b/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/TenantResourceTest.java
index 2e3f5fd..99350bb 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/TenantResourceTest.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/TenantResourceTest.java
@@ -20,20 +20,18 @@
import org.apache.nifi.registry.authorization.UserGroup;
import org.apache.nifi.registry.event.EventFactory;
import org.apache.nifi.registry.event.EventService;
-import org.apache.nifi.registry.security.authorization.AuthorizableLookup;
-import org.apache.nifi.registry.security.authorization.Authorizer;
-import org.apache.nifi.registry.security.authorization.ConfigurableAccessPolicyProvider;
-import org.apache.nifi.registry.security.authorization.ConfigurableUserGroupProvider;
-import org.apache.nifi.registry.security.authorization.ManagedAuthorizer;
-import org.apache.nifi.registry.service.AuthorizationService;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
+import org.apache.nifi.registry.revision.web.ClientIdParameter;
+import org.apache.nifi.registry.revision.web.LongParameter;
+import org.apache.nifi.registry.web.service.ServiceFacade;
import org.junit.Before;
import org.junit.Test;
import javax.servlet.http.HttpServletRequest;
-
import java.net.URI;
import java.net.URISyntaxException;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -42,28 +40,15 @@
public class TenantResourceTest {
private TenantResource tenantResource;
- private AuthorizationService authorizationService;
- private Authorizer authorizer;
- private ConfigurableAccessPolicyProvider accessPolicyProvider;
private EventService eventService;
- private ConfigurableUserGroupProvider userGroupProvider;
- private AuthorizableLookup authorizableLookup;
+ private ServiceFacade serviceFacade;
@Before
public void setUp() {
- authorizationService = mock(AuthorizationService.class);
- authorizer = mock(ManagedAuthorizer.class);
- authorizableLookup = mock(AuthorizableLookup.class);
- accessPolicyProvider = mock(ConfigurableAccessPolicyProvider.class);
- userGroupProvider = mock(ConfigurableUserGroupProvider.class);
eventService = mock(EventService.class);
+ serviceFacade = mock(ServiceFacade.class);
- when(accessPolicyProvider.getUserGroupProvider()).thenReturn(userGroupProvider);
- when(((ManagedAuthorizer) authorizer).getAccessPolicyProvider()).thenReturn(accessPolicyProvider);
- when(authorizationService.getAuthorizer()).thenReturn(authorizer);
- when(authorizationService.getAuthorizableLookup()).thenReturn(authorizableLookup);
-
- tenantResource = new TenantResource(authorizationService, eventService) {
+ tenantResource = new TenantResource(serviceFacade, eventService) {
@Override
protected URI getBaseUri() {
@@ -79,27 +64,36 @@
@Test
public void testCreateUser() {
HttpServletRequest request = mock(HttpServletRequest.class);
- User user = new User(null, "identity");
- User result = new User("identifier", user.getIdentity());
- when(authorizationService.createUser(user)).thenReturn(result);
+ RevisionInfo revisionInfo = new RevisionInfo("client1", 0L);
+ User user = new User(null, "identity");
+ user.setRevision(revisionInfo);
+
+ RevisionInfo resultRevisionInfo = new RevisionInfo("client1", 1L);
+ User result = new User("identifier", user.getIdentity());
+ result.setRevision(resultRevisionInfo);
+
+ when(serviceFacade.createUser(user)).thenReturn(result);
tenantResource.createUser(request, user);
- verify(authorizationService).createUser(user);
+ verify(serviceFacade).createUser(user);
verify(eventService).publish(eq(EventFactory.userCreated(result)));
}
@Test
public void testUpdateUser() {
HttpServletRequest request = mock(HttpServletRequest.class);
- User user = new User("identifier", "new-identity");
- when(authorizationService.updateUser(user)).thenReturn(user);
+ RevisionInfo revisionInfo = new RevisionInfo("client1", 1L);
+ User user = new User("identifier", "new-identity");
+ user.setRevision(revisionInfo);
+
+ when(serviceFacade.updateUser(user)).thenReturn(user);
tenantResource.updateUser(request, user.getIdentifier(), user);
- verify(authorizationService).updateUser(user);
+ verify(serviceFacade).updateUser(user);
verify(eventService).publish(eq(EventFactory.userUpdated(user)));
}
@@ -107,39 +101,50 @@
public void testDeleteUser() {
HttpServletRequest request = mock(HttpServletRequest.class);
User user = new User("identifier", "identity");
+ Long version = new Long(0);
+ ClientIdParameter clientId = new ClientIdParameter();
- when(authorizationService.deleteUser(user.getIdentifier())).thenReturn(user);
+ when(serviceFacade.deleteUser(eq(user.getIdentifier()), any(RevisionInfo.class))).thenReturn(user);
- tenantResource.removeUser(request, user.getIdentifier());
+ tenantResource.removeUser(request, new LongParameter(version.toString()), clientId, user.getIdentifier());
- verify(authorizationService).deleteUser(user.getIdentifier());
+ verify(serviceFacade).deleteUser(eq(user.getIdentifier()), any(RevisionInfo.class));
verify(eventService).publish(eq(EventFactory.userDeleted(user)));
}
@Test
public void testCreateUserGroup() {
HttpServletRequest request = mock(HttpServletRequest.class);
- UserGroup userGroup = new UserGroup(null, "identity");
- UserGroup result = new UserGroup("identifier", userGroup.getIdentity());
- when(authorizationService.createUserGroup(userGroup)).thenReturn(result);
+ RevisionInfo revisionInfo = new RevisionInfo("client1", 0L);
+ UserGroup userGroup = new UserGroup(null, "identity");
+ userGroup.setRevision(revisionInfo);
+
+ RevisionInfo resultRevisionInfo = new RevisionInfo("client1", 1L);
+ UserGroup result = new UserGroup("identifier", userGroup.getIdentity());
+ result.setRevision(resultRevisionInfo);
+
+ when(serviceFacade.createUserGroup(userGroup)).thenReturn(result);
tenantResource.createUserGroup(request, userGroup);
- verify(authorizationService).createUserGroup(userGroup);
+ verify(serviceFacade).createUserGroup(userGroup);
verify(eventService).publish(eq(EventFactory.userGroupCreated(result)));
}
@Test
public void testUpdateUserGroup() {
HttpServletRequest request = mock(HttpServletRequest.class);
- UserGroup userGroup = new UserGroup("identifier", "new-identity");
- when(authorizationService.updateUserGroup(userGroup)).thenReturn(userGroup);
+ RevisionInfo revisionInfo = new RevisionInfo("client1", 1L);
+ UserGroup userGroup = new UserGroup("identifier", "new-identity");
+ userGroup.setRevision(revisionInfo);
+
+ when(serviceFacade.updateUserGroup(userGroup)).thenReturn(userGroup);
tenantResource.updateUserGroup(request, userGroup.getIdentifier(), userGroup);
- verify(authorizationService).updateUserGroup(userGroup);
+ verify(serviceFacade).updateUserGroup(userGroup);
verify(eventService).publish(eq(EventFactory.userGroupUpdated(userGroup)));
}
@@ -147,12 +152,14 @@
public void testDeleteUserGroup() {
HttpServletRequest request = mock(HttpServletRequest.class);
UserGroup userGroup = new UserGroup("identifier", "identity");
+ Long version = new Long(0);
+ ClientIdParameter clientId = new ClientIdParameter();
- when(authorizationService.deleteUserGroup(userGroup.getIdentifier())).thenReturn(userGroup);
+ when(serviceFacade.deleteUserGroup(eq(userGroup.getIdentifier()), any(RevisionInfo.class))).thenReturn(userGroup);
- tenantResource.removeUserGroup(request, userGroup.getIdentifier());
+ tenantResource.removeUserGroup(request, new LongParameter(version.toString()), clientId, userGroup.getIdentifier());
- verify(authorizationService).deleteUserGroup(userGroup.getIdentifier());
+ verify(serviceFacade).deleteUserGroup(eq(userGroup.getIdentifier()), any(RevisionInfo.class));
verify(eventService).publish(eq(EventFactory.userGroupDeleted(userGroup)));
}
}
diff --git a/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/UnsecuredNiFiRegistryClientIT.java b/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/UnsecuredNiFiRegistryClientIT.java
index 0bcf0a2..547f63b 100644
--- a/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/UnsecuredNiFiRegistryClientIT.java
+++ b/nifi-registry-core/nifi-registry-web-api/src/test/java/org/apache/nifi/registry/web/api/UnsecuredNiFiRegistryClientIT.java
@@ -69,6 +69,7 @@
import org.apache.nifi.registry.flow.VersionedProcessGroup;
import org.apache.nifi.registry.flow.VersionedProcessor;
import org.apache.nifi.registry.flow.VersionedPropertyDescriptor;
+import org.apache.nifi.registry.revision.entity.RevisionInfo;
import org.apache.nifi.registry.util.FileUtils;
import org.bouncycastle.util.encoders.Hex;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
@@ -114,10 +115,12 @@
static final Logger LOGGER = LoggerFactory.getLogger(UnsecuredNiFiRegistryClientIT.class);
+ static final String CLIENT_ID = "UnsecuredNiFiRegistryClientIT";
+
private NiFiRegistryClient client;
@Before
- public void setup() throws IOException {
+ public void setup() {
final String baseUrl = createBaseURL();
LOGGER.info("Using base url = " + baseUrl);
@@ -189,6 +192,7 @@
for (final Bucket bucket : createdBuckets) {
final Bucket retrievedBucket = bucketClient.get(bucket.getIdentifier());
assertNotNull(retrievedBucket);
+ assertNotNull(retrievedBucket.getRevision());
assertFalse(retrievedBucket.isAllowBundleRedeploy());
LOGGER.info("Retrieved bucket " + retrievedBucket.getIdentifier());
}
@@ -213,6 +217,7 @@
final Bucket bucketUpdate = new Bucket();
bucketUpdate.setIdentifier(bucket.getIdentifier());
bucketUpdate.setDescription(bucket.getDescription() + " UPDATE");
+ bucketUpdate.setRevision(bucket.getRevision());
final Bucket updatedBucket = bucketClient.update(bucketUpdate);
assertNotNull(updatedBucket);
@@ -251,6 +256,7 @@
final VersionedFlow flow1Update = new VersionedFlow();
flow1Update.setIdentifier(flow1.getIdentifier());
flow1Update.setName(flow1.getName() + " UPDATED");
+ flow1Update.setRevision(retrievedFlow1.getRevision());
final VersionedFlow updatedFlow1 = flowClient.update(flowsBucket.getIdentifier(), flow1Update);
assertNotNull(updatedFlow1);
@@ -793,11 +799,11 @@
// ---------------------- DELETE DATA --------------------------//
- final VersionedFlow deletedFlow1 = flowClient.delete(flowsBucket.getIdentifier(), flow1.getIdentifier());
+ final VersionedFlow deletedFlow1 = flowClient.delete(flowsBucket.getIdentifier(), updatedFlow1.getIdentifier(), updatedFlow1.getRevision());
assertNotNull(deletedFlow1);
LOGGER.info("Deleted flow " + deletedFlow1.getIdentifier());
- final VersionedFlow deletedFlow2 = flowClient.delete(flowsBucket.getIdentifier(), flow2.getIdentifier());
+ final VersionedFlow deletedFlow2 = flowClient.delete(flowsBucket.getIdentifier(), flow2.getIdentifier(), flow2.getRevision());
assertNotNull(deletedFlow2);
LOGGER.info("Deleted flow " + deletedFlow2.getIdentifier());
@@ -811,7 +817,7 @@
// delete each bucket
for (final Bucket bucket : createdBuckets) {
- final Bucket deletedBucket = bucketClient.delete(bucket.getIdentifier());
+ final Bucket deletedBucket = bucketClient.delete(bucket.getIdentifier(), bucket.getRevision());
assertNotNull(deletedBucket);
LOGGER.info("Deleted bucket " + deletedBucket.getIdentifier());
}
@@ -823,9 +829,12 @@
@Test
public void testFlowSnapshotsWithParameterContextAndEncodingVersion() throws IOException, NiFiRegistryException {
+ final RevisionInfo initialRevision = new RevisionInfo(null, 0L);
+
// Create a bucket
final Bucket bucket = new Bucket();
bucket.setName("Test Bucket");
+ bucket.setRevision(initialRevision);
final Bucket createdBucket = client.getBucketClient().create(bucket);
assertNotNull(createdBucket);
@@ -834,6 +843,7 @@
final VersionedFlow flow = new VersionedFlow();
flow.setName("My Flow");
flow.setBucketIdentifier(createdBucket.getIdentifier());
+ flow.setRevision(initialRevision);
final VersionedFlow createdFlow = client.getFlowClient().create(flow);
assertNotNull(createdFlow);
@@ -961,6 +971,7 @@
final Bucket bucket = new Bucket();
bucket.setName("Bucket #" + num);
bucket.setDescription("This is bucket #" + num);
+ bucket.setRevision(new RevisionInfo(CLIENT_ID, 0L));
return bucketClient.create(bucket);
}
@@ -969,6 +980,7 @@
versionedFlow.setName(bucket.getName() + " Flow #" + num);
versionedFlow.setDescription("This is " + bucket.getName() + " flow #" + num);
versionedFlow.setBucketIdentifier(bucket.getIdentifier());
+ versionedFlow.setRevision(new RevisionInfo(CLIENT_ID, 0L));
return client.create(versionedFlow);
}
diff --git a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-user-to-groups/nf-registry-add-user-to-groups.js b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-user-to-groups/nf-registry-add-user-to-groups.js
index 0461bb0..c05510f 100644
--- a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-user-to-groups/nf-registry-add-user-to-groups.js
+++ b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-user-to-groups/nf-registry-add-user-to-groups.js
@@ -18,10 +18,9 @@
import { TdDataTableService } from '@covalent/core/data-table';
import NfRegistryApi from 'services/nf-registry.api';
import { Component } from '@angular/core';
-import { FdsSnackBarService } from '@nifi-fds/core';
+import { FdsDialogService, FdsSnackBarService } from '@nifi-fds/core';
import NfRegistryService from 'services/nf-registry.service';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
-import $ from 'jquery';
/**
* NfRegistryAddUserToGroups constructor.
@@ -30,22 +29,23 @@
* @param tdDataTableService The covalent data table service module.
* @param nfRegistryService The nf-registry.service module.
* @param matDialogRef The angular material dialog ref.
+ * @param fdsDialogService The FDS dialog service.
* @param fdsSnackBarService The FDS snack bar service module.
* @param data The data passed into this component.
* @constructor
*/
-function NfRegistryAddUserToGroups(nfRegistryApi, tdDataTableService, nfRegistryService, matDialogRef, fdsSnackBarService, data) {
+function NfRegistryAddUserToGroups(nfRegistryApi, tdDataTableService, nfRegistryService, matDialogRef, fdsDialogService, fdsSnackBarService, data) {
//Services
this.dataTableService = tdDataTableService;
this.snackBarService = fdsSnackBarService;
+ this.dialogService = fdsDialogService;
this.nfRegistryService = nfRegistryService;
this.nfRegistryApi = nfRegistryApi;
this.dialogRef = matDialogRef;
this.data = data;
// local state
- //make an independent copy of the groups for sorting and selecting within the scope of this component
- this.groups = $.extend(true, [], this.nfRegistryService.groups);
+ this.groups = [];
this.filteredUserGroups = [];
this.isAddToSelectedGroupsDisabled = true;
this.userGroupsSearchTerms = [];
@@ -70,21 +70,35 @@
ngOnInit: function () {
var self = this;
- // filter out any groups that
- // 1) that are not configurable
- self.groups = self.groups.filter(function (group) {
- return !!(group.configurable);
- });
- // 2) the user already belongs to
- this.data.user.userGroups.forEach(function (userGroup) {
- self.groups = self.groups.filter(function (group) {
- return (group.identifier !== userGroup.identifier);
- });
- });
+ // retrieve the fresh list of groups
+ self.nfRegistryApi.getUserGroups().subscribe(function (response) {
+ if (!response.status || response.status === 200) {
+ self.groups = response;
- this.filterGroups();
- this.deselectAllUserGroups();
- this.determineAllUserGroupsSelectedState();
+ // filter out any groups that
+ // 1) that are not configurable
+ self.groups = self.groups.filter(function (group) {
+ return !!(group.configurable);
+ });
+ // 2) the user already belongs to
+ self.data.user.userGroups.forEach(function (userGroup) {
+ self.groups = self.groups.filter(function (group) {
+ return (group.identifier !== userGroup.identifier);
+ });
+ });
+
+ self.filterGroups();
+ self.deselectAllUserGroups();
+ self.determineAllUserGroupsSelectedState();
+ } else {
+ self.dialogService.openConfirm({
+ title: 'Error',
+ message: response.error,
+ acceptButton: 'Close',
+ acceptButtonColor: 'fds-warn'
+ });
+ }
+ });
},
/**
@@ -214,17 +228,26 @@
});
selectedGroups.forEach(function (selectedGroup) {
selectedGroup.users.push(self.data.user);
- self.nfRegistryApi.updateUserGroup(selectedGroup.identifier, selectedGroup.identity, selectedGroup.users).subscribe(function (group) {
+ self.nfRegistryApi.updateUserGroup(selectedGroup.identifier, selectedGroup.identity, selectedGroup.users, selectedGroup.revision).subscribe(function (response) {
self.dialogRef.close();
- self.snackBarService.openCoaster({
- title: 'Success',
- message: 'User has been added to the ' + group.identity + ' group.',
- verticalPosition: 'bottom',
- horizontalPosition: 'right',
- icon: 'fa fa-check-circle-o',
- color: '#1EB475',
- duration: 3000
- });
+ if (!response.status || response.status === 200) {
+ self.snackBarService.openCoaster({
+ title: 'Success',
+ message: 'User has been added to the ' + response.identity + ' group.',
+ verticalPosition: 'bottom',
+ horizontalPosition: 'right',
+ icon: 'fa fa-check-circle-o',
+ color: '#1EB475',
+ duration: 3000
+ });
+ } else {
+ self.dialogService.openConfirm({
+ title: 'Error',
+ message: response.error,
+ acceptButton: 'Close',
+ acceptButtonColor: 'fds-warn'
+ });
+ }
});
});
},
@@ -248,6 +271,7 @@
TdDataTableService,
NfRegistryService,
MatDialogRef,
+ FdsDialogService,
FdsSnackBarService,
MAT_DIALOG_DATA
];
diff --git a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-user-to-groups/nf-registry-add-user-to-groups.spec.js b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-user-to-groups/nf-registry-add-user-to-groups.spec.js
index 910cdfa..1f9f16c 100644
--- a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-user-to-groups/nf-registry-add-user-to-groups.spec.js
+++ b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-user-to-groups/nf-registry-add-user-to-groups.spec.js
@@ -21,12 +21,13 @@
from 'components/administration/users/dialogs/add-user-to-groups/nf-registry-add-user-to-groups';
import { of } from 'rxjs';
import { TdDataTableService } from '@covalent/core/data-table';
-import { FdsSnackBarService } from '@nifi-fds/core';
+import { FdsDialogService, FdsSnackBarService } from '@nifi-fds/core';
describe('NfRegistryAddUserToGroups Component isolated unit tests', function () {
var comp;
var nfRegistryService;
var nfRegistryApi;
+ var dialogService;
var snackBarService;
var dataTableService;
@@ -37,14 +38,17 @@
nfRegistryService.groups = [{identifier: 1, identity: 'Group 1', configurable: true, checked: true, users: []}];
nfRegistryApi = new NfRegistryApi();
+ dialogService = new FdsDialogService();
snackBarService = new FdsSnackBarService();
dataTableService = new TdDataTableService();
comp = new NfRegistryAddUserToGroups(nfRegistryApi, dataTableService, nfRegistryService, {
close: function () {
}
- }, snackBarService, {user: nfRegistryService.user});
+ }, dialogService, snackBarService, {user: nfRegistryService.user});
// Spy
+ spyOn(nfRegistryApi, 'getUserGroups').and.callFake(function () {
+ }).and.returnValue(of([{identifier: 1, identity: 'Group 1', configurable: true, checked: true, users: []}]));
spyOn(nfRegistryApi, 'getUserGroup').and.callFake(function () {
}).and.returnValue(of({identifier: 1, identity: 'Group 1'}));
spyOn(nfRegistryApi, 'updateUserGroup').and.callFake(function () {
diff --git a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-users-to-group/nf-registry-add-users-to-group.js b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-users-to-group/nf-registry-add-users-to-group.js
index b40af3d..4da70f9 100644
--- a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-users-to-group/nf-registry-add-users-to-group.js
+++ b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-users-to-group/nf-registry-add-users-to-group.js
@@ -18,10 +18,9 @@
import { TdDataTableService } from '@covalent/core/data-table';
import NfRegistryApi from 'services/nf-registry.api';
import { Component } from '@angular/core';
-import { FdsSnackBarService } from '@nifi-fds/core';
+import { FdsDialogService, FdsSnackBarService } from '@nifi-fds/core';
import NfRegistryService from 'services/nf-registry.service';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
-import $ from 'jquery';
/**
* NfRegistryAddUsersToGroup constructor.
@@ -31,13 +30,15 @@
* @param nfRegistryService The nf-registry.service module.
* @param matDialogRef The angular material dialog ref.
* @param fdsSnackBarService The FDS snack bar service module.
+ * @param fdsDialogService The FDS dialog service.
* @param data The data passed into this component.
* @constructor
*/
-function NfRegistryAddUsersToGroup(nfRegistryApi, tdDataTableService, nfRegistryService, matDialogRef, fdsSnackBarService, data) {
+function NfRegistryAddUsersToGroup(nfRegistryApi, tdDataTableService, nfRegistryService, matDialogRef, fdsDialogService, fdsSnackBarService, data) {
// Services
this.dataTableService = tdDataTableService;
this.snackBarService = fdsSnackBarService;
+ this.dialogService = fdsDialogService;
this.nfRegistryService = nfRegistryService;
this.nfRegistryApi = nfRegistryApi;
this.dialogRef = matDialogRef;
@@ -45,7 +46,7 @@
// local state
//make an independent copy of the users for sorting and selecting within the scope of this component
- this.users = $.extend(true, [], this.nfRegistryService.users);
+ this.users = [];
this.filteredUsers = [];
this.isAddSelectedUsersToGroupDisabled = true;
this.usersSearchTerms = [];
@@ -70,15 +71,29 @@
ngOnInit: function () {
var self = this;
- this.data.group.users.forEach(function (groupUser) {
- self.users = self.users.filter(function (user) {
- return (user.identifier !== groupUser.identifier);
- });
- });
+ // retrieve the fresh list of users
+ self.nfRegistryApi.getUsers().subscribe(function (response) {
+ if (!response.status || response.status === 200) {
+ self.users = response;
- this.filterUsers();
- this.deselectAllUsers();
- this.determineAllUsersSelectedState();
+ self.data.group.users.forEach(function (groupUser) {
+ self.users = self.users.filter(function (user) {
+ return (user.identifier !== groupUser.identifier);
+ });
+ });
+
+ self.filterUsers();
+ self.deselectAllUsers();
+ self.determineAllUsersSelectedState();
+ } else {
+ self.dialogService.openConfirm({
+ title: 'Error',
+ message: response.error,
+ acceptButton: 'Close',
+ acceptButtonColor: 'fds-warn'
+ });
+ }
+ });
},
/**
@@ -208,17 +223,26 @@
}).forEach(function (filteredUser) {
self.data.group.users.push(filteredUser);
});
- this.nfRegistryApi.updateUserGroup(self.data.group.identifier, self.data.group.identity, self.data.group.users).subscribe(function (group) {
+ this.nfRegistryApi.updateUserGroup(self.data.group.identifier, self.data.group.identity, self.data.group.users, self.data.group.revision).subscribe(function (response) {
self.dialogRef.close();
- self.snackBarService.openCoaster({
- title: 'Success',
- message: 'Selected users have been added to the ' + self.data.group.identity + ' group.',
- verticalPosition: 'bottom',
- horizontalPosition: 'right',
- icon: 'fa fa-check-circle-o',
- color: '#1EB475',
- duration: 3000
- });
+ if (!response.status || response.status === 200) {
+ self.snackBarService.openCoaster({
+ title: 'Success',
+ message: 'Selected users have been added to the ' + self.data.group.identity + ' group.',
+ verticalPosition: 'bottom',
+ horizontalPosition: 'right',
+ icon: 'fa fa-check-circle-o',
+ color: '#1EB475',
+ duration: 3000
+ });
+ } else {
+ self.dialogService.openConfirm({
+ title: 'Error',
+ message: response.error,
+ acceptButton: 'Close',
+ acceptButtonColor: 'fds-warn'
+ });
+ }
});
},
@@ -241,6 +265,7 @@
TdDataTableService,
NfRegistryService,
MatDialogRef,
+ FdsDialogService,
FdsSnackBarService,
MAT_DIALOG_DATA
];
diff --git a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-users-to-group/nf-registry-add-users-to-group.spec.js b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-users-to-group/nf-registry-add-users-to-group.spec.js
index 0126892..f869f54 100644
--- a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-users-to-group/nf-registry-add-users-to-group.spec.js
+++ b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/dialogs/add-users-to-group/nf-registry-add-users-to-group.spec.js
@@ -19,13 +19,14 @@
import NfRegistryService from 'services/nf-registry.service';
import { of } from 'rxjs';
import { TdDataTableService } from '@covalent/core/data-table';
-import { FdsSnackBarService } from '@nifi-fds/core';
+import { FdsDialogService, FdsSnackBarService } from '@nifi-fds/core';
import NfRegistryAddUsersToGroup from 'components/administration/users/dialogs/add-users-to-group/nf-registry-add-users-to-group';
describe('NfRegistryAddUsersToGroup Component isolated unit tests', function () {
var comp;
var nfRegistryService;
var nfRegistryApi;
+ var dialogService;
var snackBarService;
var dataTableService;
@@ -36,14 +37,17 @@
nfRegistryService.users = [{identifier: 2, identity: 'User 1', checked: true}];
nfRegistryApi = new NfRegistryApi();
+ dialogService = new FdsDialogService();
snackBarService = new FdsSnackBarService();
dataTableService = new TdDataTableService();
comp = new NfRegistryAddUsersToGroup(nfRegistryApi, dataTableService, nfRegistryService, {
close: function () {
}
- }, snackBarService, {group: nfRegistryService.group});
+ }, dialogService, snackBarService, {group: nfRegistryService.group});
// Spy
+ spyOn(nfRegistryApi, 'getUsers').and.callFake(function () {
+ }).and.returnValue(of([{identifier: 2, identity: 'User 1', checked: true}]));
spyOn(nfRegistryApi, 'updateUserGroup').and.callFake(function () {
}).and.returnValue(of({identifier: 1, identity: 'Group 1'}));
spyOn(comp.dialogRef, 'close');
diff --git a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-group/nf-registry-manage-group.js b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-group/nf-registry-manage-group.js
index 836e186..614f030 100644
--- a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-group/nf-registry-manage-group.js
+++ b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-group/nf-registry-manage-group.js
@@ -145,7 +145,7 @@
// resource exists, let's update it
policy.userGroups.push(self.nfRegistryService.group);
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// can manage buckets privileges updated!!!...now update the view
response.userGroups.forEach(function (group) {
@@ -179,7 +179,7 @@
return (group.identifier !== self.nfRegistryService.group.identifier);
});
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// can manage buckets privileges updated!!!...now update the view
self.nfRegistryApi.getUserGroup(self.nfRegistryService.group.identifier).subscribe(function (response) {
@@ -229,7 +229,7 @@
// resource exists, let's update it
policy.userGroups.push(self.nfRegistryService.group);
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// can manage tenants privileges updated!!!...now update the view
response.userGroups.forEach(function (group) {
@@ -263,7 +263,7 @@
return (group.identifier !== self.nfRegistryService.group.identifier);
});
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// can manage tenants privileges updated!!!...now update the view
self.nfRegistryApi.getUserGroup(self.nfRegistryService.group.identifier).subscribe(function (response) {
@@ -313,7 +313,7 @@
// resource exists, let's update it
policy.userGroups.push(self.nfRegistryService.group);
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// can manage policies privileges updated!!!...now update the view
response.userGroups.forEach(function (group) {
@@ -347,7 +347,7 @@
return (group.identifier !== self.nfRegistryService.group.identifier);
});
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// can manage policies privileges updated!!!...now update the view
self.nfRegistryApi.getUserGroup(self.nfRegistryService.group.identifier).subscribe(function (response) {
@@ -397,7 +397,7 @@
// resource exists, let's update it
policy.userGroups.push(self.nfRegistryService.group);
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// can manage proxy privileges updated!!!...now update the view
response.userGroups.forEach(function (group) {
@@ -431,7 +431,7 @@
return (group.identifier !== self.nfRegistryService.group.identifier);
});
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// can manage proxy privileges updated!!!...now update the view
self.nfRegistryApi.getUserGroup(self.nfRegistryService.group.identifier).subscribe(function (response) {
@@ -461,9 +461,18 @@
}).afterClosed().subscribe(function () {
self.nfRegistryApi.getUserGroup(self.nfRegistryService.group.identifier)
.subscribe(function (response) {
- self.nfRegistryService.group = response;
- self.groupname = response.identity;
- self.filterUsers();
+ if (!response.status || response.status === 200) {
+ self.nfRegistryService.group = response;
+ self.groupname = response.identity;
+ self.filterUsers();
+
+ self.nfRegistryService.groups.filter(function (group) {
+ return self.nfRegistryService.group.identifier === group.identifier;
+ }).forEach(function (group) {
+ group.identity = response.identity;
+ group.revision = response.revision;
+ });
+ }
});
});
},
@@ -547,21 +556,30 @@
return u.identifier !== user.identifier;
});
- this.nfRegistryApi.updateUserGroup(this.nfRegistryService.group.identifier, this.nfRegistryService.group.identity, users).subscribe(function (response) {
+ this.nfRegistryApi.updateUserGroup(this.nfRegistryService.group.identifier, this.nfRegistryService.group.identity, users, this.nfRegistryService.group.revision).subscribe(function (response) {
self.nfRegistryApi.getUserGroup(self.nfRegistryService.group.identifier)
.subscribe(function (response) {
self.nfRegistryService.group = response;
self.filterUsers();
});
- self.snackBarService.openCoaster({
- title: 'Success',
- message: 'The user has been removed from the ' + self.nfRegistryService.group.identity + ' group.',
- verticalPosition: 'bottom',
- horizontalPosition: 'right',
- icon: 'fa fa-check-circle-o',
- color: '#1EB475',
- duration: 3000
- });
+ if (!response.status || response.status === 200) {
+ self.snackBarService.openCoaster({
+ title: 'Success',
+ message: 'The user has been removed from the ' + self.nfRegistryService.group.identity + ' group.',
+ verticalPosition: 'bottom',
+ horizontalPosition: 'right',
+ icon: 'fa fa-check-circle-o',
+ color: '#1EB475',
+ duration: 3000
+ });
+ } else {
+ self.dialogService.openConfirm({
+ title: 'Error',
+ message: response.error,
+ acceptButton: 'Ok',
+ acceptButtonColor: 'fds-warn'
+ });
+ }
});
},
@@ -572,13 +590,14 @@
*/
updateGroupName: function (groupname) {
var self = this;
- this.nfRegistryApi.updateUserGroup(this.nfRegistryService.group.identifier, groupname, this.nfRegistryService.group.users).subscribe(function (response) {
+ this.nfRegistryApi.updateUserGroup(this.nfRegistryService.group.identifier, groupname, this.nfRegistryService.group.users, this.nfRegistryService.group.revision).subscribe(function (response) {
if (!response.status || response.status === 200) {
self.nfRegistryService.group = response;
self.nfRegistryService.groups.filter(function (group) {
return self.nfRegistryService.group.identifier === group.identifier;
}).forEach(function (group) {
group.identity = response.identity;
+ group.revision = response.revision;
});
self.snackBarService.openCoaster({
title: 'Success',
@@ -597,6 +616,32 @@
acceptButton: 'Ok',
acceptButtonColor: 'fds-warn'
});
+ } else if (response.status === 404) {
+ self.dialogService.openConfirm({
+ title: 'Error',
+ message: response.error,
+ acceptButton: 'Ok',
+ acceptButtonColor: 'fds-warn'
+ });
+ } else {
+ self.groupname = self.nfRegistryService.group.identity;
+ self.dialogService.openConfirm({
+ title: 'Error',
+ message: response.error,
+ acceptButton: 'Ok',
+ acceptButtonColor: 'fds-warn'
+ }).afterClosed().subscribe(function (accept) {
+ self.nfRegistryApi.getUserGroup(self.nfRegistryService.group.identifier)
+ .subscribe(function (response) {
+ if (!response.status || response.status === 200) {
+ self.nfRegistryService.group = response;
+ self.groupname = response.identity;
+ self.filterUsers();
+ } else if (response.status === 404) {
+ self.router.navigateByUrl('administration/users');
+ }
+ });
+ });
}
});
},
diff --git a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-user/nf-registry-manage-user.js b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-user/nf-registry-manage-user.js
index ae8b565..c90bef2 100644
--- a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-user/nf-registry-manage-user.js
+++ b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/users/sidenav/manage-user/nf-registry-manage-user.js
@@ -145,7 +145,7 @@
// resource exists, let's update it
policy.users.push(self.nfRegistryService.user);
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// can manage buckets privileges updated!!!...now update the view
response.users.forEach(function (user) {
@@ -179,7 +179,7 @@
return (user.identifier !== self.nfRegistryService.user.identifier);
});
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// can manage buckets privileges updated!!!...now update the view
self.nfRegistryApi.getUser(self.nfRegistryService.user.identifier).subscribe(function (response) {
@@ -229,7 +229,7 @@
// resource exists, let's update it
policy.users.push(self.nfRegistryService.user);
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// can manage tenants privileges updated!!!...now update the view
response.users.forEach(function (user) {
@@ -263,7 +263,7 @@
return (user.identifier !== self.nfRegistryService.user.identifier);
});
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// can manage tenants privileges updated!!!...now update the view
self.nfRegistryApi.getUser(self.nfRegistryService.user.identifier).subscribe(function (response) {
@@ -313,7 +313,7 @@
// resource exists, let's update it
policy.users.push(self.nfRegistryService.user);
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// can manage policies privileges updated!!!...now update the view
response.users.forEach(function (user) {
@@ -347,7 +347,7 @@
return (user.identifier !== self.nfRegistryService.user.identifier);
});
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// can manage policies privileges updated!!!...now update the view
self.nfRegistryApi.getUser(self.nfRegistryService.user.identifier).subscribe(function (response) {
@@ -397,7 +397,7 @@
// resource exists, let's update it
policy.users.push(self.nfRegistryService.user);
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// can manage proxy privileges updated!!!...now update the view
response.users.forEach(function (user) {
@@ -431,7 +431,7 @@
return (user.identifier !== self.nfRegistryService.user.identifier);
});
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// administrator privileges updated!!!...now update the view
self.nfRegistryApi.getUser(self.nfRegistryService.user.identifier).subscribe(function (response) {
@@ -462,9 +462,18 @@
}).afterClosed().subscribe(function () {
self.nfRegistryApi.getUser(self.nfRegistryService.user.identifier)
.subscribe(function (response) {
- self.nfRegistryService.user = response;
- self.username = response.identity;
- self.filterGroups(this.sortBy, this.sortOrder);
+ if (!response.status || response.status === 200) {
+ self.nfRegistryService.user = response;
+ self.username = response.identity;
+ self.filterGroups(this.sortBy, this.sortOrder);
+
+ self.nfRegistryService.users.filter(function (user) {
+ return self.nfRegistryService.user.identifier === user.identifier;
+ }).forEach(function (user) {
+ user.identity = response.identity;
+ user.revision = response.revision;
+ });
+ }
});
});
},
@@ -550,21 +559,32 @@
var users = fullGroup.users.filter(function (user) {
return self.nfRegistryService.user.identifier !== user.identifier;
});
- self.nfRegistryApi.updateUserGroup(fullGroup.identifier, fullGroup.identity, users).subscribe(function (response) {
+ self.nfRegistryApi.updateUserGroup(fullGroup.identifier, fullGroup.identity, users, fullGroup.revision).subscribe(function (response) {
self.nfRegistryApi.getUser(self.nfRegistryService.user.identifier)
.subscribe(function (response) {
self.nfRegistryService.user = response;
+ self.username = response.identity;
self.filterGroups(this.sortBy, this.sortOrder);
});
- self.snackBarService.openCoaster({
- title: 'Success',
- message: 'This user has been removed from the ' + group.identity + ' group.',
- verticalPosition: 'bottom',
- horizontalPosition: 'right',
- icon: 'fa fa-check-circle-o',
- color: '#1EB475',
- duration: 3000
- });
+
+ if (!response.status || response.status === 200) {
+ self.snackBarService.openCoaster({
+ title: 'Success',
+ message: 'This user has been removed from the ' + group.identity + ' group.',
+ verticalPosition: 'bottom',
+ horizontalPosition: 'right',
+ icon: 'fa fa-check-circle-o',
+ color: '#1EB475',
+ duration: 3000
+ });
+ } else {
+ self.dialogService.openConfirm({
+ title: 'Error',
+ message: response.error,
+ acceptButton: 'Ok',
+ acceptButtonColor: 'fds-warn'
+ });
+ }
});
}
});
@@ -577,13 +597,14 @@
*/
updateUserName: function (username) {
var self = this;
- this.nfRegistryApi.updateUser(this.nfRegistryService.user.identifier, username).subscribe(function (response) {
+ this.nfRegistryApi.updateUser(this.nfRegistryService.user.identifier, username, this.nfRegistryService.user.revision).subscribe(function (response) {
if (!response.status || response.status === 200) {
self.nfRegistryService.user = response;
self.nfRegistryService.users.filter(function (user) {
return self.nfRegistryService.user.identifier === user.identifier;
}).forEach(function (user) {
user.identity = response.identity;
+ user.revision = response.revision;
});
self.snackBarService.openCoaster({
title: 'Success',
@@ -602,6 +623,32 @@
acceptButton: 'Ok',
acceptButtonColor: 'fds-warn'
});
+ } else if (response.status === 404) {
+ self.dialogService.openConfirm({
+ title: 'Error',
+ message: response.error,
+ acceptButton: 'Ok',
+ acceptButtonColor: 'fds-warn'
+ });
+ } else {
+ self.username = self.nfRegistryService.user.identity;
+ self.dialogService.openConfirm({
+ title: 'Error',
+ message: response.error,
+ acceptButton: 'Ok',
+ acceptButtonColor: 'fds-warn'
+ }).afterClosed().subscribe(function (accept) {
+ self.nfRegistryApi.getUser(self.nfRegistryService.user.identifier)
+ .subscribe(function (response) {
+ if (!response.status || response.status === 200) {
+ self.nfRegistryService.user = response;
+ self.username = response.identity;
+ self.filterGroups(this.sortBy, this.sortOrder);
+ } else if (response.status === 404) {
+ self.router.navigateByUrl('administration/users');
+ }
+ });
+ });
}
});
},
diff --git a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/dialogs/add-policy-to-bucket/nf-registry-add-policy-to-bucket.js b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/dialogs/add-policy-to-bucket/nf-registry-add-policy-to-bucket.js
index a437b08..4edfc0a 100644
--- a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/dialogs/add-policy-to-bucket/nf-registry-add-policy-to-bucket.js
+++ b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/dialogs/add-policy-to-bucket/nf-registry-add-policy-to-bucket.js
@@ -200,7 +200,7 @@
policy.userGroups.push(self.userOrGroup);
}
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// policy updated!!!...now update the view
self.nfRegistryApi.getBucket(self.nfRegistryService.bucket.identifier).subscribe(function (response) {
@@ -261,7 +261,7 @@
policy.userGroups.push(self.userOrGroup);
}
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// policy updated!!!...now update the view
self.nfRegistryApi.getBucket(self.nfRegistryService.bucket.identifier).subscribe(function (response) {
@@ -322,7 +322,7 @@
policy.userGroups.push(self.userOrGroup);
}
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// policy updated!!!...now update the view
self.nfRegistryApi.getBucket(self.nfRegistryService.bucket.identifier).subscribe(function (response) {
diff --git a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/dialogs/edit-bucket-policy/nf-registry-edit-bucket-policy.js b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/dialogs/edit-bucket-policy/nf-registry-edit-bucket-policy.js
index dfda7b9..206ba7f 100644
--- a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/dialogs/edit-bucket-policy/nf-registry-edit-bucket-policy.js
+++ b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/dialogs/edit-bucket-policy/nf-registry-edit-bucket-policy.js
@@ -130,7 +130,7 @@
policy.userGroups.push(self.userOrGroup);
}
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// policy updated!!!...now update the view
self.nfRegistryApi.getBucket(self.nfRegistryService.bucket.identifier).subscribe(function (response) {
@@ -158,7 +158,7 @@
});
}
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// policy updated!!!...now update the view
self.nfRegistryApi.getBucket(self.nfRegistryService.bucket.identifier).subscribe(function (response) {
@@ -201,7 +201,7 @@
policy.userGroups.push(self.userOrGroup);
}
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// policy updated!!!...now update the view
self.nfRegistryApi.getBucket(self.nfRegistryService.bucket.identifier).subscribe(function (response) {
@@ -229,7 +229,7 @@
});
}
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// policy updated!!!...now update the view
self.nfRegistryApi.getBucket(self.nfRegistryService.bucket.identifier).subscribe(function (response) {
@@ -272,7 +272,7 @@
policy.userGroups.push(self.userOrGroup);
}
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// policy updated!!!...now update the view
self.nfRegistryApi.getBucket(self.nfRegistryService.bucket.identifier).subscribe(function (response) {
@@ -300,7 +300,7 @@
});
}
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// policy updated!!!...now update the view
self.nfRegistryApi.getBucket(self.nfRegistryService.bucket.identifier).subscribe(function (response) {
diff --git a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/sidenav/manage-bucket/nf-registry-manage-bucket.js b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/sidenav/manage-bucket/nf-registry-manage-bucket.js
index f4efc49..0478b4e 100644
--- a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/sidenav/manage-bucket/nf-registry-manage-bucket.js
+++ b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/components/administration/workflow/sidenav/manage-bucket/nf-registry-manage-bucket.js
@@ -355,7 +355,7 @@
return (group.identity !== userOrGroup.identity);
});
self.nfRegistryApi.putPolicyActionResource(policy.identifier, policy.action,
- policy.resource, policy.users, policy.userGroups).subscribe(
+ policy.resource, policy.users, policy.userGroups, policy.revision).subscribe(
function (response) {
// policy removed!!!...now update the view
self.nfRegistryApi.getPolicies().subscribe(function (response) {
@@ -412,14 +412,16 @@
this.nfRegistryApi.updateBucket({
'identifier': this.nfRegistryService.bucket.identifier,
'name': bucketname,
+ 'revision': this.nfRegistryService.bucket.revision
}).subscribe(function (response) {
if (!response.status || response.status === 200) {
self.nfRegistryService.bucket = response;
- // update the bucket identity in the buckets table
+ // update the bucket identity and revision in the buckets table
self.nfRegistryService.buckets.filter(function (bucket) {
return self.nfRegistryService.bucket.identifier === bucket.identifier;
}).forEach(function (bucket) {
bucket.name = response.name;
+ bucket.revision = response.revision;
});
self.snackBarService.openCoaster({
title: 'Success',
@@ -441,17 +443,32 @@
acceptButton: 'Ok',
acceptButtonColor: 'fds-warn'
});
- } else if (response.status === 400) {
- self.bucketname = self.nfRegistryService.bucket.name;
- self.allowBundleRedeploy = self.nfRegistryService.bucket.allowBundleRedeploy;
- self.allowPublicRead = self.nfRegistryService.bucket.allowPublicRead;
-
+ } else if (response.status === 404) {
self.dialogService.openConfirm({
title: 'Error',
message: response.error,
acceptButton: 'Ok',
acceptButtonColor: 'fds-warn'
});
+ } else {
+ self.dialogService.openConfirm({
+ title: 'Error',
+ message: response.error,
+ acceptButton: 'Ok',
+ acceptButtonColor: 'fds-warn'
+ }).afterClosed().subscribe(function (accept) {
+ self.nfRegistryApi.getBucket(self.nfRegistryService.bucket.identifier)
+ .subscribe(function (response) {
+ if (!response.status || response.status === 200) {
+ self.nfRegistryService.bucket = response;
+ self.bucketname = self.nfRegistryService.bucket.name;
+ self.allowBundleRedeploy = self.nfRegistryService.bucket.allowBundleRedeploy;
+ self.allowPublicRead = self.nfRegistryService.bucket.allowPublicRead;
+ } else if (response.status === 404) {
+ self.router.navigateByUrl('administration/workflow');
+ }
+ });
+ });
}
});
},
@@ -466,9 +483,16 @@
this.nfRegistryApi.updateBucket({
'identifier': this.nfRegistryService.bucket.identifier,
'allowBundleRedeploy': event.checked,
+ 'revision': this.nfRegistryService.bucket.revision
}).subscribe(function (response) {
if (!response.status || response.status === 200) {
self.nfRegistryService.bucket = response;
+ // update the bucket revision in the buckets table
+ self.nfRegistryService.buckets.filter(function (bucket) {
+ return self.nfRegistryService.bucket.identifier === bucket.identifier;
+ }).forEach(function (bucket) {
+ bucket.revision = response.revision;
+ });
self.snackBarService.openCoaster({
title: 'Success',
message: 'Bundle settings have been updated.',
@@ -478,14 +502,32 @@
color: '#1EB475',
duration: 3000
});
- } else if (response.status === 400) {
- self.allowBundleRedeploy = !event.checked;
+ } else if (response.status === 404) {
self.dialogService.openConfirm({
title: 'Error',
message: response.error,
acceptButton: 'Ok',
acceptButtonColor: 'fds-warn'
});
+ } else {
+ self.dialogService.openConfirm({
+ title: 'Error',
+ message: response.error,
+ acceptButton: 'Ok',
+ acceptButtonColor: 'fds-warn'
+ }).afterClosed().subscribe(function (accept) {
+ self.nfRegistryApi.getBucket(self.nfRegistryService.bucket.identifier)
+ .subscribe(function (response) {
+ if (!response.status || response.status === 200) {
+ self.nfRegistryService.bucket = response;
+ self.bucketname = self.nfRegistryService.bucket.name;
+ self.allowBundleRedeploy = self.nfRegistryService.bucket.allowBundleRedeploy;
+ self.allowPublicRead = self.nfRegistryService.bucket.allowPublicRead;
+ } else if (response.status === 404) {
+ self.router.navigateByUrl('administration/workflow');
+ }
+ });
+ });
}
});
},
@@ -500,9 +542,16 @@
this.nfRegistryApi.updateBucket({
'identifier': this.nfRegistryService.bucket.identifier,
'allowPublicRead': event.checked,
+ 'revision': this.nfRegistryService.bucket.revision
}).subscribe(function (response) {
if (!response.status || response.status === 200) {
self.nfRegistryService.bucket = response;
+ // update the bucket revision in the buckets table
+ self.nfRegistryService.buckets.filter(function (bucket) {
+ return self.nfRegistryService.bucket.identifier === bucket.identifier;
+ }).forEach(function (bucket) {
+ bucket.revision = response.revision;
+ });
self.snackBarService.openCoaster({
title: 'Success',
message: 'Bucket settings have been updated.',
@@ -512,14 +561,32 @@
color: '#1EB475',
duration: 3000
});
- } else if (response.status === 400) {
- self.allowPublicRead = !event.checked;
+ } else if (response.status === 404) {
self.dialogService.openConfirm({
title: 'Error',
message: response.error,
acceptButton: 'Ok',
acceptButtonColor: 'fds-warn'
});
+ } else {
+ self.dialogService.openConfirm({
+ title: 'Error',
+ message: response.error,
+ acceptButton: 'Ok',
+ acceptButtonColor: 'fds-warn'
+ }).afterClosed().subscribe(function (accept) {
+ self.nfRegistryApi.getBucket(self.nfRegistryService.bucket.identifier)
+ .subscribe(function (response) {
+ if (!response.status || response.status === 200) {
+ self.nfRegistryService.bucket = response;
+ self.bucketname = self.nfRegistryService.bucket.name;
+ self.allowBundleRedeploy = self.nfRegistryService.bucket.allowBundleRedeploy;
+ self.allowPublicRead = self.nfRegistryService.bucket.allowPublicRead;
+ } else if (response.status === 404) {
+ self.router.navigateByUrl('administration/workflow');
+ }
+ });
+ });
}
});
},
diff --git a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.api.js b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.api.js
index 8ec0c6a..676824e 100644
--- a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.api.js
+++ b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.api.js
@@ -162,7 +162,13 @@
*/
createBucket: function (name, allowPublicRead) {
var self = this;
- return this.http.post('../nifi-registry-api/buckets', {'name': name, 'allowPublicRead': allowPublicRead}, headers).pipe(
+ return this.http.post('../nifi-registry-api/buckets', {
+ 'name': name,
+ 'allowPublicRead': allowPublicRead,
+ 'revision': {
+ 'version': 0
+ }
+ }, headers).pipe(
map(function (response) {
return response;
}),
@@ -182,11 +188,12 @@
* Delete an existing bucket in the registry, along with all the objects it is storing.
*
* @param {string} bucketId The identifier of the bucket to be deleted.
+ * @param {int} version The version from the revision of the bucket
* @returns {*}
*/
- deleteBucket: function (bucketId) {
+ deleteBucket: function (bucketId, version) {
var self = this;
- return this.http.delete('../nifi-registry-api/buckets/' + bucketId, headers).pipe(
+ return this.http.delete('../nifi-registry-api/buckets/' + bucketId + '?version=' + version, headers).pipe(
map(function (response) {
return response;
}),
@@ -331,6 +338,9 @@
canWrite: false,
canDelete: false
}
+ },
+ revision: {
+ version: 0
}
}, headers).pipe(
map(function (response) {
@@ -353,12 +363,14 @@
*
* @param {string} identifier The identifier of the user.
* @param {string} identity The identity of the user.
+ * @param {string} revision The revision of the user.
* @returns {*}
*/
- updateUser: function (identifier, identity) {
+ updateUser: function (identifier, identity, revision) {
return this.http.put('../nifi-registry-api/tenants/users/' + identifier, {
'identifier': identifier,
- 'identity': identity
+ 'identity': identity,
+ 'revision': revision
}, headers).pipe(
map(function (response) {
return response;
@@ -396,11 +408,12 @@
* Delete an existing user from the registry.
*
* @param {string} userId The identifier of the user to be deleted.
+ * @param {int} version The version from the user's revision
* @returns {*}
*/
- deleteUser: function (userId) {
+ deleteUser: function (userId, version) {
var self = this;
- return this.http.delete('../nifi-registry-api/tenants/users/' + userId, headers).pipe(
+ return this.http.delete('../nifi-registry-api/tenants/users/' + userId + '?version=' + version, headers).pipe(
map(function (response) {
return response;
}),
@@ -467,11 +480,12 @@
* Delete an existing user group from the registry.
*
* @param {string} userGroupId The identifier of the user group to be deleted.
+ * @param {int} version The version from the group's revision
* @returns {*}
*/
- deleteUserGroup: function (userGroupId) {
+ deleteUserGroup: function (userGroupId, version) {
var self = this;
- return this.http.delete('../nifi-registry-api/tenants/user-groups/' + userGroupId, headers).pipe(
+ return this.http.delete('../nifi-registry-api/tenants/user-groups/' + userGroupId + '?version=' + version, headers).pipe(
map(function (response) {
return response;
}),
@@ -500,7 +514,10 @@
return this.http.post('../nifi-registry-api/tenants/user-groups', {
'identifier': identifier,
'identity': identity,
- 'users': users
+ 'users': users,
+ 'revision': {
+ 'version': 0
+ }
}, headers).pipe(
map(function (response) {
return response;
@@ -522,14 +539,16 @@
*
* @param {string} identifier The identifier of the group.
* @param {string} identity The identity of the group.
- * @param {array} users The array of users in the new group.
+ * @param {array} users The array of users in the new group.
+ * @param {string} revision The revision of the group.
* @returns {*}
*/
- updateUserGroup: function (identifier, identity, users) {
+ updateUserGroup: function (identifier, identity, users, revision) {
return this.http.put('../nifi-registry-api/tenants/user-groups/' + identifier, {
'identifier': identifier,
'identity': identity,
- 'users': users
+ 'users': users,
+ 'revision': revision
}, headers).pipe(
map(function (response) {
return response;
@@ -604,14 +623,15 @@
* @param {string} userGroups The user groups with resource privileges.
* @returns {*}
*/
- putPolicyActionResource: function (identifier, action, resource, users, userGroups) {
+ putPolicyActionResource: function (identifier, action, resource, users, userGroups, revision) {
var self = this;
return this.http.put('../nifi-registry-api/policies/' + identifier, {
'identifier': identifier,
'resource': resource,
'action': action,
'users': users,
- 'userGroups': userGroups
+ 'userGroups': userGroups,
+ 'revision': revision
}, headers).pipe(
map(function (response) {
return response;
@@ -643,7 +663,10 @@
'resource': resource,
'action': action,
'users': users,
- 'userGroups': userGroups
+ 'userGroups': userGroups,
+ 'revision': {
+ 'version': 0
+ }
}, headers).pipe(
map(function (response) {
return response;
diff --git a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.api.spec.js b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.api.spec.js
index e13be06..b27ee11 100644
--- a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.api.spec.js
+++ b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.api.spec.js
@@ -438,10 +438,10 @@
it('should DELETE a bucket.', inject([HttpTestingController], function (httpMock) {
// api call
- nfRegistryApi.deleteBucket('1234').subscribe(function (response) {
+ nfRegistryApi.deleteBucket('1234', 0).subscribe(function (response) {
});
// the request it made
- req = httpMock.expectOne('../nifi-registry-api/buckets/1234');
+ req = httpMock.expectOne('../nifi-registry-api/buckets/1234?version=0');
expect(req.request.method).toEqual('DELETE');
// Next, fulfill the request by transmitting a response.
@@ -457,20 +457,20 @@
});
// api call
- nfRegistryApi.deleteBucket('1234').subscribe(function (response) {
- expect(response.message).toEqual('Http failure response for ../nifi-registry-api/buckets/1234: 401 DELETE bucket mock error');
+ nfRegistryApi.deleteBucket('1234', 0).subscribe(function (response) {
+ expect(response.message).toEqual('Http failure response for ../nifi-registry-api/buckets/1234?version=0: 401 DELETE bucket mock error');
var dialogServiceCall = nfRegistryApi.dialogService.openConfirm.calls.first();
expect(dialogServiceCall.args[0].title).toBe('Error');
- expect(dialogServiceCall.args[0].message).toBe('Http failure response for ../nifi-registry-api/buckets/1234: 401 DELETE bucket mock error');
+ expect(dialogServiceCall.args[0].message).toBe('Http failure response for ../nifi-registry-api/buckets/1234?version=0: 401 DELETE bucket mock error');
expect(dialogServiceCall.args[0].acceptButton).toBe('Ok');
expect(dialogServiceCall.args[0].acceptButtonColor).toBe('fds-warn');
});
// the request it made
- req = httpMock.expectOne('../nifi-registry-api/buckets/1234');
+ req = httpMock.expectOne('../nifi-registry-api/buckets/1234?version=0');
expect(req.request.method).toEqual('DELETE');
// Next, fulfill the request by transmitting a response.
- req.flush('Http failure response for ../nifi-registry-api/buckets/1234: 401 DELETE bucket mock error', {status: 401, statusText: 'DELETE bucket mock error'});
+ req.flush('Http failure response for ../nifi-registry-api/buckets/1234?version=0: 401 DELETE bucket mock error', {status: 401, statusText: 'DELETE bucket mock error'});
// Finally, assert that there are no outstanding requests.
httpMock.verify();
@@ -799,12 +799,12 @@
it('should DELETE users.', inject([HttpTestingController], function (httpMock) {
// api call
- nfRegistryApi.deleteUser(123).subscribe(function (response) {
+ nfRegistryApi.deleteUser(123, 0).subscribe(function (response) {
expect(response.identity).toEqual('User #1');
});
// the request it made
- req = httpMock.expectOne('../nifi-registry-api/tenants/users/123');
+ req = httpMock.expectOne('../nifi-registry-api/tenants/users/123?version=0');
expect(req.request.method).toEqual('DELETE');
// Next, fulfill the request by transmitting a response.
@@ -823,21 +823,21 @@
});
// api call
- nfRegistryApi.deleteUser(123).subscribe(function (response) {
- expect(response.message).toEqual('Http failure response for ../nifi-registry-api/tenants/users/123: 401 DELETE users mock error');
+ nfRegistryApi.deleteUser(123, 0).subscribe(function (response) {
+ expect(response.message).toEqual('Http failure response for ../nifi-registry-api/tenants/users/123?version=0: 401 DELETE users mock error');
var dialogServiceCall = nfRegistryApi.dialogService.openConfirm.calls.first();
expect(dialogServiceCall.args[0].title).toBe('Error');
- expect(dialogServiceCall.args[0].message).toBe('Http failure response for ../nifi-registry-api/tenants/users/123: 401 DELETE users mock error');
+ expect(dialogServiceCall.args[0].message).toBe('Http failure response for ../nifi-registry-api/tenants/users/123?version=0: 401 DELETE users mock error');
expect(dialogServiceCall.args[0].acceptButton).toBe('Ok');
expect(dialogServiceCall.args[0].acceptButtonColor).toBe('fds-warn');
});
// the request it made
- req = httpMock.expectOne('../nifi-registry-api/tenants/users/123');
+ req = httpMock.expectOne('../nifi-registry-api/tenants/users/123?version=0');
expect(req.request.method).toEqual('DELETE');
// Next, fulfill the request by transmitting a response.
- req.flush('Http failure response for ../nifi-registry-api/tenants/users/123: 401 DELETE users mock error', {status: 401, statusText: 'DELETE users mock error'});
+ req.flush('Http failure response for ../nifi-registry-api/tenants/users/123?version=0: 401 DELETE users mock error', {status: 401, statusText: 'DELETE users mock error'});
// Finally, assert that there are no outstanding requests.
httpMock.verify();
@@ -1188,12 +1188,12 @@
it('should DELETE a user group.', inject([HttpTestingController], function (httpMock) {
// api call
- nfRegistryApi.deleteUserGroup(123).subscribe(function (response) {
+ nfRegistryApi.deleteUserGroup(123, 0).subscribe(function (response) {
expect(response.identity).toEqual('Group #1');
});
// the request it made
- req = httpMock.expectOne('../nifi-registry-api/tenants/user-groups/123');
+ req = httpMock.expectOne('../nifi-registry-api/tenants/user-groups/123?version=0');
expect(req.request.method).toEqual('DELETE');
// Next, fulfill the request by transmitting a response.
@@ -1212,21 +1212,21 @@
});
// api call
- nfRegistryApi.deleteUserGroup(123).subscribe(function (response) {
- expect(response.message).toEqual('Http failure response for ../nifi-registry-api/tenants/user-groups/123: 401 DELETE user groups mock error');
+ nfRegistryApi.deleteUserGroup(123, 0).subscribe(function (response) {
+ expect(response.message).toEqual('Http failure response for ../nifi-registry-api/tenants/user-groups/123?version=0: 401 DELETE user groups mock error');
var dialogServiceCall = nfRegistryApi.dialogService.openConfirm.calls.first();
expect(dialogServiceCall.args[0].title).toBe('Error');
- expect(dialogServiceCall.args[0].message).toBe('Http failure response for ../nifi-registry-api/tenants/user-groups/123: 401 DELETE user groups mock error');
+ expect(dialogServiceCall.args[0].message).toBe('Http failure response for ../nifi-registry-api/tenants/user-groups/123?version=0: 401 DELETE user groups mock error');
expect(dialogServiceCall.args[0].acceptButton).toBe('Ok');
expect(dialogServiceCall.args[0].acceptButtonColor).toBe('fds-warn');
});
// the request it made
- req = httpMock.expectOne('../nifi-registry-api/tenants/user-groups/123');
+ req = httpMock.expectOne('../nifi-registry-api/tenants/user-groups/123?version=0');
expect(req.request.method).toEqual('DELETE');
// Next, fulfill the request by transmitting a response.
- req.flush('Http failure response for ../nifi-registry-api/tenants/user-groups/123: 401 DELETE user groups mock error', {status: 401, statusText: 'DELETE user groups mock error'});
+ req.flush('Http failure response for ../nifi-registry-api/tenants/user-groups/123?version=0: 401 DELETE user groups mock error', {status: 401, statusText: 'DELETE user groups mock error'});
// Finally, assert that there are no outstanding requests.
httpMock.verify();
diff --git a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.service.js b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.service.js
index 7999978..c0c65d7 100644
--- a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.service.js
+++ b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.service.js
@@ -410,21 +410,27 @@
}).afterClosed().subscribe(
function (accept) {
if (accept) {
- self.api.deleteDroplet(droplet.link.href).subscribe(function (response) {
- self.droplets = self.droplets.filter(function (d) {
- return (d.identifier !== droplet.identifier);
- });
- self.snackBarService.openCoaster({
- title: 'Success',
- message: 'All versions of this ' + droplet.type.toLowerCase() + ' have been deleted.',
- verticalPosition: 'bottom',
- horizontalPosition: 'right',
- icon: 'fa fa-check-circle-o',
- color: '#1EB475',
- duration: 3000
- });
- self.droplet = {};
- self.filterDroplets();
+ var deleteUrl = droplet.link.href;
+ if (droplet.type === 'Flow') {
+ deleteUrl = deleteUrl + '?version=' + droplet.revision.version;
+ }
+ self.api.deleteDroplet(deleteUrl).subscribe(function (response) {
+ if (!response.status || response.status === 200) {
+ self.droplets = self.droplets.filter(function (d) {
+ return (d.identifier !== droplet.identifier);
+ });
+ self.snackBarService.openCoaster({
+ title: 'Success',
+ message: 'All versions of this ' + droplet.type.toLowerCase() + ' have been deleted.',
+ verticalPosition: 'bottom',
+ horizontalPosition: 'right',
+ icon: 'fa fa-check-circle-o',
+ color: '#1EB475',
+ duration: 3000
+ });
+ self.droplet = {};
+ self.filterDroplets();
+ }
});
}
}
@@ -544,22 +550,24 @@
}).afterClosed().subscribe(
function (accept) {
if (accept) {
- self.api.deleteBucket(bucket.identifier).subscribe(function (response) {
- self.buckets = self.buckets.filter(function (b) {
- return b.identifier !== bucket.identifier;
- });
- self.snackBarService.openCoaster({
- title: 'Success',
- message: 'All versions of all items in this bucket, as well as the bucket, have been deleted.',
- verticalPosition: 'bottom',
- horizontalPosition: 'right',
- icon: 'fa fa-check-circle-o',
- color: '#1EB475',
- duration: 3000
- });
- self.bucket = {};
- self.filterBuckets();
- self.determineAllBucketsSelectedState();
+ self.api.deleteBucket(bucket.identifier, bucket.revision.version).subscribe(function (response) {
+ if (!response.status || response.status === 200) {
+ self.buckets = self.buckets.filter(function (b) {
+ return b.identifier !== bucket.identifier;
+ });
+ self.snackBarService.openCoaster({
+ title: 'Success',
+ message: 'All versions of all items in this bucket, as well as the bucket, have been deleted.',
+ verticalPosition: 'bottom',
+ horizontalPosition: 'right',
+ icon: 'fa fa-check-circle-o',
+ color: '#1EB475',
+ duration: 3000
+ });
+ self.bucket = {};
+ self.filterBuckets();
+ self.determineAllBucketsSelectedState();
+ }
});
}
}
@@ -773,20 +781,22 @@
if (accept) {
self.filteredBuckets.forEach(function (filteredBucket) {
if (filteredBucket.checked) {
- self.api.deleteBucket(filteredBucket.identifier).subscribe(function (response) {
- self.buckets = self.buckets.filter(function (bucket) {
- return bucket.identifier !== filteredBucket.identifier;
- });
- self.snackBarService.openCoaster({
- title: 'Success',
- message: 'All versions of all items in ' + filteredBucket.name + ' have been deleted.',
- verticalPosition: 'bottom',
- horizontalPosition: 'right',
- icon: 'fa fa-check-circle-o',
- color: '#1EB475',
- duration: 3000
- });
- self.filterBuckets();
+ self.api.deleteBucket(filteredBucket.identifier, filteredBucket.revision.version).subscribe(function (response) {
+ if (!response.status || response.status === 200) {
+ self.buckets = self.buckets.filter(function (bucket) {
+ return bucket.identifier !== filteredBucket.identifier;
+ });
+ self.snackBarService.openCoaster({
+ title: 'Success',
+ message: 'All versions of all items in ' + filteredBucket.name + ' have been deleted.',
+ verticalPosition: 'bottom',
+ horizontalPosition: 'right',
+ icon: 'fa fa-check-circle-o',
+ color: '#1EB475',
+ duration: 3000
+ });
+ self.filterBuckets();
+ }
});
}
});
@@ -1018,21 +1028,23 @@
}).afterClosed().subscribe(
function (accept) {
if (accept) {
- self.api.deleteUser(user.identifier).subscribe(function (response) {
- self.users = self.users.filter(function (u) {
- return u.identifier !== user.identifier;
- });
- self.snackBarService.openCoaster({
- title: 'Success',
- message: 'User: ' + user.identity + ' has been deleted.',
- verticalPosition: 'bottom',
- horizontalPosition: 'right',
- icon: 'fa fa-check-circle-o',
- color: '#1EB475',
- duration: 3000
- });
- self.filterUsersAndGroups();
- self.determineAllUsersAndGroupsSelectedState();
+ self.api.deleteUser(user.identifier, user.revision.version).subscribe(function (response) {
+ if (!response.status || response.status === 200) {
+ self.users = self.users.filter(function (u) {
+ return u.identifier !== user.identifier;
+ });
+ self.snackBarService.openCoaster({
+ title: 'Success',
+ message: 'User: ' + user.identity + ' has been deleted.',
+ verticalPosition: 'bottom',
+ horizontalPosition: 'right',
+ icon: 'fa fa-check-circle-o',
+ color: '#1EB475',
+ duration: 3000
+ });
+ self.filterUsersAndGroups();
+ self.determineAllUsersAndGroupsSelectedState();
+ }
});
}
}
@@ -1065,21 +1077,23 @@
}).afterClosed().subscribe(
function (accept) {
if (accept) {
- self.api.deleteUserGroup(group.identifier).subscribe(function (response) {
- self.groups = self.groups.filter(function (u) {
- return u.identifier !== group.identifier;
- });
- self.snackBarService.openCoaster({
- title: 'Success',
- message: 'Group: ' + group.identity + ' has been deleted.',
- verticalPosition: 'bottom',
- horizontalPosition: 'right',
- icon: 'fa fa-check-circle-o',
- color: '#1EB475',
- duration: 3000
- });
- self.filterUsersAndGroups();
- self.determineAllUsersAndGroupsSelectedState();
+ self.api.deleteUserGroup(group.identifier, group.revision.version).subscribe(function (response) {
+ if (!response.status || response.status === 200) {
+ self.groups = self.groups.filter(function (u) {
+ return u.identifier !== group.identifier;
+ });
+ self.snackBarService.openCoaster({
+ title: 'Success',
+ message: 'Group: ' + group.identity + ' has been deleted.',
+ verticalPosition: 'bottom',
+ horizontalPosition: 'right',
+ icon: 'fa fa-check-circle-o',
+ color: '#1EB475',
+ duration: 3000
+ });
+ self.filterUsersAndGroups();
+ self.determineAllUsersAndGroupsSelectedState();
+ }
});
}
}
@@ -1110,39 +1124,43 @@
if (accept) {
self.filteredUserGroups.forEach(function (filteredUserGroup) {
if (filteredUserGroup.checked) {
- self.api.deleteUserGroup(filteredUserGroup.identifier).subscribe(function (response) {
- self.groups = self.groups.filter(function (u) {
- return u.identifier !== filteredUserGroup.identifier;
- });
- self.snackBarService.openCoaster({
- title: 'Success',
- message: 'User group: ' + filteredUserGroup.identity + ' has been deleted.',
- verticalPosition: 'bottom',
- horizontalPosition: 'right',
- icon: 'fa fa-check-circle-o',
- color: '#1EB475',
- duration: 3000
- });
- self.filterUsersAndGroups();
+ self.api.deleteUserGroup(filteredUserGroup.identifier, filteredUserGroup.revision.version).subscribe(function (response) {
+ if (!response.status || response.status === 200) {
+ self.groups = self.groups.filter(function (u) {
+ return u.identifier !== filteredUserGroup.identifier;
+ });
+ self.snackBarService.openCoaster({
+ title: 'Success',
+ message: 'User group: ' + filteredUserGroup.identity + ' has been deleted.',
+ verticalPosition: 'bottom',
+ horizontalPosition: 'right',
+ icon: 'fa fa-check-circle-o',
+ color: '#1EB475',
+ duration: 3000
+ });
+ self.filterUsersAndGroups();
+ }
});
}
});
self.filteredUsers.forEach(function (filteredUser) {
if (filteredUser.checked) {
- self.api.deleteUser(filteredUser.identifier).subscribe(function (response) {
- self.users = self.users.filter(function (u) {
- return u.identifier !== filteredUser.identifier;
- });
- self.snackBarService.openCoaster({
- title: 'Success',
- message: 'User: ' + filteredUser.identity + ' has been deleted.',
- verticalPosition: 'bottom',
- horizontalPosition: 'right',
- icon: 'fa fa-check-circle-o',
- color: '#1EB475',
- duration: 3000
- });
- self.filterUsersAndGroups();
+ self.api.deleteUser(filteredUser.identifier, filteredUser.revision.version).subscribe(function (response) {
+ if (!response.status || response.status === 200) {
+ self.users = self.users.filter(function (u) {
+ return u.identifier !== filteredUser.identifier;
+ });
+ self.snackBarService.openCoaster({
+ title: 'Success',
+ message: 'User: ' + filteredUser.identity + ' has been deleted.',
+ verticalPosition: 'bottom',
+ horizontalPosition: 'right',
+ icon: 'fa fa-check-circle-o',
+ color: '#1EB475',
+ duration: 3000
+ });
+ self.filterUsersAndGroups();
+ }
});
}
});
diff --git a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.service.spec.js b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.service.spec.js
index b70de9b..455963d 100644
--- a/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.service.spec.js
+++ b/nifi-registry-core/nifi-registry-web-ui/src/main/webapp/services/nf-registry.service.spec.js
@@ -795,10 +795,10 @@
}).and.returnValue(of({identifier: '2e04b4fb-9513-47bb-aa74-1ae34616bfdc', link: null}));
// object to be updated by the test
- const bucket = {identifier: '999'};
+ const bucket = {identifier: '999', revision: { version: 0}};
// set up the bucket to be deleted
- nfRegistryService.buckets = [bucket, {identifier: 1}];
+ nfRegistryService.buckets = [bucket, {identifier: 1, revision: { version: 0}}];
// The function to test
nfRegistryService.executeBucketAction({name: 'delete'}, bucket);
@@ -847,10 +847,10 @@
}).and.returnValue(of({identifier: '2e04b4fb-9513-47bb-aa74-1ae34616bfdc', link: null}));
// object to be updated by the test
- const user = {identifier: '999'};
+ const user = {identifier: '999', revision: {version: 0}};
// set up the user to be deleted
- nfRegistryService.users = [user, {identifier: 1}];
+ nfRegistryService.users = [user, {identifier: 1, revision: { version: 0}}];
// The function to test
nfRegistryService.executeUserAction({name: 'delete'}, user);
@@ -899,10 +899,10 @@
}).and.returnValue(of({identifier: '2e04b4fb-9513-47bb-aa74-1ae34616bfdc', link: null}));
// object to be updated by the test
- const group = {identifier: '999'};
+ const group = {identifier: '999', revision: {version: 0}};
// set up the user to be deleted
- nfRegistryService.groups = [group, {identifier: 1}];
+ nfRegistryService.groups = [group, {identifier: 1, revision: {version: 0}}];
// The function to test
nfRegistryService.executeGroupAction({name: 'delete'}, group);
@@ -1023,10 +1023,10 @@
}).and.returnValue(of({identifier: 999, link: null}));
// object to be updated by the test
- const bucket = {identifier: 999, checked: true};
+ const bucket = {identifier: 999, checked: true, revision: { version: 0}};
// set up the bucket to be deleted
- nfRegistryService.buckets = [bucket, {identifier: 1}];
+ nfRegistryService.buckets = [bucket, {identifier: 1, revision: { version: 0}}];
nfRegistryService.filteredBuckets = nfRegistryService.buckets;
// The function to test
@@ -1065,13 +1065,13 @@
}).and.returnValue(of({identifier: 99, link: null}));
// object to be updated by the test
- const group = {identifier: 999, checked: true};
- const user = {identifier: 999, checked: true};
+ const group = {identifier: 999, checked: true, revision: { version: 0}};
+ const user = {identifier: 999, checked: true, revision: { version: 0}};
// set up the group to be deleted
- nfRegistryService.groups = [group, {identifier: 1}];
+ nfRegistryService.groups = [group, {identifier: 1, revision: { version: 0}}];
nfRegistryService.filteredUserGroups = nfRegistryService.groups;
- nfRegistryService.users = [user, {identifier: 12}];
+ nfRegistryService.users = [user, {identifier: 12, revision: { version: 0}}];
nfRegistryService.filteredUsers = nfRegistryService.users;
// The function to test