http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/provider/extension/TestFileSystemExtensionBundlePersistenceProvider.java ---------------------------------------------------------------------- diff --git a/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/provider/extension/TestFileSystemExtensionBundlePersistenceProvider.java b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/provider/extension/TestFileSystemExtensionBundlePersistenceProvider.java new file mode 100644 index 0000000..5a611bb --- /dev/null +++ b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/provider/extension/TestFileSystemExtensionBundlePersistenceProvider.java @@ -0,0 +1,295 @@ +/* + * 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.provider.extension; + +import org.apache.commons.io.IOUtils; +import org.apache.nifi.registry.extension.ExtensionBundleContext; +import org.apache.nifi.registry.extension.ExtensionBundlePersistenceException; +import org.apache.nifi.registry.extension.ExtensionBundlePersistenceProvider; +import org.apache.nifi.registry.provider.ProviderConfigurationContext; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +import static org.mockito.Mockito.when; + +public class TestFileSystemExtensionBundlePersistenceProvider { + + static final String EXTENSION_STORAGE_DIR = "target/extension_storage"; + + static final ProviderConfigurationContext CONFIGURATION_CONTEXT = new ProviderConfigurationContext() { + @Override + public Map<String, String> getProperties() { + final Map<String,String> props = new HashMap<>(); + props.put(FileSystemExtensionBundlePersistenceProvider.BUNDLE_STORAGE_DIR_PROP, EXTENSION_STORAGE_DIR); + return props; + } + }; + + private File bundleStorageDir; + private ExtensionBundlePersistenceProvider fileSystemBundleProvider; + + @Before + public void setup() throws IOException { + bundleStorageDir = new File(EXTENSION_STORAGE_DIR); + if (bundleStorageDir.exists()) { + org.apache.commons.io.FileUtils.cleanDirectory(bundleStorageDir); + bundleStorageDir.delete(); + } + + Assert.assertFalse(bundleStorageDir.exists()); + + fileSystemBundleProvider = new FileSystemExtensionBundlePersistenceProvider(); + fileSystemBundleProvider.onConfigured(CONFIGURATION_CONTEXT); + Assert.assertTrue(bundleStorageDir.exists()); + } + + @Test + public void testSaveSuccessfully() throws IOException { + // first version in b1 + final String content1 = "g1-a1-1.0.0"; + createAndSaveBundleVersion(fileSystemBundleProvider, "b1", "g1", "a1", "1.0.0", + ExtensionBundleContext.BundleType.NIFI_NAR, content1); + verifyBundleVersion(bundleStorageDir, "b1", "g1", "a1", "1.0.0", + ExtensionBundleContext.BundleType.NIFI_NAR, content1); + + // second version in b1 + final String content2 = "g1-a1-1.1.0"; + createAndSaveBundleVersion(fileSystemBundleProvider, "b1", "g1", "a1", "1.1.0", + ExtensionBundleContext.BundleType.NIFI_NAR, content2); + verifyBundleVersion(bundleStorageDir, "b1", "g1", "a1", "1.1.0", + ExtensionBundleContext.BundleType.NIFI_NAR, content2); + + // same bundle but in b2 + final String content3 = "g1-a1-1.1.0"; + createAndSaveBundleVersion(fileSystemBundleProvider, "b2", "g1", "a1", "1.1.0", + ExtensionBundleContext.BundleType.NIFI_NAR, content3); + verifyBundleVersion(bundleStorageDir, "b2", "g1", "a1", "1.1.0", + ExtensionBundleContext.BundleType.NIFI_NAR, content2); + } + + @Test + public void testSaveWhenBundleVersionAlreadyExists() throws IOException { + final String content1 = "g1-a1-1.0.0"; + createAndSaveBundleVersion(fileSystemBundleProvider, "b1", "g1", "a1", "1.0.0", + ExtensionBundleContext.BundleType.NIFI_NAR, content1); + verifyBundleVersion(bundleStorageDir, "b1", "g1", "a1", "1.0.0", + ExtensionBundleContext.BundleType.NIFI_NAR, content1); + + // try to save same bundle version that already exists + try { + final String newContent = "new content"; + createAndSaveBundleVersion(fileSystemBundleProvider, "b1", "g1", "a1", "1.0.0", + ExtensionBundleContext.BundleType.NIFI_NAR, newContent); + Assert.fail("Should have thrown exception"); + } catch (ExtensionBundlePersistenceException e) { + + } + + // verify existing content wasn't modified + verifyBundleVersion(bundleStorageDir, "b1", "g1", "a1", "1.0.0", + ExtensionBundleContext.BundleType.NIFI_NAR, content1); + } + + @Test + public void testSaveAndGet() throws IOException { + final String bucketName = "b1"; + final String groupId = "g1"; + final String artifactId = "a1"; + + final String content1 = groupId + "-" + artifactId + "-" + "1.0.0"; + createAndSaveBundleVersion(fileSystemBundleProvider, bucketName, groupId, artifactId, "1.0.0", + ExtensionBundleContext.BundleType.NIFI_NAR, content1); + + final String content2 = groupId + "-" + artifactId + "-" + "1.1.0"; + createAndSaveBundleVersion(fileSystemBundleProvider, bucketName, groupId, artifactId, "1.1.0", + ExtensionBundleContext.BundleType.NIFI_NAR, content2); + + try (final OutputStream out = new ByteArrayOutputStream()) { + final ExtensionBundleContext context = getExtensionBundleContext( + bucketName, groupId, artifactId, "1.0.0", ExtensionBundleContext.BundleType.NIFI_NAR); + fileSystemBundleProvider.getBundleVersion(context, out); + + final String retrievedContent1 = new String(((ByteArrayOutputStream) out).toByteArray(), StandardCharsets.UTF_8); + Assert.assertEquals(content1, retrievedContent1); + } + + try (final OutputStream out = new ByteArrayOutputStream()) { + final ExtensionBundleContext context = getExtensionBundleContext( + bucketName, groupId, artifactId, "1.1.0", ExtensionBundleContext.BundleType.NIFI_NAR); + fileSystemBundleProvider.getBundleVersion(context, out); + + final String retrievedContent2 = new String(((ByteArrayOutputStream) out).toByteArray(), StandardCharsets.UTF_8); + Assert.assertEquals(content2, retrievedContent2); + } + } + + @Test(expected = ExtensionBundlePersistenceException.class) + public void testGetWhenDoesNotExist() throws IOException { + final String bucketName = "b1"; + final String groupId = "g1"; + final String artifactId = "a1"; + + try (final OutputStream out = new ByteArrayOutputStream()) { + final ExtensionBundleContext context = getExtensionBundleContext( + bucketName, groupId, artifactId, "1.0.0", ExtensionBundleContext.BundleType.NIFI_NAR); + fileSystemBundleProvider.getBundleVersion(context, out); + Assert.fail("Should have thrown exception"); + } + } + + @Test + public void testDeleteExtensionBundleVersion() throws IOException { + final String bucketName = "b1"; + final String groupId = "g1"; + final String artifactId = "a1"; + final String version = "1.0.0"; + final ExtensionBundleContext.BundleType bundleType = ExtensionBundleContext.BundleType.NIFI_NAR; + + // create and verify the bundle version + final String content1 = groupId + "-" + artifactId + "-" + "1.0.0"; + createAndSaveBundleVersion(fileSystemBundleProvider, bucketName, groupId, artifactId, version, bundleType, content1); + verifyBundleVersion(bundleStorageDir, bucketName, groupId, artifactId, version, bundleType, content1); + + // delete the bundle version + fileSystemBundleProvider.deleteBundleVersion(getExtensionBundleContext(bucketName, groupId, artifactId, version, bundleType)); + + // verify it was deleted + final File bundleVersionDir = FileSystemExtensionBundlePersistenceProvider.getBundleVersionDirectory( + bundleStorageDir, bucketName, groupId, artifactId, version); + + final File bundleFile = FileSystemExtensionBundlePersistenceProvider.getBundleFile( + bundleVersionDir, artifactId, version, bundleType); + Assert.assertFalse(bundleFile.exists()); + } + + @Test + public void testDeleteExtensionBundleVersionWhenDoesNotExist() throws IOException { + final String bucketName = "b1"; + final String groupId = "g1"; + final String artifactId = "a1"; + final String version = "1.0.0"; + final ExtensionBundleContext.BundleType bundleType = ExtensionBundleContext.BundleType.NIFI_NAR; + + // verify the bundle version does not already exist + final File bundleVersionDir = FileSystemExtensionBundlePersistenceProvider.getBundleVersionDirectory( + bundleStorageDir, bucketName, groupId, artifactId, version); + + final File bundleFile = FileSystemExtensionBundlePersistenceProvider.getBundleFile( + bundleVersionDir, artifactId, version, bundleType); + Assert.assertFalse(bundleFile.exists()); + + // delete the bundle version + fileSystemBundleProvider.deleteBundleVersion(getExtensionBundleContext(bucketName, groupId, artifactId, version, bundleType)); + } + + @Test + public void testDeleteAllBundleVersions() throws IOException { + final String bucketName = "b1"; + final String groupId = "g1"; + final String artifactId = "a1"; + final String version1 = "1.0.0"; + final String version2 = "2.0.0"; + final ExtensionBundleContext.BundleType bundleType = ExtensionBundleContext.BundleType.NIFI_NAR; + + // create and verify the bundle version 1 + final String content1 = groupId + "-" + artifactId + "-" + version1; + createAndSaveBundleVersion(fileSystemBundleProvider, bucketName, groupId, artifactId, version1, bundleType, content1); + verifyBundleVersion(bundleStorageDir, bucketName, groupId, artifactId, version1, bundleType, content1); + + // create and verify the bundle version 2 + final String content2 = groupId + "-" + artifactId + "-" + version2; + createAndSaveBundleVersion(fileSystemBundleProvider, bucketName, groupId, artifactId, version2, bundleType, content2); + verifyBundleVersion(bundleStorageDir, bucketName, groupId, artifactId, version2, bundleType, content2); + + fileSystemBundleProvider.deleteAllBundleVersions(bucketName, bucketName, groupId, artifactId); + Assert.assertEquals(0, bundleStorageDir.listFiles().length); + } + + @Test + public void testDeleteAllBundleVersionsWhenDoesNotExist() throws IOException { + final String bucketName = "b1"; + final String groupId = "g1"; + final String artifactId = "a1"; + + Assert.assertEquals(0, bundleStorageDir.listFiles().length); + fileSystemBundleProvider.deleteAllBundleVersions(bucketName, bucketName, groupId, artifactId); + Assert.assertEquals(0, bundleStorageDir.listFiles().length); + } + + private void createAndSaveBundleVersion(final ExtensionBundlePersistenceProvider persistenceProvider, + final String bucketName, + final String groupId, + final String artifactId, + final String version, + final ExtensionBundleContext.BundleType bundleType, + final String content) throws IOException { + + final ExtensionBundleContext context = getExtensionBundleContext(bucketName, groupId, artifactId, version, bundleType); + + try (final InputStream in = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))) { + persistenceProvider.saveBundleVersion(context, in); + } + } + + private static ExtensionBundleContext getExtensionBundleContext(final String bucketName, + final String groupId, + final String artifactId, + final String version, + final ExtensionBundleContext.BundleType bundleType) { + final ExtensionBundleContext context = Mockito.mock(ExtensionBundleContext.class); + when(context.getBucketName()).thenReturn(bucketName); + when(context.getBundleGroupId()).thenReturn(groupId); + when(context.getBundleArtifactId()).thenReturn(artifactId); + when(context.getBundleVersion()).thenReturn(version); + when(context.getBundleType()).thenReturn(bundleType); + return context; + } + + private static void verifyBundleVersion(final File storageDir, + final String bucketName, + final String groupId, + final String artifactId, + final String version, + final ExtensionBundleContext.BundleType bundleType, + final String contentString) throws IOException { + + final File bundleVersionDir = FileSystemExtensionBundlePersistenceProvider.getBundleVersionDirectory( + storageDir, bucketName, groupId, artifactId, version); + + final File bundleFile = FileSystemExtensionBundlePersistenceProvider.getBundleFile( + bundleVersionDir, artifactId, version, bundleType); + Assert.assertTrue(bundleFile.exists()); + + try (InputStream in = new FileInputStream(bundleFile)) { + Assert.assertEquals(contentString, IOUtils.toString(in, StandardCharsets.UTF_8)); + } + } + +}
http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/ldap/tenants/LdapUserGroupProviderTest.java ---------------------------------------------------------------------- diff --git a/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/ldap/tenants/LdapUserGroupProviderTest.java b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/ldap/tenants/LdapUserGroupProviderTest.java index 242cf28..9a408b2 100644 --- a/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/ldap/tenants/LdapUserGroupProviderTest.java +++ b/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/security/ldap/tenants/LdapUserGroupProviderTest.java @@ -67,7 +67,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.anyString; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-framework/src/test/java/org/apache/nifi/registry/service/TestRegistryService.java ---------------------------------------------------------------------- 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 95e2d1a..0af08c1 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 @@ -32,6 +32,8 @@ import org.apache.nifi.registry.flow.VersionedProcessGroup; import org.apache.nifi.registry.flow.VersionedProcessor; import org.apache.nifi.registry.serialization.Serializer; import org.apache.nifi.registry.serialization.VersionedProcessGroupSerializer; +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; @@ -73,6 +75,7 @@ public class TestRegistryService { private MetadataService metadataService; private FlowPersistenceProvider flowPersistenceProvider; private Serializer<VersionedProcessGroup> snapshotSerializer; + private ExtensionService extensionService; private Validator validator; private RegistryService registryService; @@ -82,11 +85,12 @@ public class TestRegistryService { metadataService = mock(MetadataService.class); flowPersistenceProvider = mock(FlowPersistenceProvider.class); snapshotSerializer = mock(VersionedProcessGroupSerializer.class); + extensionService = mock(StandardExtensionService.class); final ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory(); validator = validatorFactory.getValidator(); - registryService = new RegistryService(metadataService, flowPersistenceProvider, snapshotSerializer, validator); + registryService = new RegistryService(metadataService, flowPersistenceProvider, snapshotSerializer, extensionService, validator); } // ---------------------- Test Bucket methods --------------------------------------------- http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-framework/src/test/resources/db/migration/V999999.1__test-setup.sql ---------------------------------------------------------------------- diff --git a/nifi-registry-core/nifi-registry-framework/src/test/resources/db/migration/V999999.1__test-setup.sql b/nifi-registry-core/nifi-registry-framework/src/test/resources/db/migration/V999999.1__test-setup.sql index 8f3cfa8..b8e0d3a 100644 --- a/nifi-registry-core/nifi-registry-framework/src/test/resources/db/migration/V999999.1__test-setup.sql +++ b/nifi-registry-core/nifi-registry-framework/src/test/resources/db/migration/V999999.1__test-setup.sql @@ -67,4 +67,225 @@ insert into flow_snapshot (flow_id, version, created, created_by, comments) -- test data for signing keys insert into signing_key (id, tenant_identity, key_value) - values ('1', 'unit_test_tenant_identity', '0123456789abcdef'); \ No newline at end of file + values ('1', 'unit_test_tenant_identity', '0123456789abcdef'); + +-- test data for extension bundles + +-- processors bundle, depends on service api bundle +insert into bucket_item ( + id, + name, + description, + created, + modified, + item_type, + bucket_id +) values ( + 'eb1', + 'nifi-example-processors-nar', + 'Example processors bundle', + parsedatetime('2018-11-02 12:56:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'), + parsedatetime('2018-11-02 12:56:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'), + 'EXTENSION_BUNDLE', + '3' +); + +insert into extension_bundle ( + id, + bucket_id, + bundle_type, + group_id, + artifact_id +) values ( + 'eb1', + '3', + 'NIFI_NAR', + 'org.apache.nifi', + 'nifi-example-processors-nar' +); + +insert into extension_bundle_version ( + id, + extension_bundle_id, + version, + created, + created_by, + description, + sha_256_hex, + sha_256_supplied +) values ( + 'eb1-v1', + 'eb1', + '1.0.0', + parsedatetime('2018-11-02 13:00:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'), + 'user1', + 'First version of eb1', + '123456789', + '1' +); + +insert into extension_bundle_version_dependency ( + id, + extension_bundle_version_id, + group_id, + artifact_id, + version +) values ( + 'eb1-v1-dep1', + 'eb1-v1', + 'org.apache.nifi', + 'nifi-example-service-api-nar', + '2.0.0' +); + +-- service impl bundle, depends on service api bundle +insert into bucket_item ( + id, + name, + description, + created, + modified, + item_type, + bucket_id +) values ( + 'eb2', + 'nifi-example-services-nar', + 'Example services bundle', + parsedatetime('2018-11-02 12:57:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'), + parsedatetime('2018-11-02 12:57:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'), + 'EXTENSION_BUNDLE', + '3' +); + +insert into extension_bundle ( + id, + bucket_id, + bundle_type, + group_id, + artifact_id +) values ( + 'eb2', + '3', + 'NIFI_NAR', + 'org.apache.nifi', + 'nifi-example-services-nar' +); + +insert into extension_bundle_version ( + id, + extension_bundle_id, + version, + created, + created_by, + description, + sha_256_hex, + sha_256_supplied +) values ( + 'eb2-v1', + 'eb2', + '1.0.0', + parsedatetime('2018-11-02 13:00:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'), + 'user1', + 'First version of eb2', + '123456789', + '1' +); + +insert into extension_bundle_version_dependency ( + id, + extension_bundle_version_id, + group_id, + artifact_id, + version +) values ( + 'eb2-v1-dep1', + 'eb2-v1', + 'org.apache.nifi', + 'nifi-example-service-api-nar', + '2.0.0' +); + +-- service api bundle +insert into bucket_item ( + id, + name, + description, + created, + modified, + item_type, + bucket_id +) values ( + 'eb3', + 'nifi-example-service-api-nar', + 'Example service API bundle', + parsedatetime('2018-11-02 12:58:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'), + parsedatetime('2017-11-02 12:58:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'), + 'EXTENSION_BUNDLE', + '3' +); + +insert into extension_bundle ( + id, + bucket_id, + bundle_type, + group_id, + artifact_id +) values ( + 'eb3', + '3', + 'NIFI_NAR', + 'org.apache.nifi', + 'nifi-example-service-api-nar' +); + +insert into extension_bundle_version ( + id, + extension_bundle_id, + version, + created, + created_by, + description, + sha_256_hex, + sha_256_supplied +) values ( + 'eb3-v1', + 'eb3', + '2.0.0', + parsedatetime('2018-11-02 13:00:00.000 UTC', 'yyyy-MM-dd hh:mm:ss.SSS z'), + 'user1', + 'First version of eb3', + '123456789', + '1' +); + +-- test data for extensions + +insert into extension ( + id, extension_bundle_version_id, type, type_description, is_restricted, category, tags +) values ( + 'e1', 'eb1-v1', 'org.apache.nifi.ExampleProcessor', 'This is Example Processor 1', 0, 'PROCESSOR', 'example, processor' +); + +insert into extension ( + id, extension_bundle_version_id, type, type_description, is_restricted, category, tags) +values ( + 'e2', 'eb1-v1', 'org.apache.nifi.ExampleProcessorRestricted', 'This is Example Processor Restricted', 1, 'PROCESSOR', 'example, processor, restricted' +); + +insert into extension ( + id, extension_bundle_version_id, type, type_description, is_restricted, category, tags) +values ( + 'e3', 'eb2-v1', 'org.apache.nifi.ExampleService', 'This is Example Service', 0, 'CONTROLLER_SERVICE', 'example, service' +); + +-- test data for extension tags + +insert into extension_tag (extension_id, tag) values ('e1', 'example'); +insert into extension_tag (extension_id, tag) values ('e1', 'processor'); + +insert into extension_tag (extension_id, tag) values ('e2', 'example'); +insert into extension_tag (extension_id, tag) values ('e2', 'processor'); +insert into extension_tag (extension_id, tag) values ('e2', 'restricted'); + +insert into extension_tag (extension_id, tag) values ('e3', 'example'); +insert into extension_tag (extension_id, tag) values ('e3', 'service'); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/hook/bad-script-provider.xml ---------------------------------------------------------------------- diff --git a/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/hook/bad-script-provider.xml b/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/hook/bad-script-provider.xml index 568e756..1e23386 100644 --- a/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/hook/bad-script-provider.xml +++ b/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/hook/bad-script-provider.xml @@ -27,4 +27,10 @@ <property name="Working Directory"></property> </eventHookProvider> + <extensionBundlePersistenceProvider> + <class>org.apache.nifi.registry.provider.MockExtensionBundlePersistenceProvider</class> + <property name="Extension Property 1">extension foo</property> + <property name="Extension Property 2">extension bar</property> + </extensionBundlePersistenceProvider> + </providers> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/providers-class-not-found.xml ---------------------------------------------------------------------- diff --git a/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/providers-class-not-found.xml b/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/providers-class-not-found.xml index 9adba54..7031ca9 100644 --- a/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/providers-class-not-found.xml +++ b/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/providers-class-not-found.xml @@ -21,4 +21,10 @@ <property name="Flow Property 2">bar</property> </flowPersistenceProvider> + <extensionBundlePersistenceProvider> + <class>org.apache.nifi.registry.provider.MockExtensionBundlePersistenceProvider</class> + <property name="Extension Property 1">extension foo</property> + <property name="Extension Property 2">extension bar</property> + </extensionBundlePersistenceProvider> + </providers> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/providers-good.xml ---------------------------------------------------------------------- diff --git a/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/providers-good.xml b/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/providers-good.xml index 32414e5..fc963f1 100644 --- a/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/providers-good.xml +++ b/nifi-registry-core/nifi-registry-framework/src/test/resources/provider/providers-good.xml @@ -21,4 +21,10 @@ <property name="Flow Property 2">flow bar</property> </flowPersistenceProvider> + <extensionBundlePersistenceProvider> + <class>org.apache.nifi.registry.provider.MockExtensionBundlePersistenceProvider</class> + <property name="Extension Property 1">extension foo</property> + <property name="Extension Property 2">extension bar</property> + </extensionBundlePersistenceProvider> + </providers> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java ---------------------------------------------------------------------- diff --git a/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java b/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java index 1dcb0f7..89a8e60 100644 --- a/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java +++ b/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java @@ -16,16 +16,16 @@ */ package org.apache.nifi.registry.properties; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.File; import java.util.Enumeration; import java.util.HashSet; import java.util.Properties; import java.util.Set; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class NiFiRegistryProperties extends Properties { private static final Logger logger = LoggerFactory.getLogger(NiFiRegistryProperties.class); @@ -58,6 +58,8 @@ public class NiFiRegistryProperties extends Properties { public static final String PROVIDERS_CONFIGURATION_FILE = "nifi.registry.providers.configuration.file"; + public static final String EXTENSIONS_WORKING_DIR = "nifi.registry.extensions.working.directory"; + // Original DB properties public static final String DATABASE_DIRECTORY = "nifi.registry.db.directory"; public static final String DATABASE_URL_APPEND = "nifi.registry.db.url.append"; @@ -86,6 +88,7 @@ public class NiFiRegistryProperties extends Properties { public static final String DEFAULT_SECURITY_AUTHORIZERS_CONFIGURATION_FILE = "./conf/authorizers.xml"; public static final String DEFAULT_SECURITY_IDENTITY_PROVIDER_CONFIGURATION_FILE = "./conf/identity-providers.xml"; public static final String DEFAULT_AUTHENTICATION_EXPIRATION = "12 hours"; + public static final String DEFAULT_EXTENSIONS_WORKING_DIR = "./work/extensions"; public int getWebThreads() { int webThreads = 200; @@ -158,6 +161,10 @@ public class NiFiRegistryProperties extends Properties { return new File(getProperty(WEB_WORKING_DIR, DEFAULT_WEB_WORKING_DIR)); } + public File getExtensionsWorkingDirectory() { + return new File(getProperty(EXTENSIONS_WORKING_DIR, DEFAULT_EXTENSIONS_WORKING_DIR)); + } + public File getProvidersConfigurationFile() { return getPropertyAsFile(PROVIDERS_CONFIGURATION_FILE, DEFAULT_PROVIDERS_CONFIGURATION_FILE); } http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-provider-api/src/main/java/org/apache/nifi/registry/extension/ExtensionBundleContext.java ---------------------------------------------------------------------- diff --git a/nifi-registry-core/nifi-registry-provider-api/src/main/java/org/apache/nifi/registry/extension/ExtensionBundleContext.java b/nifi-registry-core/nifi-registry-provider-api/src/main/java/org/apache/nifi/registry/extension/ExtensionBundleContext.java new file mode 100644 index 0000000..2681849 --- /dev/null +++ b/nifi-registry-core/nifi-registry-provider-api/src/main/java/org/apache/nifi/registry/extension/ExtensionBundleContext.java @@ -0,0 +1,79 @@ +/* + * 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.extension; + +/** + * The context that will be passed to the {@link ExtensionBundlePersistenceProvider} when saving a new version of an extension bundle. + */ +public interface ExtensionBundleContext { + + enum BundleType { + NIFI_NAR, + MINIFI_CPP; + } + + /** + * @return the id of the bucket the bundle belongs to + */ + String getBucketId(); + + /** + * @return the name of the bucket the bundle belongs to + */ + String getBucketName(); + + /** + * @return the type of the bundle + */ + BundleType getBundleType(); + + /** + * @return the NiFi Registry id of the bundle + */ + String getBundleId(); + + /** + * @return the group id of the bundle + */ + String getBundleGroupId(); + + /** + * @return the artifact id of the bundle + */ + String getBundleArtifactId(); + + /** + * @return the version of the bundle + */ + String getBundleVersion(); + + /** + * @return the comments for the version of the bundle + */ + String getDescription(); + + /** + * @return the timestamp the bundle was created + */ + long getTimestamp(); + + /** + * @return the user that created the bundle + */ + String getAuthor(); + +} http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-provider-api/src/main/java/org/apache/nifi/registry/extension/ExtensionBundlePersistenceException.java ---------------------------------------------------------------------- diff --git a/nifi-registry-core/nifi-registry-provider-api/src/main/java/org/apache/nifi/registry/extension/ExtensionBundlePersistenceException.java b/nifi-registry-core/nifi-registry-provider-api/src/main/java/org/apache/nifi/registry/extension/ExtensionBundlePersistenceException.java new file mode 100644 index 0000000..4722604 --- /dev/null +++ b/nifi-registry-core/nifi-registry-provider-api/src/main/java/org/apache/nifi/registry/extension/ExtensionBundlePersistenceException.java @@ -0,0 +1,32 @@ +/* + * 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.extension; + +/** + * An Exception for errors encountered when a ExtensionBundlePersistenceProvider saves or retrieves a bundle. + */ +public class ExtensionBundlePersistenceException extends RuntimeException { + + public ExtensionBundlePersistenceException(String message) { + super(message); + } + + public ExtensionBundlePersistenceException(String message, Throwable cause) { + super(message, cause); + } + +} http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-provider-api/src/main/java/org/apache/nifi/registry/extension/ExtensionBundlePersistenceProvider.java ---------------------------------------------------------------------- diff --git a/nifi-registry-core/nifi-registry-provider-api/src/main/java/org/apache/nifi/registry/extension/ExtensionBundlePersistenceProvider.java b/nifi-registry-core/nifi-registry-provider-api/src/main/java/org/apache/nifi/registry/extension/ExtensionBundlePersistenceProvider.java new file mode 100644 index 0000000..9ef2646 --- /dev/null +++ b/nifi-registry-core/nifi-registry-provider-api/src/main/java/org/apache/nifi/registry/extension/ExtensionBundlePersistenceProvider.java @@ -0,0 +1,67 @@ +/* + * 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.extension; + +import org.apache.nifi.registry.provider.Provider; + +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Responsible for storing and retrieving the binary content of a version of an extension bundle. + */ +public interface ExtensionBundlePersistenceProvider extends Provider { + + /** + * Persists the binary content of a version of an extension bundle. + * + * @param context the context about the bundle version being persisted + * @param contentStream the stream of binary content to persist + * @throws ExtensionBundlePersistenceException if an error occurs storing the content + */ + void saveBundleVersion(ExtensionBundleContext context, InputStream contentStream) throws ExtensionBundlePersistenceException; + + /** + * Writes the binary content of the bundle specified by the bucket-group-artifact-version to the provided OutputStream. + * + * @param context the context about the bundle version being retrieved + * @param outputStream the output stream to write the contents to + * @throws ExtensionBundlePersistenceException if an error occurs retrieving the content + */ + void getBundleVersion(ExtensionBundleContext context, OutputStream outputStream) throws ExtensionBundlePersistenceException; + + /** + * Deletes the content of the bundle version specified by bucket-group-artifact-version. + * + * @param context the context about the bundle version being deleted + * @throws ExtensionBundlePersistenceException if an error occurs deleting the content + */ + void deleteBundleVersion(ExtensionBundleContext context) throws ExtensionBundlePersistenceException; + + /** + * Deletes the content for all versions of the bundle specified by bucket-group-artifact. + * + * @param bucketId the id of the bucket where the bundle is located + * @param bucketName the bucket name where the bundle is located + * @param groupId the group id of the bundle + * @param artifactId the artifact id of the bundle + * @throws ExtensionBundlePersistenceException if an error occurs deleting the content + */ + void deleteAllBundleVersions(String bucketId, String bucketName, String groupId, String artifactId) + throws ExtensionBundlePersistenceException; + +} http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-provider-api/src/main/java/org/apache/nifi/registry/hook/EventFieldName.java ---------------------------------------------------------------------- diff --git a/nifi-registry-core/nifi-registry-provider-api/src/main/java/org/apache/nifi/registry/hook/EventFieldName.java b/nifi-registry-core/nifi-registry-provider-api/src/main/java/org/apache/nifi/registry/hook/EventFieldName.java index 35b0cfe..3f2fa6e 100644 --- a/nifi-registry-core/nifi-registry-provider-api/src/main/java/org/apache/nifi/registry/hook/EventFieldName.java +++ b/nifi-registry-core/nifi-registry-provider-api/src/main/java/org/apache/nifi/registry/hook/EventFieldName.java @@ -23,6 +23,7 @@ public enum EventFieldName { BUCKET_ID, FLOW_ID, + EXTENSION_BUNDLE_ID, VERSION, USER, COMMENT; http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-provider-api/src/main/java/org/apache/nifi/registry/hook/EventType.java ---------------------------------------------------------------------- diff --git a/nifi-registry-core/nifi-registry-provider-api/src/main/java/org/apache/nifi/registry/hook/EventType.java b/nifi-registry-core/nifi-registry-provider-api/src/main/java/org/apache/nifi/registry/hook/EventType.java index c11a60c..0af35dc 100644 --- a/nifi-registry-core/nifi-registry-provider-api/src/main/java/org/apache/nifi/registry/hook/EventType.java +++ b/nifi-registry-core/nifi-registry-provider-api/src/main/java/org/apache/nifi/registry/hook/EventType.java @@ -41,6 +41,17 @@ public enum EventType { EventFieldName.VERSION, EventFieldName.USER, EventFieldName.COMMENT), + CREATE_EXTENSION_BUNDLE( + EventFieldName.BUCKET_ID, + EventFieldName.EXTENSION_BUNDLE_ID, + EventFieldName.USER + ), + CREATE_EXTENSION_BUNDLE_VERSION( + EventFieldName.BUCKET_ID, + EventFieldName.EXTENSION_BUNDLE_ID, + EventFieldName.VERSION, + EventFieldName.USER + ), REGISTRY_START(), UPDATE_BUCKET( EventFieldName.BUCKET_ID, @@ -55,7 +66,19 @@ public enum EventType { DELETE_FLOW( EventFieldName.BUCKET_ID, EventFieldName.FLOW_ID, - EventFieldName.USER); + EventFieldName.USER), + DELETE_EXTENSION_BUNDLE( + EventFieldName.BUCKET_ID, + EventFieldName.EXTENSION_BUNDLE_ID, + EventFieldName.USER + ), + DELETE_EXTENSION_BUNDLE_VERSION( + EventFieldName.BUCKET_ID, + EventFieldName.EXTENSION_BUNDLE_ID, + EventFieldName.VERSION, + EventFieldName.USER + ) + ; private List<EventFieldName> fieldNames; http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/nifi-registry.properties ---------------------------------------------------------------------- diff --git a/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/nifi-registry.properties b/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/nifi-registry.properties index fb77a07..ce4377f 100644 --- a/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/nifi-registry.properties +++ b/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/nifi-registry.properties @@ -42,6 +42,9 @@ nifi.registry.security.identity.provider=${nifi.registry.security.identity.provi # providers properties # nifi.registry.providers.configuration.file=${nifi.registry.providers.configuration.file} +# extensions working dir # +nifi.registry.extensions.working.directory=${nifi.registry.extensions.working.directory} + # legacy database properties, used to migrate data from original DB to new DB below # NOTE: Users upgrading from 0.1.0 should leave these populated, but new installs after 0.1.0 should leave these empty nifi.registry.db.directory=${nifi.registry.db.directory} http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/providers.xml ---------------------------------------------------------------------- diff --git a/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/providers.xml b/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/providers.xml index faf8d4f..306c073 100644 --- a/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/providers.xml +++ b/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/providers.xml @@ -51,4 +51,9 @@ </eventHookProvider> --> + <extensionBundlePersistenceProvider> + <class>org.apache.nifi.registry.provider.extension.FileSystemExtensionBundlePersistenceProvider</class> + <property name="Extension Bundle Storage Directory">./extension_bundles</property> + </extensionBundlePersistenceProvider> + </providers> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-web-api/pom.xml ---------------------------------------------------------------------- diff --git a/nifi-registry-core/nifi-registry-web-api/pom.xml b/nifi-registry-core/nifi-registry-web-api/pom.xml index e0af632..6e9fa10 100644 --- a/nifi-registry-core/nifi-registry-web-api/pom.xml +++ b/nifi-registry-core/nifi-registry-web-api/pom.xml @@ -231,6 +231,13 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jersey</artifactId> <version>${spring.boot.version}</version> + <exclusions> + <!-- spring-boot-starter-jersey brings in a Spring 4.x version of spring-aop which causes problems --> + <exclusion> + <groupId>org.springframework</groupId> + <artifactId>spring-aop</artifactId> + </exclusion> + </exclusions> </dependency> <!-- Exclude micrometer-core because it creates a class cast issue with logback, revisit later --> <dependency> @@ -308,6 +315,10 @@ <artifactId>swagger-annotations</artifactId> </dependency> <dependency> + <groupId>org.glassfish.jersey.media</groupId> + <artifactId>jersey-media-multipart</artifactId> + </dependency> + <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.7.0</version> http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/NiFiRegistryApiApplication.java ---------------------------------------------------------------------- diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/NiFiRegistryApiApplication.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/NiFiRegistryApiApplication.java index d06555d..2ffefbb 100644 --- a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/NiFiRegistryApiApplication.java +++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/NiFiRegistryApiApplication.java @@ -54,6 +54,13 @@ public class NiFiRegistryApiApplication extends SpringBootServletInitializer { protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { final Properties defaultProperties = new Properties(); + // Spring Boot 2.1.0 disabled bean overriding so this re-enables it + defaultProperties.setProperty("spring.main.allow-bean-definition-overriding", "true"); + + // Disable unnecessary Spring MVC filters that cause problems with Jersey + defaultProperties.setProperty("spring.mvc.hiddenmethod.filter.enabled", "false"); + defaultProperties.setProperty("spring.mvc.formcontent.filter.enabled", "false"); + // Enable Actuator Endpoints defaultProperties.setProperty("management.endpoints.web.expose", "*"); http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/NiFiRegistryResourceConfig.java ---------------------------------------------------------------------- diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/NiFiRegistryResourceConfig.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/NiFiRegistryResourceConfig.java index a5ab5ef..7394e8c 100644 --- a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/NiFiRegistryResourceConfig.java +++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/NiFiRegistryResourceConfig.java @@ -18,12 +18,16 @@ package org.apache.nifi.registry.web; import org.apache.nifi.registry.web.api.AccessPolicyResource; import org.apache.nifi.registry.web.api.AccessResource; +import org.apache.nifi.registry.web.api.BucketExtensionResource; import org.apache.nifi.registry.web.api.BucketFlowResource; import org.apache.nifi.registry.web.api.BucketResource; import org.apache.nifi.registry.web.api.ConfigResource; +import org.apache.nifi.registry.web.api.ExtensionRepositoryResource; +import org.apache.nifi.registry.web.api.ExtensionResource; import org.apache.nifi.registry.web.api.FlowResource; import org.apache.nifi.registry.web.api.ItemResource; import org.apache.nifi.registry.web.api.TenantResource; +import org.glassfish.jersey.media.multipart.MultiPartFeature; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.ServerProperties; import org.glassfish.jersey.server.filter.HttpMethodOverrideFilter; @@ -57,11 +61,17 @@ public class NiFiRegistryResourceConfig extends ResourceConfig { register(AccessResource.class); register(BucketResource.class); register(BucketFlowResource.class); + register(BucketExtensionResource.class); + register(ExtensionResource.class); + register(ExtensionRepositoryResource.class); register(FlowResource.class); register(ItemResource.class); register(TenantResource.class); register(ConfigResource.class); + // register multipart feature + register(MultiPartFeature.class); + // include bean validation errors in response property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true); http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ApplicationResource.java ---------------------------------------------------------------------- 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 776a693..22c5211 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 @@ -71,9 +71,8 @@ public class ApplicationResource { } } - protected String generateResourceUri(final String... path) { + protected URI getBaseUri() { final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder(); - uriBuilder.segment(path); URI uri = uriBuilder.build(); try { @@ -126,7 +125,13 @@ public class ApplicationResource { } catch (final URISyntaxException use) { throw new UriBuilderException(use); } - return uri.toString(); + return uri; + } + + protected String generateResourceUri(final String... path) { + final URI baseUri = getBaseUri(); + final URI fullUri = UriBuilder.fromUri(baseUri).segment(path).build(); + return fullUri.toString(); } /** http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketExtensionResource.java ---------------------------------------------------------------------- diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketExtensionResource.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketExtensionResource.java new file mode 100644 index 0000000..4477acd --- /dev/null +++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketExtensionResource.java @@ -0,0 +1,179 @@ +/* + * 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 io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import io.swagger.annotations.Authorization; +import io.swagger.annotations.Extension; +import io.swagger.annotations.ExtensionProperty; +import org.apache.nifi.registry.event.EventFactory; +import org.apache.nifi.registry.event.EventService; +import org.apache.nifi.registry.extension.ExtensionBundle; +import org.apache.nifi.registry.extension.ExtensionBundleType; +import org.apache.nifi.registry.extension.ExtensionBundleVersion; +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.glassfish.jersey.media.multipart.FormDataContentDisposition; +import org.glassfish.jersey.media.multipart.FormDataParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +@Component +@Path("/buckets/{bucketId}/extensions") +@Api( + value = "bucket_extensions", + description = "Create extension bundles scoped to an existing bucket in the registry.", + authorizations = { @Authorization("Authorization") } +) +public class BucketExtensionResource extends AuthorizableApplicationResource { + + private static final Logger LOGGER = LoggerFactory.getLogger(BucketExtensionResource.class); + + private final RegistryService registryService; + private final LinkService linkService; + private final PermissionsService permissionsService; + + @Autowired + public BucketExtensionResource( + 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; + } + + @POST + @Path("bundles/{bundleType}") + @Consumes(MediaType.MULTIPART_FORM_DATA) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Creates a version of an extension bundle by uploading a binary artifact", + notes = "If an extension bundle already exists in the given bucket with the same group id and artifact id " + + "as that of the bundle being uploaded, then it will be added as a new version to the existing bundle. " + + "If an extension bundle does not already exist in the given bucket with the same group id and artifact id, " + + "then a new extension bundle will be created and this version will be added to the new bundle. " + + "Client's may optionally supply a SHA-256 in hex format through the multi-part form field 'sha256'. " + + "If supplied, then this value will be compared against the SHA-256 computed by the server, and the bundle " + + "will be rejected if the values do not match. If not supplied, the bundle will be accepted, but will be marked " + + "to indicate that the client did not supply a SHA-256 during creation.", + response = ExtensionBundleVersion.class, + extensions = { + @Extension(name = "access-policy", properties = { + @ExtensionProperty(name = "action", value = "write"), + @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") }) + } + ) + @ApiResponses({ + @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400), + @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), + @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), + @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404), + @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) }) + public Response createExtensionBundleVersion( + @PathParam("bucketId") + @ApiParam("The bucket identifier") + final String bucketId, + @PathParam("bundleType") + @ApiParam("The type of the bundle") + final String bundleType, + @FormDataParam("file") + final InputStream fileInputStream, + @FormDataParam("file") + final FormDataContentDisposition fileMetaData, + @FormDataParam("sha256") + final String clientSha256) throws IOException { + + authorizeBucketAccess(RequestAction.WRITE, bucketId); + + final ExtensionBundleType extensionBundleType = ExtensionBundleType.fromString(bundleType); + LOGGER.debug("Creating extension bundle version for bundle type {}", new Object[]{extensionBundleType}); + + final ExtensionBundleVersion createdBundleVersion = registryService.createExtensionBundleVersion( + bucketId, extensionBundleType, fileInputStream, clientSha256); + + publish(EventFactory.extensionBundleCreated(createdBundleVersion.getExtensionBundle())); + publish(EventFactory.extensionBundleVersionCreated(createdBundleVersion)); + + linkService.populateLinks(createdBundleVersion.getVersionMetadata()); + linkService.populateLinks(createdBundleVersion.getExtensionBundle()); + linkService.populateLinks(createdBundleVersion.getBucket()); + + permissionsService.populateItemPermissions(createdBundleVersion.getExtensionBundle()); + + return Response.status(Response.Status.OK).entity(createdBundleVersion).build(); + } + + @GET + @Path("bundles") + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Gets all extension bundles in the given bucket", + response = ExtensionBundle.class, + responseContainer = "List", + extensions = { + @Extension(name = "access-policy", properties = { + @ExtensionProperty(name = "action", value = "read"), + @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") }) + } + ) + @ApiResponses({ + @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400), + @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), + @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), + @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404), + @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) }) + public Response getExtensionBundles( + @PathParam("bucketId") + @ApiParam("The bucket identifier") + final String bucketId + ) { + authorizeBucketAccess(RequestAction.READ, bucketId); + + final List<ExtensionBundle> bundles = registryService.getExtensionBundlesByBucket(bucketId); + permissionsService.populateItemPermissions(bundles); + linkService.populateLinks(bundles); + + return Response.status(Response.Status.OK).entity(bundles).build(); + } + +} http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketFlowResource.java ---------------------------------------------------------------------- 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 942a3d4..ccf41d9 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 @@ -119,7 +119,7 @@ public class BucketFlowResource extends AuthorizableApplicationResource { publish(EventFactory.flowCreated(createdFlow)); permissionsService.populateItemPermissions(createdFlow); - linkService.populateFlowLinks(createdFlow); + linkService.populateLinks(createdFlow); return Response.status(Response.Status.OK).entity(createdFlow).build(); } @@ -151,7 +151,7 @@ public class BucketFlowResource extends AuthorizableApplicationResource { final List<VersionedFlow> flows = registryService.getFlows(bucketId); permissionsService.populateItemPermissions(flows); - linkService.populateFlowLinks(flows); + linkService.populateLinks(flows); return Response.status(Response.Status.OK).entity(flows).build(); } @@ -187,7 +187,7 @@ public class BucketFlowResource extends AuthorizableApplicationResource { final VersionedFlow flow = registryService.getFlow(bucketId, flowId); permissionsService.populateItemPermissions(flow); - linkService.populateFlowLinks(flow); + linkService.populateLinks(flow); return Response.status(Response.Status.OK).entity(flow).build(); } @@ -230,7 +230,7 @@ public class BucketFlowResource extends AuthorizableApplicationResource { final VersionedFlow updatedFlow = registryService.updateFlow(flow); publish(EventFactory.flowUpdated(updatedFlow)); permissionsService.populateItemPermissions(updatedFlow); - linkService.populateFlowLinks(updatedFlow); + linkService.populateLinks(updatedFlow); return Response.status(Response.Status.OK).entity(updatedFlow).build(); } @@ -311,11 +311,11 @@ public class BucketFlowResource extends AuthorizableApplicationResource { publish(EventFactory.flowVersionCreated(createdSnapshot)); if (createdSnapshot.getSnapshotMetadata() != null) { - linkService.populateSnapshotLinks(createdSnapshot.getSnapshotMetadata()); + linkService.populateLinks(createdSnapshot.getSnapshotMetadata()); } if (createdSnapshot.getBucket() != null) { permissionsService.populateBucketPermissions(createdSnapshot.getBucket()); - linkService.populateBucketLinks(createdSnapshot.getBucket()); + linkService.populateLinks(createdSnapshot.getBucket()); } return Response.status(Response.Status.OK).entity(createdSnapshot).build(); } @@ -351,7 +351,7 @@ public class BucketFlowResource extends AuthorizableApplicationResource { final SortedSet<VersionedFlowSnapshotMetadata> snapshots = registryService.getFlowSnapshots(bucketId, flowId); if (snapshots != null ) { - linkService.populateSnapshotLinks(snapshots); + linkService.populateLinks(snapshots); } return Response.status(Response.Status.OK).entity(snapshots).build(); @@ -421,7 +421,7 @@ public class BucketFlowResource extends AuthorizableApplicationResource { authorizeBucketAccess(RequestAction.READ, bucketId); final VersionedFlowSnapshotMetadata latest = registryService.getLatestFlowSnapshotMetadata(bucketId, flowId); - linkService.populateSnapshotLinks(latest); + linkService.populateLinks(latest); return Response.status(Response.Status.OK).entity(latest).build(); } @@ -502,16 +502,16 @@ public class BucketFlowResource extends AuthorizableApplicationResource { private void populateLinksAndPermissions(VersionedFlowSnapshot snapshot) { if (snapshot.getSnapshotMetadata() != null) { - linkService.populateSnapshotLinks(snapshot.getSnapshotMetadata()); + linkService.populateLinks(snapshot.getSnapshotMetadata()); } if (snapshot.getFlow() != null) { - linkService.populateFlowLinks(snapshot.getFlow()); + linkService.populateLinks(snapshot.getFlow()); } if (snapshot.getBucket() != null) { permissionsService.populateBucketPermissions(snapshot.getBucket()); - linkService.populateBucketLinks(snapshot.getBucket()); + linkService.populateLinks(snapshot.getBucket()); } } http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/BucketResource.java ---------------------------------------------------------------------- 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 e905973..e7c0df4 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 @@ -118,7 +118,7 @@ public class BucketResource extends AuthorizableApplicationResource { publish(EventFactory.bucketCreated(createdBucket)); permissionsService.populateBucketPermissions(createdBucket); - linkService.populateBucketLinks(createdBucket); + linkService.populateLinks(createdBucket); return Response.status(Response.Status.OK).entity(createdBucket).build(); } @@ -152,7 +152,7 @@ public class BucketResource extends AuthorizableApplicationResource { final List<Bucket> buckets = registryService.getBuckets(authorizedBucketIds); permissionsService.populateBucketPermissions(buckets); - linkService.populateBucketLinks(buckets); + linkService.populateLinks(buckets); return Response.status(Response.Status.OK).entity(buckets).build(); } @@ -182,7 +182,7 @@ public class BucketResource extends AuthorizableApplicationResource { authorizeBucketAccess(RequestAction.READ, bucketId); final Bucket bucket = registryService.getBucket(bucketId); permissionsService.populateBucketPermissions(bucket); - linkService.populateBucketLinks(bucket); + linkService.populateLinks(bucket); return Response.status(Response.Status.OK).entity(bucket).build(); } @@ -233,7 +233,7 @@ public class BucketResource extends AuthorizableApplicationResource { publish(EventFactory.bucketUpdated(updatedBucket)); permissionsService.populateBucketPermissions(updatedBucket); - linkService.populateBucketLinks(updatedBucket); + linkService.populateLinks(updatedBucket); return Response.status(Response.Status.OK).entity(updatedBucket).build(); } http://git-wip-us.apache.org/repos/asf/nifi-registry/blob/f1e5aef7/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ExtensionRepositoryResource.java ---------------------------------------------------------------------- diff --git a/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ExtensionRepositoryResource.java b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ExtensionRepositoryResource.java new file mode 100644 index 0000000..46be907 --- /dev/null +++ b/nifi-registry-core/nifi-registry-web-api/src/main/java/org/apache/nifi/registry/web/api/ExtensionRepositoryResource.java @@ -0,0 +1,374 @@ +/* + * 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 io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import io.swagger.annotations.Authorization; +import io.swagger.annotations.Extension; +import io.swagger.annotations.ExtensionProperty; +import org.apache.nifi.registry.bucket.Bucket; +import org.apache.nifi.registry.bucket.BucketItem; +import org.apache.nifi.registry.event.EventService; +import org.apache.nifi.registry.extension.ExtensionBundleVersion; +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.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.service.extension.ExtensionBundleVersionCoordinate; +import org.apache.nifi.registry.web.link.LinkService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +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.Set; +import java.util.SortedSet; + +@Component +@Path("/extensions/repo") +@Api( + value = "extension_repository", + description = "Interact with extension bundles via the hierarchy of bucket/group/artifact/version.", + authorizations = { @Authorization("Authorization") } +) +public class ExtensionRepositoryResource extends AuthorizableApplicationResource { + + public static final String CONTENT_DISPOSITION_HEADER = "content-disposition"; + private final RegistryService registryService; + private final LinkService linkService; + + @Autowired + public ExtensionRepositoryResource( + final RegistryService registryService, + final LinkService linkService, + final AuthorizationService authorizationService, + final EventService eventService) { + super(authorizationService, eventService); + this.registryService = registryService; + this.linkService = linkService; + } + + @GET + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Gets the names of the buckets the current user is authorized for in order to browse the repo by bucket", + response = ExtensionRepoBucket.class, + responseContainer = "List" + ) + @ApiResponses({ + @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400), + @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), + @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), + @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<BucketItem>()).build(); + } + + final SortedSet<ExtensionRepoBucket> repoBuckets = registryService.getExtensionRepoBuckets(authorizedBucketIds); + linkService.populateFullLinks(repoBuckets, getBaseUri()); + return Response.status(Response.Status.OK).entity(repoBuckets).build(); + } + + @GET + @Path("{bucketName}") + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Gets the groups in the extension repository in the bucket with the given name", + response = ExtensionRepoGroup.class, + responseContainer = "List", + extensions = { + @Extension(name = "access-policy", properties = { + @ExtensionProperty(name = "action", value = "read"), + @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") }) + } + ) + @ApiResponses({ + @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400), + @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), + @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), + @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404), + @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) }) + public Response getExtensionRepoGroups( + @PathParam("bucketName") + @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()); + return Response.status(Response.Status.OK).entity(repoGroups).build(); + } + + @GET + @Path("{bucketName}/{groupId}") + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Gets the artifacts in the extension repository with the given group in the bucket with the given name", + response = ExtensionRepoArtifact.class, + responseContainer = "List", + extensions = { + @Extension(name = "access-policy", properties = { + @ExtensionProperty(name = "action", value = "read"), + @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") }) + } + ) + @ApiResponses({ + @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400), + @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), + @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), + @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404), + @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) }) + public Response getExtensionRepoArtifacts( + @PathParam("bucketName") + @ApiParam("The bucket name") + final String bucketName, + @PathParam("groupId") + @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()); + return Response.status(Response.Status.OK).entity(repoArtifacts).build(); + } + + @GET + @Path("{bucketName}/{groupId}/{artifactId}") + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Gets the versions of the artifact in the extension repository specified by the given bucket, group, artifact, and version", + response = ExtensionRepoVersionSummary.class, + responseContainer = "List", + extensions = { + @Extension(name = "access-policy", properties = { + @ExtensionProperty(name = "action", value = "read"), + @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") }) + } + ) + @ApiResponses({ + @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400), + @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), + @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), + @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404), + @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) }) + public Response getExtensionBundleVersions( + @PathParam("bucketName") + @ApiParam("The bucket name") + final String bucketName, + @PathParam("groupId") + @ApiParam("The group identifier") + final String groupId, + @PathParam("artifactId") + @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()); + return Response.status(Response.Status.OK).entity(repoVersions).build(); + } + + @GET + @Path("{bucketName}/{groupId}/{artifactId}/{version}") + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_JSON) + @ApiOperation( + value = "Gets the information about the version specified by the given bucket, group, artifact, and version", + response = ExtensionRepoVersion.class, + extensions = { + @Extension(name = "access-policy", properties = { + @ExtensionProperty(name = "action", value = "read"), + @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") }) + } + ) + @ApiResponses({ + @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400), + @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), + @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), + @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404), + @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) }) + public Response getExtensionBundleVersion( + @PathParam("bucketName") + @ApiParam("The bucket name") + final String bucketName, + @PathParam("groupId") + @ApiParam("The group identifier") + final String groupId, + @PathParam("artifactId") + @ApiParam("The artifact identifier") + final String artifactId, + @PathParam("version") + @ApiParam("The version") + final String version + ) { + final Bucket bucket = registryService.getBucketByName(bucketName); + authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier()); + + final ExtensionBundleVersionCoordinate versionCoordinate = new ExtensionBundleVersionCoordinate( + bucket.getIdentifier(), groupId, artifactId, version); + + final ExtensionBundleVersion bundleVersion = registryService.getExtensionBundleVersion(versionCoordinate); + + final String downloadUri = generateResourceUri( + "extensions", "repo", + bundleVersion.getBucket().getName(), + bundleVersion.getExtensionBundle().getGroupId(), + bundleVersion.getExtensionBundle().getArtifactId(), + bundleVersion.getVersionMetadata().getVersion(), + "content"); + + final String sha256Uri = generateResourceUri( + "extensions", "repo", + bundleVersion.getBucket().getName(), + bundleVersion.getExtensionBundle().getGroupId(), + bundleVersion.getExtensionBundle().getArtifactId(), + bundleVersion.getVersionMetadata().getVersion(), + "sha256"); + + final ExtensionRepoVersion repoVersion = new ExtensionRepoVersion(); + repoVersion.setDownloadLink(Link.fromUri(downloadUri).rel("content").build()); + repoVersion.setSha256Link(Link.fromUri(sha256Uri).rel("sha256").build()); + repoVersion.setSha256Supplied(bundleVersion.getVersionMetadata().getSha256Supplied()); + + return Response.ok(repoVersion).build(); + } + + @GET + @Path("{bucketName}/{groupId}/{artifactId}/{version}/content") + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.APPLICATION_OCTET_STREAM) + @ApiOperation( + value = "Gets the binary content of the extension bundle specified by the given bucket, group, artifact, and version", + response = byte[].class, + extensions = { + @Extension(name = "access-policy", properties = { + @ExtensionProperty(name = "action", value = "read"), + @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") }) + } + ) + @ApiResponses({ + @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400), + @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), + @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), + @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404), + @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) }) + public Response getExtensionBundleVersionContent( + @PathParam("bucketName") + @ApiParam("The bucket name") + final String bucketName, + @PathParam("groupId") + @ApiParam("The group identifier") + final String groupId, + @PathParam("artifactId") + @ApiParam("The artifact identifier") + final String artifactId, + @PathParam("version") + @ApiParam("The version") + final String version + ) { + final Bucket bucket = registryService.getBucketByName(bucketName); + authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier()); + + final ExtensionBundleVersionCoordinate versionCoordinate = new ExtensionBundleVersionCoordinate( + bucket.getIdentifier(), groupId, artifactId, version); + + final ExtensionBundleVersion bundleVersion = registryService.getExtensionBundleVersion(versionCoordinate); + final StreamingOutput streamingOutput = (output) -> registryService.writeExtensionBundleVersionContent(bundleVersion, output); + + return Response.ok(streamingOutput) + .header(CONTENT_DISPOSITION_HEADER,"attachment; filename = " + bundleVersion.getFilename()) + .build(); + } + + @GET + @Path("{bucketName}/{groupId}/{artifactId}/{version}/sha256") + @Consumes(MediaType.WILDCARD) + @Produces(MediaType.TEXT_PLAIN) + @ApiOperation( + value = "Gets the hex representation of the SHA-256 digest for the binary content of the version of the extension bundle", + response = String.class, + extensions = { + @Extension(name = "access-policy", properties = { + @ExtensionProperty(name = "action", value = "read"), + @ExtensionProperty(name = "resource", value = "/buckets/{bucketId}") }) + } + ) + @ApiResponses({ + @ApiResponse(code = 400, message = HttpStatusMessages.MESSAGE_400), + @ApiResponse(code = 401, message = HttpStatusMessages.MESSAGE_401), + @ApiResponse(code = 403, message = HttpStatusMessages.MESSAGE_403), + @ApiResponse(code = 404, message = HttpStatusMessages.MESSAGE_404), + @ApiResponse(code = 409, message = HttpStatusMessages.MESSAGE_409) }) + public Response getExtensionBundleVersionSha256( + @PathParam("bucketName") + @ApiParam("The bucket name") + final String bucketName, + @PathParam("groupId") + @ApiParam("The group identifier") + final String groupId, + @PathParam("artifactId") + @ApiParam("The artifact identifier") + final String artifactId, + @PathParam("version") + @ApiParam("The version") + final String version + ) { + final Bucket bucket = registryService.getBucketByName(bucketName); + authorizeBucketAccess(RequestAction.READ, bucket.getIdentifier()); + + final ExtensionBundleVersionCoordinate versionCoordinate = new ExtensionBundleVersionCoordinate( + bucket.getIdentifier(), groupId, artifactId, version); + + final ExtensionBundleVersion bundleVersion = registryService.getExtensionBundleVersion(versionCoordinate); + final String sha256Hex = bundleVersion.getVersionMetadata().getSha256(); + + return Response.ok(sha256Hex, MediaType.TEXT_PLAIN).build(); + } + + +}
