/* * Copyright © 2016 Cask Data, Inc. * * Licensed 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 co.cask.cdap.client; import co.cask.cdap.AppWithDataset; import co.cask.cdap.StandaloneTester; import co.cask.cdap.WordCountApp; import co.cask.cdap.WordCountMinusFlowApp; import co.cask.cdap.api.Config; import co.cask.cdap.api.data.format.FormatSpecification; import co.cask.cdap.api.data.schema.Schema; import co.cask.cdap.api.dataset.lib.KeyValueTable; import co.cask.cdap.api.dataset.table.Table; import co.cask.cdap.app.program.ManifestFields; import co.cask.cdap.client.app.AllProgramsApp; import co.cask.cdap.client.util.RESTClient; import co.cask.cdap.common.BadRequestException; import co.cask.cdap.common.NotFoundException; import co.cask.cdap.data2.metadata.dataset.MetadataDataset; import co.cask.cdap.data2.metadata.system.DatasetSystemMetadataWriter; import co.cask.cdap.metadata.MetadataHttpHandler; import co.cask.cdap.proto.Id; import co.cask.cdap.proto.NamespaceMeta; import co.cask.cdap.proto.ProgramType; import co.cask.cdap.proto.StreamProperties; import co.cask.cdap.proto.ViewSpecification; import co.cask.cdap.proto.artifact.AppRequest; import co.cask.cdap.proto.artifact.ArtifactSummary; import co.cask.cdap.proto.id.ApplicationId; import co.cask.cdap.proto.id.DatasetId; import co.cask.cdap.proto.id.Ids; import co.cask.cdap.proto.id.NamespaceId; import co.cask.cdap.proto.id.ProgramId; import co.cask.cdap.proto.id.StreamId; import co.cask.cdap.proto.metadata.Metadata; import co.cask.cdap.proto.metadata.MetadataRecord; import co.cask.cdap.proto.metadata.MetadataScope; import co.cask.cdap.proto.metadata.MetadataSearchResultRecord; import co.cask.cdap.proto.metadata.MetadataSearchTargetType; import co.cask.common.http.HttpRequest; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import org.jboss.netty.handler.codec.http.HttpResponseStatus; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.jar.Manifest; import javax.annotation.Nullable; /** * Tests for {@link MetadataHttpHandler} */ public class MetadataHttpHandlerTestRun extends MetadataTestBase { private final Id.Application application = Id.Application.from(Id.Namespace.DEFAULT, AppWithDataset.class.getSimpleName()); private final Id.Artifact artifactId = Id.Artifact.from(Id.Namespace.DEFAULT, application.getId(), "1.0.0"); private final Id.Program pingService = Id.Program.from(application, ProgramType.SERVICE, "PingService"); private final Id.DatasetInstance myds = Id.DatasetInstance.from(Id.Namespace.DEFAULT, "myds"); private final Id.Stream mystream = Id.Stream.from(Id.Namespace.DEFAULT, "mystream"); private final Id.Stream.View myview = Id.Stream.View.from(mystream, "myview"); private final Id.Application nonExistingApp = Id.Application.from("blah", AppWithDataset.class.getSimpleName()); private final Id.Service nonExistingService = Id.Service.from(nonExistingApp, "PingService"); private final Id.DatasetInstance nonExistingDataset = Id.DatasetInstance.from("blah", "myds"); private final Id.Stream nonExistingStream = Id.Stream.from("blah", "mystream"); private final Id.Stream.View nonExistingView = Id.Stream.View.from(nonExistingStream, "myView"); private final Id.Artifact nonExistingArtifact = Id.Artifact.from(Id.Namespace.from("blah"), "art", "1.0.0"); @Before public void before() throws Exception { addAppArtifact(artifactId, AppWithDataset.class); AppRequest<Config> appRequest = new AppRequest<>( new ArtifactSummary(artifactId.getName(), artifactId.getVersion().getVersion())); appClient.deploy(application, appRequest); FormatSpecification format = new FormatSpecification("csv", null, null); ViewSpecification viewSpec = new ViewSpecification(format, null); streamViewClient.createOrUpdate(myview, viewSpec); } @After public void after() throws Exception { appClient.delete(application); artifactClient.delete(artifactId); namespaceClient.delete(NamespaceId.DEFAULT.toId()); } @Test public void testProperties() throws Exception { // should fail because we haven't provided any metadata in the request addProperties(application, null, BadRequestException.class); String multiWordValue = "wow1 WoW2 - WOW3 - wow4_woW5 wow6"; Map<String, String> appProperties = ImmutableMap.of("aKey", "aValue", "multiword", multiWordValue); addProperties(application, appProperties); // should fail because we haven't provided any metadata in the request addProperties(pingService, null, BadRequestException.class); Map<String, String> serviceProperties = ImmutableMap.of("sKey", "sValue", "sK", "sV"); addProperties(pingService, serviceProperties); // should fail because we haven't provided any metadata in the request addProperties(myds, null, BadRequestException.class); Map<String, String> datasetProperties = ImmutableMap.of("dKey", "dValue", "dK", "dV"); addProperties(myds, datasetProperties); // should fail because we haven't provided any metadata in the request addProperties(mystream, null, BadRequestException.class); Map<String, String> streamProperties = ImmutableMap.of("stKey", "stValue", "stK", "stV", "multiword", multiWordValue); addProperties(mystream, streamProperties); addProperties(myview, null, BadRequestException.class); Map<String, String> viewProperties = ImmutableMap.of("viewKey", "viewValue", "viewK", "viewV"); addProperties(myview, viewProperties); // should fail because we haven't provided any metadata in the request addProperties(artifactId, null, BadRequestException.class); Map<String, String> artifactProperties = ImmutableMap.of("rKey", "rValue", "rK", "rV"); addProperties(artifactId, artifactProperties); // retrieve properties and verify Map<String, String> properties = getProperties(application, MetadataScope.USER); Assert.assertEquals(appProperties, properties); properties = getProperties(pingService, MetadataScope.USER); Assert.assertEquals(serviceProperties, properties); properties = getProperties(myds, MetadataScope.USER); Assert.assertEquals(datasetProperties, properties); properties = getProperties(mystream, MetadataScope.USER); Assert.assertEquals(streamProperties, properties); properties = getProperties(myview, MetadataScope.USER); Assert.assertEquals(viewProperties, properties); properties = getProperties(artifactId, MetadataScope.USER); Assert.assertEquals(artifactProperties, properties); // test search for application Set<MetadataSearchResultRecord> expected = ImmutableSet.of( new MetadataSearchResultRecord(application) ); Set<MetadataSearchResultRecord> searchProperties = searchMetadata(Id.Namespace.DEFAULT, "aKey:aValue", MetadataSearchTargetType.APP); Assert.assertEquals(expected, searchProperties); searchProperties = searchMetadata(Id.Namespace.DEFAULT, "multiword:wow1", MetadataSearchTargetType.APP); Assert.assertEquals(expected, searchProperties); searchProperties = searchMetadata(Id.Namespace.DEFAULT, "multiword:woW5", MetadataSearchTargetType.APP); Assert.assertEquals(expected, searchProperties); searchProperties = searchMetadata(Id.Namespace.DEFAULT, "WOW3", MetadataSearchTargetType.APP); Assert.assertEquals(expected, searchProperties); // test search for stream searchProperties = searchMetadata(Id.Namespace.DEFAULT, "stKey:stValue", MetadataSearchTargetType.STREAM); expected = ImmutableSet.of( new MetadataSearchResultRecord(mystream) ); Assert.assertEquals(expected, searchProperties); // test search for view with lowercase key value when metadata was stored in mixed case searchProperties = searchMetadata(Id.Namespace.DEFAULT, "viewkey:viewvalue", MetadataSearchTargetType.VIEW); expected = ImmutableSet.of( new MetadataSearchResultRecord(myview) ); Assert.assertEquals(expected, searchProperties); // test search for view with lowercase value when metadata was stored in mixed case searchProperties = searchMetadata(Id.Namespace.DEFAULT, "viewvalue", MetadataSearchTargetType.VIEW); expected = ImmutableSet.of( new MetadataSearchResultRecord(myview) ); Assert.assertEquals(expected, searchProperties); // test search for artifact searchProperties = searchMetadata(Id.Namespace.DEFAULT, "rKey:rValue", MetadataSearchTargetType.ARTIFACT); expected = ImmutableSet.of( new MetadataSearchResultRecord(artifactId) ); Assert.assertEquals(expected, searchProperties); expected = ImmutableSet.of( new MetadataSearchResultRecord(application), new MetadataSearchResultRecord(mystream) ); searchProperties = searchMetadata(Id.Namespace.DEFAULT, "multiword:w*", MetadataSearchTargetType.ALL); Assert.assertEquals(2, searchProperties.size()); Assert.assertEquals(expected, searchProperties); searchProperties = searchMetadata(Id.Namespace.DEFAULT, "multiword:*", MetadataSearchTargetType.ALL); Assert.assertEquals(2, searchProperties.size()); Assert.assertEquals(expected, searchProperties); searchProperties = searchMetadata(Id.Namespace.DEFAULT, "wo*", MetadataSearchTargetType.ALL); Assert.assertEquals(2, searchProperties.size()); Assert.assertEquals(expected, searchProperties); // test prefix search for service searchProperties = searchMetadata(Id.Namespace.DEFAULT, "sKey:s*", MetadataSearchTargetType.ALL); expected = ImmutableSet.of( new MetadataSearchResultRecord(pingService) ); Assert.assertEquals(expected, searchProperties); // search without any target param searchProperties = searchMetadata(Id.Namespace.DEFAULT, "sKey:s*"); Assert.assertEquals(expected, searchProperties); // Should get empty searchProperties = searchMetadata(Id.Namespace.DEFAULT, "sKey:s"); Assert.assertTrue(searchProperties.size() == 0); searchProperties = searchMetadata(Id.Namespace.DEFAULT, "s"); Assert.assertTrue(searchProperties.size() == 0); // search non-existent property should return empty set searchProperties = searchMetadata(Id.Namespace.DEFAULT, "NullKey:s*"); Assert.assertEquals(ImmutableSet.<MetadataSearchResultRecord>of(), searchProperties); // search invalid ns should return empty set searchProperties = searchMetadata(Id.Namespace.from("invalidnamespace"), "sKey:s*"); Assert.assertEquals(ImmutableSet.of(), searchProperties); // test removal removeProperties(application); Assert.assertTrue(getProperties(application, MetadataScope.USER).isEmpty()); removeProperty(pingService, "sKey"); removeProperty(pingService, "sK"); Assert.assertTrue(getProperties(pingService, MetadataScope.USER).isEmpty()); removeProperty(myds, "dKey"); Assert.assertEquals(ImmutableMap.of("dK", "dV"), getProperties(myds, MetadataScope.USER)); removeProperty(mystream, "stK"); removeProperty(mystream, "stKey"); Assert.assertEquals(ImmutableMap.of("multiword", multiWordValue), getProperties(mystream, MetadataScope.USER)); removeProperty(myview, "viewK"); Assert.assertEquals(ImmutableMap.of("viewKey", "viewValue"), getProperties(myview, MetadataScope.USER)); // cleanup removeProperties(myview); removeProperties(application); removeProperties(pingService); removeProperties(myds); removeProperties(mystream); removeProperties(artifactId); Assert.assertTrue(getProperties(application, MetadataScope.USER).isEmpty()); Assert.assertTrue(getProperties(pingService, MetadataScope.USER).isEmpty()); Assert.assertTrue(getProperties(myds, MetadataScope.USER).isEmpty()); Assert.assertTrue(getProperties(mystream, MetadataScope.USER).isEmpty()); Assert.assertTrue(getProperties(myview, MetadataScope.USER).isEmpty()); Assert.assertTrue(getProperties(artifactId, MetadataScope.USER).isEmpty()); // non-existing namespace addProperties(nonExistingApp, appProperties, NotFoundException.class); addProperties(nonExistingService, serviceProperties, NotFoundException.class); addProperties(nonExistingDataset, datasetProperties, NotFoundException.class); addProperties(nonExistingStream, streamProperties, NotFoundException.class); addProperties(nonExistingView, streamProperties, NotFoundException.class); addProperties(nonExistingArtifact, artifactProperties, NotFoundException.class); } @Test public void testTags() throws Exception { // should fail because we haven't provided any metadata in the request addTags(application, null, BadRequestException.class); Set<String> appTags = ImmutableSet.of("aTag", "aT", "Wow-WOW1", "WOW_WOW2"); addTags(application, appTags); // should fail because we haven't provided any metadata in the request addTags(pingService, null, BadRequestException.class); Set<String> serviceTags = ImmutableSet.of("sTag", "sT"); addTags(pingService, serviceTags); addTags(myds, null, BadRequestException.class); Set<String> datasetTags = ImmutableSet.of("dTag", "dT"); addTags(myds, datasetTags); addTags(mystream, null, BadRequestException.class); Set<String> streamTags = ImmutableSet.of("stTag", "stT", "Wow-WOW1", "WOW_WOW2"); addTags(mystream, streamTags); addTags(myview, null, BadRequestException.class); Set<String> viewTags = ImmutableSet.of("viewTag", "viewT"); addTags(myview, viewTags); Set<String> artifactTags = ImmutableSet.of("rTag", "rT"); addTags(artifactId, artifactTags); // retrieve tags and verify Set<String> tags = getTags(application, MetadataScope.USER); Assert.assertTrue(tags.containsAll(appTags)); Assert.assertTrue(appTags.containsAll(tags)); tags = getTags(pingService, MetadataScope.USER); Assert.assertTrue(tags.containsAll(serviceTags)); Assert.assertTrue(serviceTags.containsAll(tags)); tags = getTags(myds, MetadataScope.USER); Assert.assertTrue(tags.containsAll(datasetTags)); Assert.assertTrue(datasetTags.containsAll(tags)); tags = getTags(mystream, MetadataScope.USER); Assert.assertTrue(tags.containsAll(streamTags)); Assert.assertTrue(streamTags.containsAll(tags)); tags = getTags(myview, MetadataScope.USER); Assert.assertTrue(tags.containsAll(viewTags)); Assert.assertTrue(viewTags.containsAll(tags)); tags = getTags(artifactId, MetadataScope.USER); Assert.assertTrue(tags.containsAll(artifactTags)); Assert.assertTrue(artifactTags.containsAll(tags)); // test search for stream Set<MetadataSearchResultRecord> searchTags = searchMetadata(Id.Namespace.DEFAULT, "stT", MetadataSearchTargetType.STREAM); Set<MetadataSearchResultRecord> expected = ImmutableSet.of( new MetadataSearchResultRecord(mystream) ); Assert.assertEquals(expected, searchTags); searchTags = searchMetadata(Id.Namespace.DEFAULT, "Wow", MetadataSearchTargetType.STREAM); expected = ImmutableSet.of( new MetadataSearchResultRecord(mystream) ); Assert.assertEquals(expected, searchTags); // test search for view with lowercase tag when metadata was stored in mixed case searchTags = searchMetadata(Id.Namespace.DEFAULT, "viewtag", MetadataSearchTargetType.VIEW); expected = ImmutableSet.of( new MetadataSearchResultRecord(myview) ); Assert.assertEquals(expected, searchTags); // test prefix search, should match stream and application searchTags = searchMetadata(Id.Namespace.DEFAULT, "Wow*", MetadataSearchTargetType.ALL); expected = ImmutableSet.of( new MetadataSearchResultRecord(application), new MetadataSearchResultRecord(mystream) ); Assert.assertEquals(expected, searchTags); // search without any target param searchTags = searchMetadata(Id.Namespace.DEFAULT, "Wow*"); Assert.assertEquals(expected, searchTags); // search non-existent tags should return empty set searchTags = searchMetadata(Id.Namespace.DEFAULT, "NullKey"); Assert.assertEquals(ImmutableSet.<MetadataSearchResultRecord>of(), searchTags); // test removal removeTag(application, "aTag"); Assert.assertEquals(ImmutableSet.of("aT", "Wow-WOW1", "WOW_WOW2"), getTags(application, MetadataScope.USER)); removeTags(pingService); Assert.assertTrue(getTags(pingService, MetadataScope.USER).isEmpty()); removeTags(pingService); Assert.assertTrue(getTags(pingService, MetadataScope.USER).isEmpty()); removeTag(myds, "dT"); Assert.assertEquals(ImmutableSet.of("dTag"), getTags(myds, MetadataScope.USER)); removeTag(mystream, "stT"); removeTag(mystream, "stTag"); removeTag(mystream, "Wow-WOW1"); removeTag(mystream, "WOW_WOW2"); removeTag(myview, "viewT"); removeTag(myview, "viewTag"); Assert.assertTrue(getTags(mystream, MetadataScope.USER).isEmpty()); removeTag(artifactId, "rTag"); removeTag(artifactId, "rT"); Assert.assertTrue(getTags(artifactId, MetadataScope.USER).isEmpty()); // cleanup removeTags(application); removeTags(pingService); removeTags(myds); removeTags(mystream); removeTags(myview); removeTags(artifactId); Assert.assertTrue(getTags(application, MetadataScope.USER).isEmpty()); Assert.assertTrue(getTags(pingService, MetadataScope.USER).isEmpty()); Assert.assertTrue(getTags(myds, MetadataScope.USER).isEmpty()); Assert.assertTrue(getTags(mystream, MetadataScope.USER).isEmpty()); Assert.assertTrue(getTags(artifactId, MetadataScope.USER).isEmpty()); // non-existing namespace addTags(nonExistingApp, appTags, NotFoundException.class); addTags(nonExistingService, serviceTags, NotFoundException.class); addTags(nonExistingDataset, datasetTags, NotFoundException.class); addTags(nonExistingStream, streamTags, NotFoundException.class); addTags(nonExistingView, streamTags, NotFoundException.class); addTags(nonExistingArtifact, artifactTags, NotFoundException.class); } @Test public void testMetadata() throws Exception { assertCleanState(MetadataScope.USER); // Remove when nothing exists removeAllMetadata(); assertCleanState(MetadataScope.USER); // Add some properties and tags Map<String, String> appProperties = ImmutableMap.of("aKey", "aValue"); Map<String, String> serviceProperties = ImmutableMap.of("sKey", "sValue"); Map<String, String> datasetProperties = ImmutableMap.of("dKey", "dValue"); Map<String, String> streamProperties = ImmutableMap.of("stKey", "stValue"); Map<String, String> viewProperties = ImmutableMap.of("viewKey", "viewValue"); Map<String, String> artifactProperties = ImmutableMap.of("rKey", "rValue"); Set<String> appTags = ImmutableSet.of("aTag"); Set<String> serviceTags = ImmutableSet.of("sTag"); Set<String> datasetTags = ImmutableSet.of("dTag"); Set<String> streamTags = ImmutableSet.of("stTag"); Set<String> viewTags = ImmutableSet.of("viewTag"); Set<String> artifactTags = ImmutableSet.of("rTag"); addProperties(application, appProperties); addProperties(pingService, serviceProperties); addProperties(myds, datasetProperties); addProperties(mystream, streamProperties); addProperties(myview, viewProperties); addProperties(artifactId, artifactProperties); addTags(application, appTags); addTags(pingService, serviceTags); addTags(myds, datasetTags); addTags(mystream, streamTags); addTags(myview, viewTags); addTags(artifactId, artifactTags); // verify app Set<MetadataRecord> metadataRecords = getMetadata(application, MetadataScope.USER); Assert.assertEquals(1, metadataRecords.size()); MetadataRecord metadata = metadataRecords.iterator().next(); Assert.assertEquals(MetadataScope.USER, metadata.getScope()); Assert.assertEquals(application, metadata.getEntityId()); Assert.assertEquals(appProperties, metadata.getProperties()); Assert.assertEquals(appTags, metadata.getTags()); // verify service metadataRecords = getMetadata(pingService, MetadataScope.USER); Assert.assertEquals(1, metadataRecords.size()); metadata = metadataRecords.iterator().next(); Assert.assertEquals(MetadataScope.USER, metadata.getScope()); Assert.assertEquals(pingService, metadata.getEntityId()); Assert.assertEquals(serviceProperties, metadata.getProperties()); Assert.assertEquals(serviceTags, metadata.getTags()); // verify dataset metadataRecords = getMetadata(myds, MetadataScope.USER); Assert.assertEquals(1, metadataRecords.size()); metadata = metadataRecords.iterator().next(); Assert.assertEquals(MetadataScope.USER, metadata.getScope()); Assert.assertEquals(myds, metadata.getEntityId()); Assert.assertEquals(datasetProperties, metadata.getProperties()); Assert.assertEquals(datasetTags, metadata.getTags()); // verify stream metadataRecords = getMetadata(mystream, MetadataScope.USER); Assert.assertEquals(1, metadataRecords.size()); metadata = metadataRecords.iterator().next(); Assert.assertEquals(MetadataScope.USER, metadata.getScope()); Assert.assertEquals(mystream, metadata.getEntityId()); Assert.assertEquals(streamProperties, metadata.getProperties()); Assert.assertEquals(streamTags, metadata.getTags()); // verify view metadataRecords = getMetadata(myview, MetadataScope.USER); Assert.assertEquals(1, metadataRecords.size()); metadata = metadataRecords.iterator().next(); Assert.assertEquals(MetadataScope.USER, metadata.getScope()); Assert.assertEquals(myview, metadata.getEntityId()); Assert.assertEquals(viewProperties, metadata.getProperties()); Assert.assertEquals(viewTags, metadata.getTags()); // verify artifact metadataRecords = getMetadata(artifactId, MetadataScope.USER); Assert.assertEquals(1, metadataRecords.size()); metadata = metadataRecords.iterator().next(); Assert.assertEquals(MetadataScope.USER, metadata.getScope()); Assert.assertEquals(artifactId, metadata.getEntityId()); Assert.assertEquals(artifactProperties, metadata.getProperties()); Assert.assertEquals(artifactTags, metadata.getTags()); // cleanup removeAllMetadata(); assertCleanState(MetadataScope.USER); } @Test public void testDeleteApplication() throws Exception { namespaceClient.create(new NamespaceMeta.Builder().setName(TEST_NAMESPACE1.toId()).build()); appClient.deploy(TEST_NAMESPACE1.toId(), createAppJarFile(WordCountApp.class)); Id.Program programId = new ProgramId(TEST_NAMESPACE1.getNamespace(), "WordCountApp", ProgramType.FLOW, "WordCountFlow").toId(); // Set some properties metadata Map<String, String> flowProperties = ImmutableMap.of("sKey", "sValue", "sK", "sV"); addProperties(programId, flowProperties); // Get properties Map<String, String> properties = getProperties(programId, MetadataScope.USER); Assert.assertEquals(2, properties.size()); // Delete the App after stopping the flow appClient.delete(programId.getApplication()); // Delete again should throw not found exception try { appClient.delete(programId.getApplication()); Assert.fail("Expected NotFoundException"); } catch (NotFoundException e) { // expected } // Now try to get from invalid entity should throw 404. getPropertiesFromInvalidEntity(programId); } @Test public void testInvalidEntities() throws IOException { Id.Program nonExistingProgram = Id.Program.from(application, ProgramType.SERVICE, "NonExistingService"); Id.DatasetInstance nonExistingDataset = Id.DatasetInstance.from(Id.Namespace.DEFAULT, "NonExistingDataset"); Id.Stream nonExistingStream = Id.Stream.from(Id.Namespace.DEFAULT, "NonExistingStream"); Id.Stream.View nonExistingView = Id.Stream.View.from(mystream, "NonExistingView"); Id.Application nonExistingApp = Id.Application.from(Id.Namespace.DEFAULT, "NonExistingApp"); Map<String, String> properties = ImmutableMap.of("aKey", "aValue", "aK", "aV"); addProperties(nonExistingApp, properties, NotFoundException.class); addProperties(nonExistingProgram, properties, NotFoundException.class); addProperties(nonExistingDataset, properties, NotFoundException.class); addProperties(nonExistingView, properties, NotFoundException.class); addProperties(nonExistingStream, properties, NotFoundException.class); } @Test public void testInvalidProperties() throws IOException { // Test length StringBuilder builder = new StringBuilder(100); for (int i = 0; i < 100; i++) { builder.append("a"); } Map<String, String> properties = ImmutableMap.of("aKey", builder.toString()); addProperties(application, properties, BadRequestException.class); properties = ImmutableMap.of(builder.toString(), "aValue"); addProperties(application, properties, BadRequestException.class); // Try to add tag as property properties = ImmutableMap.of("tags", "aValue"); addProperties(application, properties, BadRequestException.class); // Invalid chars properties = ImmutableMap.of("aKey$", "aValue"); addProperties(application, properties, BadRequestException.class); properties = ImmutableMap.of("aKey", "aValue$"); addProperties(application, properties, BadRequestException.class); } @Test public void testInvalidTags() throws IOException { // Invalid chars Set<String> tags = ImmutableSet.of("aTag$"); addTags(application, tags, BadRequestException.class); // Test length StringBuilder builder = new StringBuilder(100); for (int i = 0; i < 100; i++) { builder.append("a"); } tags = ImmutableSet.of(builder.toString()); addTags(application, tags, BadRequestException.class); } @Test public void testDeletedProgramHandlerStage() throws Exception { appClient.deploy(TEST_NAMESPACE1.toId(), createAppJarFile(WordCountApp.class)); Id.Program program = Id.Program.from(TEST_NAMESPACE1.toId(), "WordCountApp", ProgramType.FLOW, "WordCountFlow"); // Set some properties metadata Map<String, String> flowProperties = ImmutableMap.of("sKey", "sValue", "sK", "sV"); addProperties(program, flowProperties); // Get properties Map<String, String> properties = getProperties(program, MetadataScope.USER); Assert.assertEquals(2, properties.size()); // Deploy WordCount App without Flow program. No need to start/stop the flow. appClient.deploy(TEST_NAMESPACE1.toId(), createAppJarFile(WordCountMinusFlowApp.class)); // Get properties from deleted (flow) program - should return 404 getPropertiesFromInvalidEntity(program); // Delete the App after stopping the flow final Id.Application wordCountApp = Id.Application.from(TEST_NAMESPACE1.toId(), "WordCountApp"); appClient.delete(wordCountApp); } @Test public void testSystemMetadataRetrieval() throws Exception { appClient.deploy(Id.Namespace.DEFAULT, createAppJarFile(AllProgramsApp.class)); // verify stream system metadata Id.Stream streamId = Id.Stream.from(Id.Namespace.DEFAULT, AllProgramsApp.STREAM_NAME); Set<String> streamSystemTags = getTags(streamId, MetadataScope.SYSTEM); Assert.assertEquals(ImmutableSet.of(AllProgramsApp.STREAM_NAME), streamSystemTags); Map<String, String> streamSystemProperties = getProperties(streamId, MetadataScope.SYSTEM); // Verify create time exists, and is within the past hour final String creationTime = "creation-time"; String description = "description"; String schema = "schema"; String ttl = "ttl"; Assert.assertTrue("Expected creation time to exist but it does not", streamSystemProperties.containsKey(creationTime)); long createTime = Long.parseLong(streamSystemProperties.get(creationTime)); Assert.assertTrue("Stream create time should be within the last hour - " + createTime, createTime > System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1)); Assert.assertEquals( ImmutableMap.of(schema, Schema.recordOf("stringBody", Schema.Field.of("body", Schema.of(Schema.Type.STRING))).toString(), ttl, String.valueOf(Long.MAX_VALUE), description, "test stream", creationTime, String.valueOf(createTime) ), streamSystemProperties ); // Update stream properties and verify metadata got updated (except creation time and description) long newTtl = 100000L; streamClient.setStreamProperties(streamId, new StreamProperties(newTtl, null, null)); streamSystemProperties = getProperties(streamId, MetadataScope.SYSTEM); Assert.assertEquals( ImmutableMap.of(schema, Schema.recordOf("stringBody", Schema.Field.of("body", Schema.of(Schema.Type.STRING))).toString(), ttl, String.valueOf(newTtl * 1000), description, "test stream", creationTime, String.valueOf(createTime) ), streamSystemProperties ); Set<MetadataRecord> streamSystemMetadata = getMetadata(streamId, MetadataScope.SYSTEM); Assert.assertEquals( ImmutableSet.of(new MetadataRecord(streamId, MetadataScope.SYSTEM, streamSystemProperties, streamSystemTags)), streamSystemMetadata); // create view and verify view system metadata Id.Stream.View view = Id.Stream.View.from(streamId, "view"); Schema viewSchema = Schema.recordOf("record", Schema.Field.of("viewBody", Schema.nullableOf(Schema.of(Schema.Type.BYTES)))); streamViewClient.createOrUpdate(view, new ViewSpecification(new FormatSpecification("format", viewSchema))); Set<String> viewSystemTags = getTags(view, MetadataScope.SYSTEM); Assert.assertEquals(ImmutableSet.of("view", AllProgramsApp.STREAM_NAME), viewSystemTags); Map<String, String> viewSystemProperties = getProperties(view, MetadataScope.SYSTEM); Assert.assertEquals(viewSchema.toString(), viewSystemProperties.get(schema)); ImmutableSet<String> viewUserTags = ImmutableSet.of("viewTag"); addTags(view, viewUserTags); Assert.assertEquals( ImmutableSet.of(new MetadataRecord(view, MetadataScope.USER, ImmutableMap.<String, String>of(), viewUserTags), new MetadataRecord(view, MetadataScope.SYSTEM, viewSystemProperties, viewSystemTags)), getMetadata(view) ); // verify dataset system metadata Id.DatasetInstance datasetInstance = Id.DatasetInstance.from(Id.Namespace.DEFAULT, AllProgramsApp.DATASET_NAME); Set<String> dsSystemTags = getTags(datasetInstance, MetadataScope.SYSTEM); Assert.assertEquals( ImmutableSet.of(AllProgramsApp.DATASET_NAME, DatasetSystemMetadataWriter.BATCH_TAG, DatasetSystemMetadataWriter.EXPLORE_TAG), dsSystemTags); Map<String, String> dsSystemProperties = getProperties(datasetInstance, MetadataScope.SYSTEM); // Verify create time exists, and is within the past hour Assert.assertTrue("Expected creation time to exist but it does not", dsSystemProperties.containsKey(creationTime)); createTime = Long.parseLong(dsSystemProperties.get(creationTime)); Assert.assertTrue("Dataset create time should be within the last hour - " + createTime, createTime > System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1)); // Now remove create time and assert all other system properties Assert.assertEquals( ImmutableMap.of( "type", KeyValueTable.class.getName(), description, "test dataset", creationTime, String.valueOf(createTime) ), dsSystemProperties ); //Update properties, and make sure that system metadata gets updated (except create time) datasetClient.update(datasetInstance, ImmutableMap.of(Table.PROPERTY_TTL, "100000")); dsSystemProperties = getProperties(datasetInstance, MetadataScope.SYSTEM); Assert.assertEquals( ImmutableMap.of( "type", KeyValueTable.class.getName(), description, "test dataset", ttl, "100000", creationTime, String.valueOf(createTime) ), dsSystemProperties ); // verify artifact metadata Id.Artifact artifactId = getArtifactId(); Assert.assertEquals( ImmutableSet.of( new MetadataRecord(artifactId, MetadataScope.SYSTEM, ImmutableMap.<String, String>of(), ImmutableSet.of(AllProgramsApp.class.getSimpleName())) ), getMetadata(artifactId, MetadataScope.SYSTEM) ); // verify app system metadata Id.Application app = Id.Application.from(Id.Namespace.DEFAULT, AllProgramsApp.NAME); Assert.assertEquals( ImmutableMap.builder() .put(ProgramType.FLOW.getPrettyName() + MetadataDataset.KEYVALUE_SEPARATOR + AllProgramsApp.NoOpFlow.NAME, AllProgramsApp.NoOpFlow.NAME) .put(ProgramType.MAPREDUCE.getPrettyName() + MetadataDataset.KEYVALUE_SEPARATOR + AllProgramsApp.NoOpMR.NAME, AllProgramsApp.NoOpMR.NAME) .put(ProgramType.MAPREDUCE.getPrettyName() + MetadataDataset.KEYVALUE_SEPARATOR + AllProgramsApp.NoOpMR2.NAME, AllProgramsApp.NoOpMR2.NAME) .put(ProgramType.SERVICE.getPrettyName() + MetadataDataset.KEYVALUE_SEPARATOR + AllProgramsApp.NoOpService.NAME, AllProgramsApp.NoOpService.NAME) .put(ProgramType.SPARK.getPrettyName() + MetadataDataset.KEYVALUE_SEPARATOR + AllProgramsApp.NoOpSpark.NAME, AllProgramsApp.NoOpSpark.NAME) .put(ProgramType.WORKER.getPrettyName() + MetadataDataset.KEYVALUE_SEPARATOR + AllProgramsApp.NoOpWorker.NAME, AllProgramsApp.NoOpWorker.NAME) .put(ProgramType.WORKFLOW.getPrettyName() + MetadataDataset.KEYVALUE_SEPARATOR + AllProgramsApp.NoOpWorkflow.NAME, AllProgramsApp.NoOpWorkflow.NAME) .put("schedule" + MetadataDataset.KEYVALUE_SEPARATOR + AllProgramsApp.SCHEDULE_NAME, AllProgramsApp.SCHEDULE_NAME + MetadataDataset.KEYVALUE_SEPARATOR + AllProgramsApp.SCHEDULE_DESCRIPTION) .build(), getProperties(app, MetadataScope.SYSTEM)); Assert.assertEquals(ImmutableSet.of(AllProgramsApp.class.getSimpleName(), AllProgramsApp.NAME), getTags(app, MetadataScope.SYSTEM)); // verify program system metadata assertProgramSystemMetadata(Id.Program.from(app, ProgramType.FLOW, AllProgramsApp.NoOpFlow.NAME), "Realtime"); assertProgramSystemMetadata(Id.Program.from(app, ProgramType.WORKER, AllProgramsApp.NoOpWorker.NAME), "Realtime"); assertProgramSystemMetadata(Id.Program.from(app, ProgramType.SERVICE, AllProgramsApp.NoOpService.NAME), "Realtime"); assertProgramSystemMetadata(Id.Program.from(app, ProgramType.MAPREDUCE, AllProgramsApp.NoOpMR.NAME), "Batch"); assertProgramSystemMetadata(Id.Program.from(app, ProgramType.SPARK, AllProgramsApp.NoOpSpark.NAME), "Batch"); assertProgramSystemMetadata(Id.Program.from(app, ProgramType.WORKFLOW, AllProgramsApp.NoOpWorkflow.NAME), "Batch"); } @Test public void testSearchUsingSystemMetadata() throws Exception { appClient.deploy(Id.Namespace.DEFAULT, createAppJarFile(AllProgramsApp.class)); Id.Application app = Id.Application.from(Id.Namespace.DEFAULT, AllProgramsApp.NAME); Id.Artifact artifact = getArtifactId(); try { // search artifacts assertArtifactSearch(); // search app assertAppSearch(app, artifact); // search programs assertProgramSearch(app); // search data entities assertDataEntitySearch(); } finally { // cleanup appClient.delete(app); artifactClient.delete(artifact); } } @Test public void testSystemScopeArtifacts() throws Exception { // add a system artifact. currently can't do this through the rest api (by design) // so bypass it and use the repository directly Id.Artifact systemId = Id.Artifact.from(Id.Namespace.SYSTEM, "wordcount", "1.0.0"); File systemArtifact = createArtifactJarFile(WordCountApp.class, "wordcount", "1.0.0", new Manifest()); StandaloneTester tester = STANDALONE.get(); tester.addSystemArtifact(systemId.getName(), systemId.getVersion(), systemArtifact, null); // verify that user metadata can be added for system-scope artifacts Map<String, String> userProperties = ImmutableMap.of("systemArtifactKey", "systemArtifactValue"); ImmutableSet<String> userTags = ImmutableSet.of("systemArtifactTag"); addProperties(systemId, userProperties); addTags(systemId, userTags); // verify that user and system metadata can be retrieved for system-scope artifacts Assert.assertEquals( ImmutableSet.of( new MetadataRecord(systemId, MetadataScope.USER, userProperties, userTags), new MetadataRecord(systemId, MetadataScope.SYSTEM, ImmutableMap.<String, String>of(), ImmutableSet.of("wordcount")) ), getMetadata(systemId) ); // verify that system scope artifacts can be returned by a search in the default namespace // with no target type Assert.assertEquals( ImmutableSet.of(new MetadataSearchResultRecord(systemId)), searchMetadata(Id.Namespace.DEFAULT, "system*") ); // with target type as artifact Assert.assertEquals( ImmutableSet.of(new MetadataSearchResultRecord(systemId)), searchMetadata(Id.Namespace.DEFAULT, "system*", MetadataSearchTargetType.ARTIFACT) ); // verify that user metadata can be deleted for system-scope artifacts removeMetadata(systemId); Assert.assertEquals( ImmutableSet.of( new MetadataRecord(systemId, MetadataScope.USER, ImmutableMap.<String, String>of(), ImmutableSet.<String>of()), new MetadataRecord(systemId, MetadataScope.SYSTEM, ImmutableMap.<String, String>of(), ImmutableSet.of("wordcount")) ), getMetadata(systemId) ); artifactClient.delete(systemId); } @Test public void testScopeQueryParam() throws Exception { appClient.deploy(Id.Namespace.DEFAULT, createAppJarFile(WordCountApp.class)); Id.Application app = Id.Application.from(Id.Namespace.DEFAULT, WordCountApp.class.getSimpleName()); RESTClient restClient = new RESTClient(clientConfig); URL url = clientConfig.resolveNamespacedURLV3(Id.Namespace.DEFAULT, "apps/WordCountApp/metadata?scope=system"); Assert.assertEquals( HttpResponseStatus.OK.getCode(), restClient.execute(HttpRequest.get(url).build(), null).getResponseCode() ); url = clientConfig.resolveNamespacedURLV3(Id.Namespace.DEFAULT, "datasets/mydataset/metadata/properties?scope=SySTeM"); Assert.assertEquals( HttpResponseStatus.OK.getCode(), restClient.execute(HttpRequest.get(url).build(), null).getResponseCode() ); url = clientConfig.resolveNamespacedURLV3(Id.Namespace.DEFAULT, "apps/WordCountApp/flows/WordCountFlow/metadata/tags?scope=USER"); Assert.assertEquals( HttpResponseStatus.OK.getCode(), restClient.execute(HttpRequest.get(url).build(), null).getResponseCode() ); url = clientConfig.resolveNamespacedURLV3(Id.Namespace.DEFAULT, "streams/text/metadata?scope=user"); Assert.assertEquals( HttpResponseStatus.OK.getCode(), restClient.execute(HttpRequest.get(url).build(), null).getResponseCode() ); url = clientConfig.resolveNamespacedURLV3(Id.Namespace.DEFAULT, "streams/text/metadata?scope=blah"); Assert.assertEquals( HttpResponseStatus.BAD_REQUEST.getCode(), restClient.execute(HttpRequest.get(url).build(), null, HttpResponseStatus.BAD_REQUEST.getCode()).getResponseCode() ); appClient.delete(app); // deleting the app does not delete the dataset and stream, delete them explicitly to clear their system metadata datasetClient.delete(Id.DatasetInstance.from(Id.Namespace.DEFAULT, "mydataset")); streamClient.delete(Id.Stream.from(Id.Namespace.DEFAULT, "text")); } @Test public void testSearchTargetType() throws Exception { NamespaceId namespace = Ids.namespace("testSearchTargetType"); namespaceClient.create(new NamespaceMeta.Builder().setName(namespace.toId()).build()); appClient.deploy(namespace.toId(), createAppJarFile(AllProgramsApp.class)); // Add metadata to app Set<String> tags = ImmutableSet.of("utag1", "utag2"); ApplicationId appId = Ids.namespace(namespace.getNamespace()).app(AllProgramsApp.NAME); addTags(appId.toId(), tags); // Add metadata to stream tags = ImmutableSet.of("utag11"); StreamId streamId = Ids.namespace(namespace.getNamespace()).stream(AllProgramsApp.STREAM_NAME); addTags(streamId.toId(), tags); // Add metadata to dataset tags = ImmutableSet.of("utag21"); DatasetId datasetId = Ids.namespace(namespace.getNamespace()).dataset(AllProgramsApp.DATASET_NAME); addTags(datasetId.toId(), tags); // Search for single target type Assert.assertEquals(ImmutableSet.of(new MetadataSearchResultRecord(appId.toId())), searchMetadata(namespace.toId(), "utag*", MetadataSearchTargetType.APP)); Assert.assertEquals(ImmutableSet.of(new MetadataSearchResultRecord(datasetId.toId())), searchMetadata(namespace.toId(), "utag*", MetadataSearchTargetType.DATASET)); // Search for multiple target types Assert.assertEquals(ImmutableSet.of( new MetadataSearchResultRecord(datasetId.toId()), new MetadataSearchResultRecord(streamId.toId()) ), searchMetadata(namespace.toId(), "utag*", ImmutableSet.of( MetadataSearchTargetType.DATASET, MetadataSearchTargetType.STREAM ) )); Assert.assertEquals(ImmutableSet.of( new MetadataSearchResultRecord(datasetId.toId()), new MetadataSearchResultRecord(appId.toId()) ), searchMetadata(namespace.toId(), "utag*", ImmutableSet.of( MetadataSearchTargetType.APP, MetadataSearchTargetType.DATASET ) )); // Search for all target types Assert.assertEquals(ImmutableSet.of( new MetadataSearchResultRecord(datasetId.toId()), new MetadataSearchResultRecord(appId.toId()), new MetadataSearchResultRecord(streamId.toId()) ), searchMetadata(namespace.toId(), "utag*", MetadataSearchTargetType.ALL)); Assert.assertEquals(ImmutableSet.of( new MetadataSearchResultRecord(datasetId.toId()), new MetadataSearchResultRecord(appId.toId()), new MetadataSearchResultRecord(streamId.toId()) ), searchMetadata(namespace.toId(), "utag*", ImmutableSet.of( MetadataSearchTargetType.DATASET, MetadataSearchTargetType.ALL ) )); } @Test public void testSearchMetadata() throws Exception { appClient.deploy(Id.Namespace.DEFAULT, createAppJarFile(AllProgramsApp.class)); Map<Id.NamespacedId, Metadata> expectedUserMetadata = new HashMap<>(); // Add metadata to app Map<String, String> props = ImmutableMap.of("key1", "value1"); Set<String> tags = ImmutableSet.of("tag1", "tag2"); Id.Application appId = Id.Application.from(Id.Namespace.DEFAULT, AllProgramsApp.NAME); addProperties(appId, props); addTags(appId, tags); expectedUserMetadata.put(appId, new Metadata(props, tags)); // Add metadata to stream props = ImmutableMap.of("key10", "value10", "key11", "value11"); tags = ImmutableSet.of("tag11"); Id.Stream streamId = Id.Stream.from(Id.Namespace.DEFAULT, AllProgramsApp.STREAM_NAME); addProperties(streamId, props); addTags(streamId, tags); expectedUserMetadata.put(streamId, new Metadata(props, tags)); Set<MetadataSearchResultRecord> results = super.searchMetadata(Id.Namespace.DEFAULT, "value*", ImmutableSet.<MetadataSearchTargetType>of()); // Verify results Assert.assertEquals(expectedUserMetadata.keySet(), getEntities(results)); for (MetadataSearchResultRecord result : results) { // User metadata has to match exactly since we know what we have set Assert.assertEquals(expectedUserMetadata.get(result.getEntityId()), result.getMetadata().get(MetadataScope.USER)); // Make sure system metadata is returned, we cannot check for exact match since we haven't set it Metadata systemMetadata = result.getMetadata().get(MetadataScope.SYSTEM); Assert.assertNotNull(systemMetadata); Assert.assertFalse(systemMetadata.getProperties().isEmpty()); Assert.assertFalse(systemMetadata.getTags().isEmpty()); } } @Test public void testSearchMetadataDelete() throws Exception { Id.Namespace namespace = Id.Namespace.from("ns1"); namespaceClient.create(new NamespaceMeta.Builder().setName(namespace).build()); // Deploy app appClient.deploy(namespace, createAppJarFile(WordCountApp.class, WordCountApp.class.getSimpleName(), "1.0")); Set<String> tags = ImmutableSet.of("tag1", "tag2"); Id.Artifact artifact = Id.Artifact.from(namespace, "WordCountApp", "1.0"); Id.Application app = Id.Application.from(namespace, "WordCountApp"); Id.Flow flow = Id.Flow.from(app, "WordCountFlow"); Id.Service service = Id.Service.from(app, "WordFrequencyService"); Id.Stream stream = Id.Stream.from(namespace, "text"); Id.DatasetInstance datasetInstance = Id.DatasetInstance.from(namespace, "mydataset"); Id.Stream.View view = Id.Stream.View.from(namespace, stream.getId(), "view"); streamViewClient.createOrUpdate(view, new ViewSpecification(new FormatSpecification("csv", null, null))); // Add metadata addTags(app, tags); addTags(flow, tags); addTags(stream, tags); addTags(datasetInstance, tags); addTags(view, tags); // Assert metadata Assert.assertEquals(ImmutableSet.of(new MetadataSearchResultRecord(stream), new MetadataSearchResultRecord(view)), searchMetadata(namespace, "text")); Assert.assertEquals(ImmutableSet.of(new MetadataSearchResultRecord(datasetInstance)), searchMetadata(namespace, "mydataset")); Assert.assertEquals(ImmutableSet.of( new MetadataSearchResultRecord(app), new MetadataSearchResultRecord(flow), new MetadataSearchResultRecord(artifact), new MetadataSearchResultRecord(service) ), searchMetadata(namespace, "word*")); Assert.assertEquals(ImmutableSet.of( new MetadataSearchResultRecord(app), new MetadataSearchResultRecord(flow), new MetadataSearchResultRecord(stream), new MetadataSearchResultRecord(datasetInstance), new MetadataSearchResultRecord(view) ), searchMetadata(namespace, "tag1")); // Delete entities appClient.delete(app); streamViewClient.delete(view); streamClient.delete(stream); datasetClient.delete(datasetInstance); artifactClient.delete(artifact); // Assert no metadata Assert.assertEquals(ImmutableSet.of(), searchMetadata(namespace, "text")); Assert.assertEquals(ImmutableSet.of(), searchMetadata(namespace, "mydataset")); Assert.assertEquals(ImmutableSet.of(), searchMetadata(namespace, "word*")); Assert.assertEquals(ImmutableSet.of(), searchMetadata(namespace, "tag1")); } @Test public void testSearchMetadataDeleteNamespace() throws Exception { Id.Namespace namespace = Id.Namespace.from("ns2"); namespaceClient.create(new NamespaceMeta.Builder().setName(namespace).build()); // Deploy app appClient.deploy(namespace, createAppJarFile(WordCountApp.class, WordCountApp.class.getSimpleName(), "1.0")); Set<String> tags = ImmutableSet.of("tag1", "tag2"); Id.Artifact artifact = Id.Artifact.from(namespace, "WordCountApp", "1.0"); Id.Application app = Id.Application.from(namespace, "WordCountApp"); Id.Flow flow = Id.Flow.from(app, "WordCountFlow"); Id.Service service = Id.Service.from(app, "WordFrequencyService"); Id.Stream stream = Id.Stream.from(namespace, "text"); Id.DatasetInstance datasetInstance = Id.DatasetInstance.from(namespace, "mydataset"); Id.Stream.View view = Id.Stream.View.from(namespace, stream.getId(), "view"); streamViewClient.createOrUpdate(view, new ViewSpecification(new FormatSpecification("csv", null, null))); // Add metadata addTags(app, tags); addTags(flow, tags); addTags(stream, tags); addTags(datasetInstance, tags); addTags(view, tags); Assert.assertEquals(ImmutableSet.of(new MetadataSearchResultRecord(stream), new MetadataSearchResultRecord(view)), searchMetadata(namespace, "text")); Assert.assertEquals(ImmutableSet.of(new MetadataSearchResultRecord(datasetInstance)), searchMetadata(namespace, "mydataset")); Assert.assertEquals(ImmutableSet.of( new MetadataSearchResultRecord(app), new MetadataSearchResultRecord(flow), new MetadataSearchResultRecord(artifact), new MetadataSearchResultRecord(service) ), searchMetadata(namespace, "word*")); Assert.assertEquals(ImmutableSet.of( new MetadataSearchResultRecord(app), new MetadataSearchResultRecord(flow), new MetadataSearchResultRecord(stream), new MetadataSearchResultRecord(datasetInstance), new MetadataSearchResultRecord(view) ), searchMetadata(namespace, "tag1")); // Delete namespace namespaceClient.delete(namespace); // Assert no metadata Assert.assertEquals(ImmutableSet.of(), searchMetadata(namespace, "text")); Assert.assertEquals(ImmutableSet.of(), searchMetadata(namespace, "mydataset")); Assert.assertEquals(ImmutableSet.of(), searchMetadata(namespace, "word*")); Assert.assertEquals(ImmutableSet.of(), searchMetadata(namespace, "tag1")); } private Set<Id.NamespacedId> getEntities(Set<MetadataSearchResultRecord> results) { return Sets.newHashSet( Iterables.transform(results, new Function<MetadataSearchResultRecord, Id.NamespacedId>() { @Override public Id.NamespacedId apply(MetadataSearchResultRecord input) { return input.getEntityId(); } }) ); } private void assertProgramSystemMetadata(Id.Program programId, String mode) throws Exception { Assert.assertTrue(getProperties(programId, MetadataScope.SYSTEM).isEmpty()); Set<String> expected = ImmutableSet.of(programId.getId(), programId.getType().getPrettyName(), mode); if (ProgramType.WORKFLOW == programId.getType()) { expected = ImmutableSet.of(programId.getId(), programId.getType().getPrettyName(), mode, AllProgramsApp.NoOpAction.class.getSimpleName(), AllProgramsApp.NoOpMR.NAME); } Assert.assertEquals(expected, getTags(programId, MetadataScope.SYSTEM)); } private void assertArtifactSearch() throws Exception { // add a plugin artifact. Manifest manifest = new Manifest(); manifest.getMainAttributes().put(ManifestFields.EXPORT_PACKAGE, AllProgramsApp.AppPlugin.class.getPackage().getName()); Id.Artifact pluginArtifact = Id.Artifact.from(Id.Namespace.DEFAULT, "plugins", "1.0.0"); addPluginArtifact(pluginArtifact, AllProgramsApp.AppPlugin.class, manifest, null); // search using artifact name Set<MetadataSearchResultRecord> expected = ImmutableSet.of(new MetadataSearchResultRecord(pluginArtifact)); Set<MetadataSearchResultRecord> results = searchMetadata(Id.Namespace.DEFAULT, "plugins"); Assert.assertEquals(expected, results); // search the artifact given a plugin results = searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.PLUGIN_TYPE); Assert.assertEquals(expected, results); results = searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.PLUGIN_NAME + ":" + AllProgramsApp.PLUGIN_TYPE); Assert.assertEquals(expected, results); results = searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.PLUGIN_NAME, MetadataSearchTargetType.ARTIFACT); Assert.assertEquals(expected, results); // add a user tag to the application with the same name as the plugin addTags(application, ImmutableSet.of(AllProgramsApp.PLUGIN_NAME)); // search for all entities with plugin name. Should return both artifact and application results = searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.PLUGIN_NAME); Assert.assertEquals( ImmutableSet.of(new MetadataSearchResultRecord(application), new MetadataSearchResultRecord(pluginArtifact)), results); // search for all entities for a plugin with the plugin name. Should return only the artifact, since for the // application, its just a tag, not a plugin results = searchMetadata(Id.Namespace.DEFAULT, "plugin:" + AllProgramsApp.PLUGIN_NAME + ":*"); Assert.assertEquals(expected, results); } private void assertAppSearch(Id.Application app, Id.Artifact artifact) throws Exception { // using app name ImmutableSet<MetadataSearchResultRecord> expected = ImmutableSet.of(new MetadataSearchResultRecord(app)); Assert.assertEquals(expected, searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.NAME)); // using artifact name: both app and artifact should match Assert.assertEquals( ImmutableSet.of(new MetadataSearchResultRecord(app), new MetadataSearchResultRecord(artifact)), searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.class.getSimpleName())); // using program names Assert.assertEquals(expected, searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.NoOpFlow.NAME, MetadataSearchTargetType.APP)); Assert.assertEquals(expected, searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.NoOpMR.NAME, MetadataSearchTargetType.APP)); Assert.assertEquals(expected, searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.NoOpService.NAME, MetadataSearchTargetType.APP)); Assert.assertEquals(expected, searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.NoOpSpark.NAME, MetadataSearchTargetType.APP)); Assert.assertEquals(expected, searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.NoOpWorker.NAME, MetadataSearchTargetType.APP)); Assert.assertEquals(expected, searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.NoOpWorkflow.NAME, MetadataSearchTargetType.APP)); // using program types Assert.assertEquals( expected, searchMetadata(Id.Namespace.DEFAULT, ProgramType.FLOW.getPrettyName() + MetadataDataset.KEYVALUE_SEPARATOR + "*", MetadataSearchTargetType.APP)); Assert.assertEquals( expected, searchMetadata(Id.Namespace.DEFAULT, ProgramType.MAPREDUCE.getPrettyName() + MetadataDataset.KEYVALUE_SEPARATOR + "*", MetadataSearchTargetType.APP)); Assert.assertEquals( ImmutableSet.builder().addAll(expected).add(new MetadataSearchResultRecord(application)).build(), searchMetadata(Id.Namespace.DEFAULT, ProgramType.SERVICE.getPrettyName() + MetadataDataset.KEYVALUE_SEPARATOR + "*", MetadataSearchTargetType.APP)); Assert.assertEquals( expected, searchMetadata(Id.Namespace.DEFAULT, ProgramType.SPARK.getPrettyName() + MetadataDataset.KEYVALUE_SEPARATOR + "*", MetadataSearchTargetType.APP)); Assert.assertEquals( expected, searchMetadata(Id.Namespace.DEFAULT, ProgramType.WORKER.getPrettyName() + MetadataDataset.KEYVALUE_SEPARATOR + "*", MetadataSearchTargetType.APP)); Assert.assertEquals( expected, searchMetadata(Id.Namespace.DEFAULT, ProgramType.WORKFLOW.getPrettyName() + MetadataDataset.KEYVALUE_SEPARATOR + "*", MetadataSearchTargetType.APP)); // using schedule Assert.assertEquals(expected, searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.SCHEDULE_NAME)); Assert.assertEquals(expected, searchMetadata(Id.Namespace.DEFAULT, "EveryMinute")); } private void assertProgramSearch(Id.Application app) throws Exception { Assert.assertEquals( ImmutableSet.of( new MetadataSearchResultRecord(Id.Program.from(app, ProgramType.MAPREDUCE, AllProgramsApp.NoOpMR.NAME)), new MetadataSearchResultRecord(Id.Program.from(app, ProgramType.MAPREDUCE, AllProgramsApp.NoOpMR2.NAME)), new MetadataSearchResultRecord(Id.Program.from(app, ProgramType.WORKFLOW, AllProgramsApp.NoOpWorkflow.NAME)), new MetadataSearchResultRecord(Id.Program.from(app, ProgramType.SPARK, AllProgramsApp.NoOpSpark.NAME)), new MetadataSearchResultRecord(Id.DatasetInstance.from(Id.Namespace.DEFAULT, AllProgramsApp.DATASET_NAME)), new MetadataSearchResultRecord(Id.DatasetInstance.from(Id.Namespace.DEFAULT, AllProgramsApp.DATASET_NAME2)), new MetadataSearchResultRecord(Id.DatasetInstance.from(Id.Namespace.DEFAULT, AllProgramsApp.DATASET_NAME3)), new MetadataSearchResultRecord( Id.DatasetInstance.from(Id.Namespace.DEFAULT, AllProgramsApp.DS_WITH_SCHEMA_NAME)), new MetadataSearchResultRecord(myds) ), searchMetadata(Id.Namespace.DEFAULT, "Batch")); Assert.assertEquals( ImmutableSet.of( new MetadataSearchResultRecord(Id.Program.from(app, ProgramType.FLOW, AllProgramsApp.NoOpFlow.NAME)), new MetadataSearchResultRecord(Id.Program.from(app, ProgramType.SERVICE, AllProgramsApp.NoOpService.NAME)), new MetadataSearchResultRecord(Id.Program.from(app, ProgramType.WORKER, AllProgramsApp.NoOpWorker.NAME)), new MetadataSearchResultRecord( Id.Program.from(Id.Application.from(Id.Namespace.DEFAULT, AppWithDataset.class.getSimpleName()), ProgramType.SERVICE, "PingService")) ), searchMetadata(Id.Namespace.DEFAULT, "Realtime")); // Using program names Assert.assertEquals( ImmutableSet.of( new MetadataSearchResultRecord(Id.Program.from(app, ProgramType.FLOW, AllProgramsApp.NoOpFlow.NAME))), searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.NoOpFlow.NAME, MetadataSearchTargetType.PROGRAM)); Assert.assertEquals( ImmutableSet.of( new MetadataSearchResultRecord(Id.Program.from(app, ProgramType.MAPREDUCE, AllProgramsApp.NoOpMR.NAME)), new MetadataSearchResultRecord(Id.Program.from(app, ProgramType.WORKFLOW, AllProgramsApp.NoOpWorkflow.NAME))), searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.NoOpMR.NAME, MetadataSearchTargetType.PROGRAM)); Assert.assertEquals( ImmutableSet.of( new MetadataSearchResultRecord(Id.Program.from(app, ProgramType.SERVICE, AllProgramsApp.NoOpService.NAME))), searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.NoOpService.NAME, MetadataSearchTargetType.PROGRAM)); Assert.assertEquals( ImmutableSet.of( new MetadataSearchResultRecord(Id.Program.from(app, ProgramType.SPARK, AllProgramsApp.NoOpSpark.NAME))), searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.NoOpSpark.NAME, MetadataSearchTargetType.PROGRAM)); Assert.assertEquals( ImmutableSet.of( new MetadataSearchResultRecord(Id.Program.from(app, ProgramType.WORKER, AllProgramsApp.NoOpWorker.NAME))), searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.NoOpWorker.NAME, MetadataSearchTargetType.PROGRAM)); Assert.assertEquals( ImmutableSet.of( new MetadataSearchResultRecord(Id.Program.from(app, ProgramType.WORKFLOW, AllProgramsApp.NoOpWorkflow.NAME))), searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.NoOpWorkflow.NAME, MetadataSearchTargetType.PROGRAM)); // using program types Assert.assertEquals( ImmutableSet.of( new MetadataSearchResultRecord(Id.Program.from(app, ProgramType.FLOW, AllProgramsApp.NoOpFlow.NAME))), searchMetadata(Id.Namespace.DEFAULT, ProgramType.FLOW.getPrettyName(), MetadataSearchTargetType.PROGRAM)); Assert.assertEquals( ImmutableSet.of( new MetadataSearchResultRecord(Id.Program.from(app, ProgramType.MAPREDUCE, AllProgramsApp.NoOpMR.NAME)), new MetadataSearchResultRecord(Id.Program.from(app, ProgramType.MAPREDUCE, AllProgramsApp.NoOpMR2.NAME))), searchMetadata(Id.Namespace.DEFAULT, ProgramType.MAPREDUCE.getPrettyName(), MetadataSearchTargetType.PROGRAM)); Assert.assertEquals( ImmutableSet.of( new MetadataSearchResultRecord(Id.Program.from(app, ProgramType.SERVICE, AllProgramsApp.NoOpService.NAME)), new MetadataSearchResultRecord(pingService)), searchMetadata(Id.Namespace.DEFAULT, ProgramType.SERVICE.getPrettyName(), MetadataSearchTargetType.PROGRAM)); Assert.assertEquals( ImmutableSet.of( new MetadataSearchResultRecord(Id.Program.from(app, ProgramType.SPARK, AllProgramsApp.NoOpSpark.NAME))), searchMetadata(Id.Namespace.DEFAULT, ProgramType.SPARK.getPrettyName(), MetadataSearchTargetType.PROGRAM)); Assert.assertEquals( ImmutableSet.of( new MetadataSearchResultRecord(Id.Program.from(app, ProgramType.WORKER, AllProgramsApp.NoOpWorker.NAME))), searchMetadata(Id.Namespace.DEFAULT, ProgramType.WORKER.getPrettyName(), MetadataSearchTargetType.PROGRAM)); Assert.assertEquals( ImmutableSet.of( new MetadataSearchResultRecord(Id.Program.from(app, ProgramType.WORKFLOW, AllProgramsApp.NoOpWorkflow.NAME))), searchMetadata(Id.Namespace.DEFAULT, ProgramType.WORKFLOW.getPrettyName(), MetadataSearchTargetType.PROGRAM)); } private void assertDataEntitySearch() throws Exception { Id.DatasetInstance datasetInstance = Id.DatasetInstance.from(Id.Namespace.DEFAULT, AllProgramsApp.DATASET_NAME); Id.DatasetInstance datasetInstance2 = Id.DatasetInstance.from(Id.Namespace.DEFAULT, AllProgramsApp.DATASET_NAME2); Id.DatasetInstance datasetInstance3 = Id.DatasetInstance.from(Id.Namespace.DEFAULT, AllProgramsApp.DATASET_NAME3); Id.DatasetInstance dsWithSchema = Id.DatasetInstance.from(Id.Namespace.DEFAULT, AllProgramsApp.DS_WITH_SCHEMA_NAME); Id.Stream streamId = Id.Stream.from(Id.Namespace.DEFAULT, AllProgramsApp.STREAM_NAME); Id.Stream.View view = Id.Stream.View.from(streamId, "view"); Set<MetadataSearchResultRecord> expected = ImmutableSet.of( new MetadataSearchResultRecord(streamId), new MetadataSearchResultRecord(mystream) ); Set<MetadataSearchResultRecord> expectedWithView = ImmutableSet.<MetadataSearchResultRecord>builder() .addAll(expected) .add(new MetadataSearchResultRecord(myview)).build(); // schema search with fieldname Set<MetadataSearchResultRecord> metadataSearchResultRecords = searchMetadata(Id.Namespace.DEFAULT, "body"); Assert.assertEquals(expectedWithView, metadataSearchResultRecords); // schema search with fieldname and fieldtype metadataSearchResultRecords = searchMetadata(Id.Namespace.DEFAULT, "body:" + Schema.Type.STRING.toString()); Assert.assertEquals(expected, metadataSearchResultRecords); // schema search for partial fieldname metadataSearchResultRecords = searchMetadata(Id.Namespace.DEFAULT, "bo*"); Assert.assertEquals(expectedWithView, metadataSearchResultRecords); // schema search with fieldname and all/partial fieldtype metadataSearchResultRecords = searchMetadata(Id.Namespace.DEFAULT, "body:STR*"); Assert.assertEquals(expected, metadataSearchResultRecords); // schema search for a field with the given fieldname:fieldtype metadataSearchResultRecords = searchMetadata(Id.Namespace.DEFAULT, "body:STRING+field1:STRING"); Assert.assertEquals(ImmutableSet.<MetadataSearchResultRecord>builder() .addAll(expected) .add(new MetadataSearchResultRecord(dsWithSchema)) .build(), metadataSearchResultRecords); // create a view Schema viewSchema = Schema.recordOf("record", Schema.Field.of("viewBody", Schema.nullableOf(Schema.of(Schema.Type.BYTES)))); streamViewClient.createOrUpdate(view, new ViewSpecification(new FormatSpecification("format", viewSchema))); // search all entities that have a defined schema // add a user property with "schema" as key Map<String, String> datasetProperties = ImmutableMap.of("schema", "schemaValue"); addProperties(datasetInstance, datasetProperties); metadataSearchResultRecords = searchMetadata(Id.Namespace.DEFAULT, "schema:*"); Assert.assertEquals(ImmutableSet.<MetadataSearchResultRecord>builder() .addAll(expectedWithView) .add(new MetadataSearchResultRecord(datasetInstance)) .add(new MetadataSearchResultRecord(dsWithSchema)) .add(new MetadataSearchResultRecord(view)) .build(), metadataSearchResultRecords); // search dataset ImmutableSet<MetadataSearchResultRecord> expectedKvTables = ImmutableSet.of( new MetadataSearchResultRecord(datasetInstance), new MetadataSearchResultRecord(datasetInstance2), new MetadataSearchResultRecord(datasetInstance3), new MetadataSearchResultRecord(myds) ); ImmutableSet<MetadataSearchResultRecord> expectedAllDatasets = ImmutableSet.<MetadataSearchResultRecord>builder() .addAll(expectedKvTables) .add(new MetadataSearchResultRecord(dsWithSchema)) .build(); metadataSearchResultRecords = searchMetadata(Id.Namespace.DEFAULT, "explore"); Assert.assertEquals(expectedAllDatasets, metadataSearchResultRecords); metadataSearchResultRecords = searchMetadata(Id.Namespace.DEFAULT, KeyValueTable.class.getName()); Assert.assertEquals(expectedKvTables, metadataSearchResultRecords); metadataSearchResultRecords = searchMetadata(Id.Namespace.DEFAULT, "type:*"); Assert.assertEquals(expectedAllDatasets, metadataSearchResultRecords); // search using ttl metadataSearchResultRecords = searchMetadata(Id.Namespace.DEFAULT, "ttl:*"); Assert.assertEquals(expected, metadataSearchResultRecords); // search using names metadataSearchResultRecords = searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.STREAM_NAME); Assert.assertEquals( ImmutableSet.of(new MetadataSearchResultRecord(streamId), new MetadataSearchResultRecord(view)), metadataSearchResultRecords); metadataSearchResultRecords = searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.STREAM_NAME, MetadataSearchTargetType.STREAM); Assert.assertEquals(ImmutableSet.of(new MetadataSearchResultRecord(streamId)), metadataSearchResultRecords); metadataSearchResultRecords = searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.STREAM_NAME, MetadataSearchTargetType.VIEW); Assert.assertEquals(ImmutableSet.of(new MetadataSearchResultRecord(view)), metadataSearchResultRecords); metadataSearchResultRecords = searchMetadata(Id.Namespace.DEFAULT, "view", MetadataSearchTargetType.VIEW); Assert.assertEquals(ImmutableSet.of(new MetadataSearchResultRecord(view)), metadataSearchResultRecords); metadataSearchResultRecords = searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.DATASET_NAME); Assert.assertEquals(ImmutableSet.of(new MetadataSearchResultRecord(datasetInstance)), metadataSearchResultRecords); metadataSearchResultRecords = searchMetadata(Id.Namespace.DEFAULT, AllProgramsApp.DS_WITH_SCHEMA_NAME); Assert.assertEquals(ImmutableSet.of(new MetadataSearchResultRecord(dsWithSchema)), metadataSearchResultRecords); } private void removeAllMetadata() throws Exception { removeMetadata(application); removeMetadata(pingService); removeMetadata(myds); removeMetadata(mystream); removeMetadata(myview); removeMetadata(artifactId); } private void assertCleanState(@Nullable MetadataScope scope) throws Exception { assertEmptyMetadata(getMetadata(application, scope), scope); assertEmptyMetadata(getMetadata(pingService, scope), scope); assertEmptyMetadata(getMetadata(myds, scope), scope); assertEmptyMetadata(getMetadata(mystream, scope), scope); assertEmptyMetadata(getMetadata(myview, scope), scope); assertEmptyMetadata(getMetadata(artifactId, scope), scope); } private void assertEmptyMetadata(Set<MetadataRecord> entityMetadata, @Nullable MetadataScope scope) { // should have two metadata records - one for each scope, both should have empty properties and tags int expectedRecords = (scope == null) ? 2 : 1; Assert.assertEquals(expectedRecords, entityMetadata.size()); for (MetadataRecord metadataRecord : entityMetadata) { Assert.assertTrue(metadataRecord.getProperties().isEmpty()); Assert.assertTrue(metadataRecord.getTags().isEmpty()); } } /** * Returns the artifact id of the deployed application. Need this because we don't know the exact version. */ private Id.Artifact getArtifactId() throws Exception { Iterable<ArtifactSummary> filtered = Iterables.filter(artifactClient.list(NamespaceId.DEFAULT.toId()), new Predicate<ArtifactSummary>() { @Override public boolean apply(ArtifactSummary artifactSummary) { return AllProgramsApp.class.getSimpleName().equals(artifactSummary.getName()); } }); ArtifactSummary artifact = Iterables.getOnlyElement(filtered); return Id.Artifact.from(Id.Namespace.DEFAULT, artifact.getName(), artifact.getVersion()); } private Set<MetadataSearchResultRecord> searchMetadata(Id.Namespace namespaceId, String query, MetadataSearchTargetType target) throws Exception { return searchMetadata(namespaceId, query, ImmutableSet.of(target)); } private Set<MetadataSearchResultRecord> searchMetadata(Id.Namespace namespaceId, String query) throws Exception { return searchMetadata(namespaceId, query, ImmutableSet.<MetadataSearchTargetType>of()); } /** * strips metadata from search results */ protected Set<MetadataSearchResultRecord> searchMetadata(Id.Namespace namespaceId, String query, Set<MetadataSearchTargetType> targets) throws Exception { Set<MetadataSearchResultRecord> results = super.searchMetadata(namespaceId, query, targets); Set<MetadataSearchResultRecord> transformed = new HashSet<>(); for (MetadataSearchResultRecord result : results) { transformed.add(new MetadataSearchResultRecord(result.getEntityId())); } return transformed; } }