diff --git a/gcloud-java-logging/src/main/java/com/google/cloud/logging/Logging.java b/gcloud-java-logging/src/main/java/com/google/cloud/logging/Logging.java index 81c2b7887921..64d42f280ad9 100644 --- a/gcloud-java-logging/src/main/java/com/google/cloud/logging/Logging.java +++ b/gcloud-java-logging/src/main/java/com/google/cloud/logging/Logging.java @@ -71,7 +71,7 @@ public static ListOption pageToken(String pageToken) { /** * Sends a request for creating a sink. This method returns a {@code Future} object to consume the - * result. {@link Future#get()} returns the created sink or {@code null} if not found. + * result. {@link Future#get()} returns the created sink. */ Future createAsync(SinkInfo sink); @@ -135,4 +135,79 @@ public static ListOption pageToken(String pageToken) { * was not found. */ Future deleteSinkAsync(String sink); + + /** + * Creates a new metric. + * + * @return the created metric + * @throws LoggingException upon failure + */ + Metric create(MetricInfo metric); + + /** + * Sends a request for creating a metric. This method returns a {@code Future} object to consume + * the result. {@link Future#get()} returns the created metric. + */ + Future createAsync(MetricInfo metric); + + /** + * Updates a metric or creates one if it does not exist. + * + * @return the created metric + * @throws LoggingException upon failure + */ + Metric update(MetricInfo metric); + + /** + * Sends a request for updating a metric (or creating it, if it does not exist). This method + * returns a {@code Future} object to consume the result. {@link Future#get()} returns the + * updated/created metric or {@code null} if not found. + */ + Future updateAsync(MetricInfo metric); + + /** + * Returns the requested metric or {@code null} if not found. + * + * @throws LoggingException upon failure + */ + Metric getMetric(String metric); + + /** + * Sends a request for getting a metric. This method returns a {@code Future} object to consume + * the result. {@link Future#get()} returns the requested metric or {@code null} if not found. + * + * @throws LoggingException upon failure + */ + Future getMetricAsync(String metric); + + /** + * Lists the metrics. This method returns a {@link Page} object that can be used to consume + * paginated results. Use {@link ListOption} to specify the page size or the page token from which + * to start listing metrics. + * + * @throws LoggingException upon failure + */ + Page listMetrics(ListOption... options); + + /** + * Sends a request for listing metrics. This method returns a {@code Future} object to consume + * the result. {@link Future#get()} returns an {@link AsyncPage} object that can be used to + * asynchronously handle paginated results. Use {@link ListOption} to specify the page size or the + * page token from which to start listing metrics. + */ + Future> listMetricsAsync(ListOption... options); + + /** + * Deletes the requested metric. + * + * @return {@code true} if the metric was deleted, {@code false} if it was not found + */ + boolean deleteMetric(String metric); + + /** + * Sends a request for deleting a metric. This method returns a {@code Future} object to consume + * the result. {@link Future#get()} returns {@code true} if the metric was deleted, {@code false} + * if it was not found. + */ + Future deleteMetricAsync(String metric); } diff --git a/gcloud-java-logging/src/main/java/com/google/cloud/logging/LoggingImpl.java b/gcloud-java-logging/src/main/java/com/google/cloud/logging/LoggingImpl.java index 5d6c25fe3022..d7839d4afd13 100644 --- a/gcloud-java-logging/src/main/java/com/google/cloud/logging/LoggingImpl.java +++ b/gcloud-java-logging/src/main/java/com/google/cloud/logging/LoggingImpl.java @@ -16,9 +16,9 @@ package com.google.cloud.logging; -import static com.google.api.client.util.Preconditions.checkArgument; import static com.google.cloud.logging.Logging.ListOption.OptionType.PAGE_SIZE; import static com.google.cloud.logging.Logging.ListOption.OptionType.PAGE_TOKEN; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.util.concurrent.Futures.lazyTransform; import com.google.cloud.AsyncPage; @@ -28,17 +28,24 @@ import com.google.cloud.PageImpl; import com.google.cloud.logging.spi.LoggingRpc; import com.google.cloud.logging.spi.v2.ConfigServiceV2Api; +import com.google.cloud.logging.spi.v2.MetricsServiceV2Api; import com.google.common.base.Function; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.util.concurrent.Uninterruptibles; +import com.google.logging.v2.CreateLogMetricRequest; import com.google.logging.v2.CreateSinkRequest; +import com.google.logging.v2.DeleteLogMetricRequest; import com.google.logging.v2.DeleteSinkRequest; +import com.google.logging.v2.GetLogMetricRequest; import com.google.logging.v2.GetSinkRequest; +import com.google.logging.v2.ListLogMetricsRequest; +import com.google.logging.v2.ListLogMetricsResponse; import com.google.logging.v2.ListSinksRequest; import com.google.logging.v2.ListSinksResponse; +import com.google.logging.v2.UpdateLogMetricRequest; import com.google.logging.v2.UpdateSinkRequest; import com.google.protobuf.Empty; @@ -111,6 +118,21 @@ public Future> nextPage() { } } + private static class MetricPageFetcher extends BasePageFetcher { + + private static final long serialVersionUID = -316783549651771553L; + + MetricPageFetcher(LoggingOptions serviceOptions, String cursor, + Map requestOptions) { + super(serviceOptions, cursor, requestOptions); + } + + @Override + public Future> nextPage() { + return listMetricsAsync(serviceOptions(), requestOptions()); + } + } + @Override public Sink create(SinkInfo sink) { return get(createAsync(sink)); @@ -208,6 +230,103 @@ public Future deleteSinkAsync(String sink) { return lazyTransform(rpc.delete(request), EMPTY_TO_BOOLEAN_FUNCTION); } + @Override + public Metric create(MetricInfo metric) { + return get(createAsync(metric)); + } + + @Override + public Future createAsync(MetricInfo metric) { + CreateLogMetricRequest request = CreateLogMetricRequest.newBuilder() + .setProjectName(MetricsServiceV2Api.formatProjectName(options().projectId())) + .setMetric(metric.toPb()) + .build(); + return lazyTransform(rpc.create(request), Metric.fromPbFunction(this)); + } + + @Override + public Metric update(MetricInfo metric) { + return get(updateAsync(metric)); + } + + @Override + public Future updateAsync(MetricInfo metric) { + UpdateLogMetricRequest request = UpdateLogMetricRequest.newBuilder() + .setMetricName(MetricsServiceV2Api.formatMetricName(options().projectId(), metric.name())) + .setMetric(metric.toPb()) + .build(); + return lazyTransform(rpc.update(request), Metric.fromPbFunction(this)); + } + + @Override + public Metric getMetric(String metric) { + return get(getMetricAsync(metric)); + } + + @Override + public Future getMetricAsync(String metric) { + GetLogMetricRequest request = GetLogMetricRequest.newBuilder() + .setMetricName(MetricsServiceV2Api.formatMetricName(options().projectId(), metric)) + .build(); + return lazyTransform(rpc.get(request), Metric.fromPbFunction(this)); + } + + private static ListLogMetricsRequest listMetricsRequest(LoggingOptions serviceOptions, + Map options) { + ListLogMetricsRequest.Builder builder = ListLogMetricsRequest.newBuilder(); + builder.setProjectName(MetricsServiceV2Api.formatProjectName(serviceOptions.projectId())); + Integer pageSize = PAGE_SIZE.get(options); + String pageToken = PAGE_TOKEN.get(options); + if (pageSize != null) { + builder.setPageSize(pageSize); + } + if (pageToken != null) { + builder.setPageToken(pageToken); + } + return builder.build(); + } + + private static Future> listMetricsAsync(final LoggingOptions serviceOptions, + final Map options) { + final ListLogMetricsRequest request = listMetricsRequest(serviceOptions, options); + Future list = serviceOptions.rpc().list(request); + return lazyTransform(list, new Function>() { + @Override + public AsyncPage apply(ListLogMetricsResponse listMetricsResponse) { + List metrics = listMetricsResponse.getMetricsList() == null + ? ImmutableList.of() : Lists.transform(listMetricsResponse.getMetricsList(), + Metric.fromPbFunction(serviceOptions.service())); + String cursor = listMetricsResponse.getNextPageToken().equals("") ? null + : listMetricsResponse.getNextPageToken(); + return new AsyncPageImpl<>(new MetricPageFetcher(serviceOptions, cursor, options), cursor, + metrics); + } + }); + } + + @Override + public Page listMetrics(ListOption... options) { + return get(listMetricsAsync(options)); + } + + @Override + public Future> listMetricsAsync(ListOption... options) { + return listMetricsAsync(options(), optionMap(options)); + } + + @Override + public boolean deleteMetric(String metric) { + return get(deleteMetricAsync(metric)); + } + + @Override + public Future deleteMetricAsync(String metric) { + DeleteLogMetricRequest request = DeleteLogMetricRequest.newBuilder() + .setMetricName(MetricsServiceV2Api.formatMetricName(options().projectId(), metric)) + .build(); + return lazyTransform(rpc.delete(request), EMPTY_TO_BOOLEAN_FUNCTION); + } + @Override public void close() throws Exception { if (closed) { diff --git a/gcloud-java-logging/src/main/java/com/google/cloud/logging/Metric.java b/gcloud-java-logging/src/main/java/com/google/cloud/logging/Metric.java new file mode 100644 index 000000000000..d93e09ba80b1 --- /dev/null +++ b/gcloud-java-logging/src/main/java/com/google/cloud/logging/Metric.java @@ -0,0 +1,200 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.cloud.logging; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Function; +import com.google.logging.v2.LogMetric; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.util.Objects; +import java.util.concurrent.Future; + +/** + * Google Cloud Logging metrics describe logs-based metric. The value of the metric is the number of + * log entries that match a logs filter (see {@link #filter()}). + * + *

{@code Metric} adds a layer of service-related functionality over {@link MetricInfo}. Objects + * of this class are immutable. To get a {@code Metric} object with the most recent information use + * {@link #reload} or {@link #reloadAsync}. + * + * @see Logs-based Metrics + * + */ +public class Metric extends MetricInfo { + + private static final long serialVersionUID = -1549310461066853001L; + + private final LoggingOptions options; + private transient Logging logging; + + /** + * A builder for {@code Metric} objects. + */ + public static final class Builder extends MetricInfo.Builder { + + private final Logging logging; + private final BuilderImpl delegate; + + private Builder(Metric metric) { + logging = metric.logging; + delegate = new BuilderImpl(metric); + } + + @Override + public Builder name(String name) { + delegate.name(name); + return this; + } + + @Override + public Builder description(String description) { + delegate.description(description); + return this; + } + + @Override + public Builder filter(String filter) { + delegate.filter(filter); + return this; + } + + @Override + public Metric build() { + return new Metric(this.logging, this.delegate); + } + } + + Metric(Logging logging, BuilderImpl builder) { + super(builder); + this.logging = checkNotNull(logging); + options = logging.options(); + } + + @Override + public Builder toBuilder() { + return new Builder(this); + } + + @Override + public final int hashCode() { + return Objects.hash(options, super.hashCode()); + } + + @Override + public final boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || !obj.getClass().equals(Metric.class)) { + return false; + } + Metric other = (Metric) obj; + return baseEquals(other) && Objects.equals(options, other.options); + } + + /** + * Returns the metrics's {@code Logging} object used to issue requests. + */ + public Logging logging() { + return logging; + } + + /** + * Deletes this metric. + * + * @return {@code true} if the metric was deleted, {@code false} if it was not found + * @throws LoggingException upon failure + */ + public boolean delete() { + return logging.deleteMetric(name()); + } + + /** + * Sends a request for deleting this metric. This method returns a {@code Future} object to + * consume the result. {@link Future#get()} returns {@code true} if the metric was deleted, + * {@code false} if it was not found. + * + * @throws LoggingException upon failure + */ + public Future deleteAsync() { + return logging.deleteMetricAsync(name()); + } + + /** + * Fetches current metric's latest information. Returns {@code null} if the metric does not exist. + * + * @return a {@code Metric} object with latest information or {@code null} if not found + * @throws LoggingException upon failure + */ + public Metric reload() { + return logging.getMetric(name()); + } + + /** + * Sends a request to fetch current metric's latest information. This method returns a + * {@code Future} object to consume the result. {@link Future#get()} returns a {@code Metric} + * object with latest information or {@code null} if not found. + * + * @throws LoggingException upon failure + */ + public Future reloadAsync() { + return logging.getMetricAsync(name()); + } + + /** + * Updates current metric. If the metric does not exist, it is created. + * + * @return a {@code Metric} object with updated information + * @throws LoggingException upon failure + */ + public Metric update(MetricInfo metricInfo) { + return logging.update(metricInfo); + } + + /** + * Sends a request to update current metric. If the metric does not exist, it is created. This + * method returns a {@code Future} object to consume the result. {@link Future#get()} returns a + * {@code Metric} object with updated information. + * + * @throws LoggingException upon failure + */ + public Future updateAsync(MetricInfo metricInfo) { + return logging.updateAsync(metricInfo); + } + + private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException { + input.defaultReadObject(); + this.logging = options.service(); + } + + static Metric fromPb(Logging logging, LogMetric metricPb) { + MetricInfo metricInfo = MetricInfo.fromPb(metricPb); + return new Metric(logging, new BuilderImpl(metricInfo)); + } + + static Function fromPbFunction(final Logging logging) { + return new Function() { + @Override + public Metric apply(LogMetric metricPb) { + return metricPb != null ? fromPb(logging, metricPb) : null; + } + }; + } +} diff --git a/gcloud-java-logging/src/test/java/com/google/cloud/logging/LoggingImplTest.java b/gcloud-java-logging/src/test/java/com/google/cloud/logging/LoggingImplTest.java index c45ea8d4f906..87dd9b41ef2e 100644 --- a/gcloud-java-logging/src/test/java/com/google/cloud/logging/LoggingImplTest.java +++ b/gcloud-java-logging/src/test/java/com/google/cloud/logging/LoggingImplTest.java @@ -35,12 +35,19 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.util.concurrent.Futures; +import com.google.logging.v2.CreateLogMetricRequest; import com.google.logging.v2.CreateSinkRequest; +import com.google.logging.v2.DeleteLogMetricRequest; import com.google.logging.v2.DeleteSinkRequest; +import com.google.logging.v2.GetLogMetricRequest; import com.google.logging.v2.GetSinkRequest; +import com.google.logging.v2.ListLogMetricsRequest; +import com.google.logging.v2.ListLogMetricsResponse; import com.google.logging.v2.ListSinksRequest; import com.google.logging.v2.ListSinksResponse; +import com.google.logging.v2.LogMetric; import com.google.logging.v2.LogSink; +import com.google.logging.v2.UpdateLogMetricRequest; import com.google.logging.v2.UpdateSinkRequest; import com.google.protobuf.Empty; @@ -63,6 +70,13 @@ public class LoggingImplTest { private static final SinkInfo SINK_INFO = SinkInfo.of(SINK_NAME, Destination.BucketDestination.of("bucket")); private static final String SINK_NAME_PB = "projects/" + PROJECT + "/sinks/" + SINK_NAME; + private static final String METRIC_NAME = "metric"; + private static final String METRIC_NAME_PB = "projects/" + PROJECT + "/metrics/" + METRIC_NAME; + private static final String FILTER = "logName=projects/my-projectid/logs/syslog"; + private static final String DESCRIPTION = "description"; + private static final MetricInfo METRIC_INFO = MetricInfo.builder(METRIC_NAME, FILTER) + .description(DESCRIPTION) + .build(); private static final Function SINK_TO_PB_FUNCTION = new Function() { @Override @@ -70,6 +84,13 @@ public LogSink apply(SinkInfo sinkInfo) { return sinkInfo.toPb(PROJECT); } }; + private static final Function METRIC_TO_PB_FUNCTION = + new Function() { + @Override + public LogMetric apply(MetricInfo metricInfo) { + return metricInfo.toPb(); + } + }; private LoggingOptions options; private LoggingRpcFactory rpcFactoryMock; @@ -214,7 +235,7 @@ public void testDeleteSink() { EasyMock.expect(loggingRpcMock.delete(request)).andReturn(response); EasyMock.replay(rpcFactoryMock, loggingRpcMock); logging = options.service(); - assertTrue(logging.deleteSink("sink")); + assertTrue(logging.deleteSink(SINK_NAME)); } @Test @@ -224,7 +245,7 @@ public void testDeleteSink_Null() { EasyMock.expect(loggingRpcMock.delete(request)).andReturn(response); EasyMock.replay(rpcFactoryMock, loggingRpcMock); logging = options.service(); - assertFalse(logging.deleteSink("sink")); + assertFalse(logging.deleteSink(SINK_NAME)); } @Test @@ -234,7 +255,7 @@ public void testDeleteSinkAsync() throws ExecutionException, InterruptedExceptio EasyMock.expect(loggingRpcMock.delete(request)).andReturn(response); EasyMock.replay(rpcFactoryMock, loggingRpcMock); logging = options.service(); - assertTrue(logging.deleteSinkAsync("sink").get()); + assertTrue(logging.deleteSinkAsync(SINK_NAME).get()); } @Test @@ -244,7 +265,7 @@ public void testDeleteSinkAsync_Null() throws ExecutionException, InterruptedExc EasyMock.expect(loggingRpcMock.delete(request)).andReturn(response); EasyMock.replay(rpcFactoryMock, loggingRpcMock); logging = options.service(); - assertFalse(logging.deleteSinkAsync("sink").get()); + assertFalse(logging.deleteSinkAsync(SINK_NAME).get()); } @Test @@ -451,4 +472,367 @@ public void testListSinksWithOptionsAsync() throws ExecutionException, Interrupt assertEquals(cursor, page.nextPageCursor()); assertArrayEquals(sinkList.toArray(), Iterables.toArray(page.values(), Sink.class)); } + + @Test + public void testCreateMetric() { + LogMetric metricPb = METRIC_INFO.toPb(); + Future response = Futures.immediateFuture(metricPb); + CreateLogMetricRequest request = CreateLogMetricRequest.newBuilder() + .setProjectName(PROJECT_PB) + .setMetric(metricPb) + .build(); + EasyMock.expect(loggingRpcMock.create(request)).andReturn(response); + EasyMock.replay(rpcFactoryMock, loggingRpcMock); + logging = options.service(); + Metric metric = logging.create(METRIC_INFO); + assertEquals(new Metric(logging, new MetricInfo.BuilderImpl(METRIC_INFO)), metric); + } + + @Test + public void testCreateMetricAsync() throws ExecutionException, InterruptedException { + LogMetric metricPb = METRIC_INFO.toPb(); + Future response = Futures.immediateFuture(metricPb); + CreateLogMetricRequest request = CreateLogMetricRequest.newBuilder() + .setProjectName(PROJECT_PB) + .setMetric(metricPb) + .build(); + EasyMock.expect(loggingRpcMock.create(request)).andReturn(response); + EasyMock.replay(rpcFactoryMock, loggingRpcMock); + logging = options.service(); + Metric metric = logging.createAsync(METRIC_INFO).get(); + assertEquals(new Metric(logging, new MetricInfo.BuilderImpl(METRIC_INFO)), metric); + } + + @Test + public void testUpdateMetric() { + LogMetric sinkPb = METRIC_INFO.toPb(); + Future response = Futures.immediateFuture(sinkPb); + UpdateLogMetricRequest request = UpdateLogMetricRequest.newBuilder() + .setMetricName(METRIC_NAME_PB) + .setMetric(sinkPb) + .build(); + EasyMock.expect(loggingRpcMock.update(request)).andReturn(response); + EasyMock.replay(rpcFactoryMock, loggingRpcMock); + logging = options.service(); + Metric sink = logging.update(METRIC_INFO); + assertEquals(new Metric(logging, new MetricInfo.BuilderImpl(METRIC_INFO)), sink); + } + + @Test + public void testUpdateMetricAsync() throws ExecutionException, InterruptedException { + LogMetric sinkPb = METRIC_INFO.toPb(); + Future response = Futures.immediateFuture(sinkPb); + UpdateLogMetricRequest request = UpdateLogMetricRequest.newBuilder() + .setMetricName(METRIC_NAME_PB) + .setMetric(sinkPb) + .build(); + EasyMock.expect(loggingRpcMock.update(request)).andReturn(response); + EasyMock.replay(rpcFactoryMock, loggingRpcMock); + logging = options.service(); + Metric sink = logging.updateAsync(METRIC_INFO).get(); + assertEquals(new Metric(logging, new MetricInfo.BuilderImpl(METRIC_INFO)), sink); + } + + @Test + public void testGetMetric() { + LogMetric sinkPb = METRIC_INFO.toPb(); + Future response = Futures.immediateFuture(sinkPb); + GetLogMetricRequest request = + GetLogMetricRequest.newBuilder().setMetricName(METRIC_NAME_PB).build(); + EasyMock.expect(loggingRpcMock.get(request)).andReturn(response); + EasyMock.replay(rpcFactoryMock, loggingRpcMock); + logging = options.service(); + Metric sink = logging.getMetric(METRIC_NAME); + assertEquals(new Metric(logging, new MetricInfo.BuilderImpl(METRIC_INFO)), sink); + } + + @Test + public void testGetMetric_Null() { + Future response = Futures.immediateFuture(null); + GetLogMetricRequest request = + GetLogMetricRequest.newBuilder().setMetricName(METRIC_NAME_PB).build(); + EasyMock.expect(loggingRpcMock.get(request)).andReturn(response); + EasyMock.replay(rpcFactoryMock, loggingRpcMock); + logging = options.service(); + assertNull(logging.getMetric(METRIC_NAME)); + } + + @Test + public void testGetMetricAsync() throws ExecutionException, InterruptedException { + LogMetric sinkPb = METRIC_INFO.toPb(); + Future response = Futures.immediateFuture(sinkPb); + GetLogMetricRequest request = + GetLogMetricRequest.newBuilder().setMetricName(METRIC_NAME_PB).build(); + EasyMock.expect(loggingRpcMock.get(request)).andReturn(response); + EasyMock.replay(rpcFactoryMock, loggingRpcMock); + logging = options.service(); + Metric sink = logging.getMetricAsync(METRIC_NAME).get(); + assertEquals(new Metric(logging, new MetricInfo.BuilderImpl(METRIC_INFO)), sink); + } + + @Test + public void testGetMetricAsync_Null() throws ExecutionException, InterruptedException { + Future response = Futures.immediateFuture(null); + GetLogMetricRequest request = + GetLogMetricRequest.newBuilder().setMetricName(METRIC_NAME_PB).build(); + EasyMock.expect(loggingRpcMock.get(request)).andReturn(response); + EasyMock.replay(rpcFactoryMock, loggingRpcMock); + logging = options.service(); + assertNull(logging.getMetricAsync(METRIC_NAME).get()); + } + + @Test + public void testDeleteMetric() { + DeleteLogMetricRequest request = + DeleteLogMetricRequest.newBuilder().setMetricName(METRIC_NAME_PB).build(); + Future response = Futures.immediateFuture(Empty.getDefaultInstance()); + EasyMock.expect(loggingRpcMock.delete(request)).andReturn(response); + EasyMock.replay(rpcFactoryMock, loggingRpcMock); + logging = options.service(); + assertTrue(logging.deleteMetric(METRIC_NAME)); + } + + @Test + public void testDeleteMetric_Null() { + DeleteLogMetricRequest request = + DeleteLogMetricRequest.newBuilder().setMetricName(METRIC_NAME_PB).build(); + Future response = Futures.immediateFuture(null); + EasyMock.expect(loggingRpcMock.delete(request)).andReturn(response); + EasyMock.replay(rpcFactoryMock, loggingRpcMock); + logging = options.service(); + assertFalse(logging.deleteMetric(METRIC_NAME)); + } + + @Test + public void testDeleteMetricAsync() throws ExecutionException, InterruptedException { + DeleteLogMetricRequest request = + DeleteLogMetricRequest.newBuilder().setMetricName(METRIC_NAME_PB).build(); + Future response = Futures.immediateFuture(Empty.getDefaultInstance()); + EasyMock.expect(loggingRpcMock.delete(request)).andReturn(response); + EasyMock.replay(rpcFactoryMock, loggingRpcMock); + logging = options.service(); + assertTrue(logging.deleteMetricAsync(METRIC_NAME).get()); + } + + @Test + public void testDeleteMetricAsync_Null() throws ExecutionException, InterruptedException { + DeleteLogMetricRequest request = + DeleteLogMetricRequest.newBuilder().setMetricName(METRIC_NAME_PB).build(); + Future response = Futures.immediateFuture(null); + EasyMock.expect(loggingRpcMock.delete(request)).andReturn(response); + EasyMock.replay(rpcFactoryMock, loggingRpcMock); + logging = options.service(); + assertFalse(logging.deleteMetricAsync(METRIC_NAME).get()); + } + + @Test + public void testListMetrics() { + String cursor = "cursor"; + EasyMock.replay(rpcFactoryMock); + logging = options.service(); + ListLogMetricsRequest request = + ListLogMetricsRequest.newBuilder().setProjectName(PROJECT_PB).build(); + List sinkList = ImmutableList.of( + new Metric(logging, new MetricInfo.BuilderImpl(METRIC_INFO)), + new Metric(logging, new MetricInfo.BuilderImpl(METRIC_INFO))); + ListLogMetricsResponse response = ListLogMetricsResponse.newBuilder() + .setNextPageToken(cursor) + .addAllMetrics(Lists.transform(sinkList, METRIC_TO_PB_FUNCTION)) + .build(); + Future futureResponse = Futures.immediateFuture(response); + EasyMock.expect(loggingRpcMock.list(request)).andReturn(futureResponse); + EasyMock.replay(loggingRpcMock); + Page page = logging.listMetrics(); + assertEquals(cursor, page.nextPageCursor()); + assertArrayEquals(sinkList.toArray(), Iterables.toArray(page.values(), Metric.class)); + } + + @Test + public void testListMetricsNextPage() { + String cursor1 = "cursor"; + EasyMock.replay(rpcFactoryMock); + logging = options.service(); + ListLogMetricsRequest request1 = + ListLogMetricsRequest.newBuilder().setProjectName(PROJECT_PB).build(); + ListLogMetricsRequest request2 = ListLogMetricsRequest.newBuilder() + .setProjectName(PROJECT_PB) + .setPageToken(cursor1) + .build(); + List sinkList1 = ImmutableList.of( + new Metric(logging, new MetricInfo.BuilderImpl(METRIC_INFO)), + new Metric(logging, new MetricInfo.BuilderImpl(METRIC_INFO))); + List sinkList2 = ImmutableList.of( + new Metric(logging, new MetricInfo.BuilderImpl(METRIC_INFO))); + ListLogMetricsResponse response1 = ListLogMetricsResponse.newBuilder() + .setNextPageToken(cursor1) + .addAllMetrics(Lists.transform(sinkList1, METRIC_TO_PB_FUNCTION)) + .build(); + String cursor2 = "nextCursor"; + ListLogMetricsResponse response2 = ListLogMetricsResponse.newBuilder() + .setNextPageToken(cursor2) + .addAllMetrics(Lists.transform(sinkList2, METRIC_TO_PB_FUNCTION)) + .build(); + Future futureResponse1 = Futures.immediateFuture(response1); + Future futureResponse2 = Futures.immediateFuture(response2); + EasyMock.expect(loggingRpcMock.list(request1)).andReturn(futureResponse1); + EasyMock.expect(loggingRpcMock.list(request2)).andReturn(futureResponse2); + EasyMock.replay(loggingRpcMock); + Page page = logging.listMetrics(); + assertEquals(cursor1, page.nextPageCursor()); + assertArrayEquals(sinkList1.toArray(), Iterables.toArray(page.values(), Metric.class)); + page = page.nextPage(); + assertEquals(cursor2, page.nextPageCursor()); + assertArrayEquals(sinkList2.toArray(), Iterables.toArray(page.values(), Metric.class)); + } + + @Test + public void testListMetricsEmpty() { + EasyMock.replay(rpcFactoryMock); + logging = options.service(); + ListLogMetricsRequest request = + ListLogMetricsRequest.newBuilder().setProjectName(PROJECT_PB).build(); + List sinkList = ImmutableList.of(); + ListLogMetricsResponse response = ListLogMetricsResponse.newBuilder() + .setNextPageToken("") + .addAllMetrics(Lists.transform(sinkList, METRIC_TO_PB_FUNCTION)) + .build(); + Future futureResponse = Futures.immediateFuture(response); + EasyMock.expect(loggingRpcMock.list(request)).andReturn(futureResponse); + EasyMock.replay(loggingRpcMock); + Page page = logging.listMetrics(); + assertNull(page.nextPageCursor()); + assertNull(page.nextPage()); + assertArrayEquals(sinkList.toArray(), Iterables.toArray(page.values(), Metric.class)); + } + + @Test + public void testListMetricsWithOptions() { + String cursor = "cursor"; + EasyMock.replay(rpcFactoryMock); + logging = options.service(); + ListLogMetricsRequest request = ListLogMetricsRequest.newBuilder() + .setPageToken(cursor) + .setPageSize(42) + .setProjectName(PROJECT_PB) + .build(); + List sinkList = ImmutableList.of( + new Metric(logging, new MetricInfo.BuilderImpl(METRIC_INFO)), + new Metric(logging, new MetricInfo.BuilderImpl(METRIC_INFO))); + ListLogMetricsResponse response = ListLogMetricsResponse.newBuilder() + .setNextPageToken(cursor) + .addAllMetrics(Lists.transform(sinkList, METRIC_TO_PB_FUNCTION)) + .build(); + Future futureResponse = Futures.immediateFuture(response); + EasyMock.expect(loggingRpcMock.list(request)).andReturn(futureResponse); + EasyMock.replay(loggingRpcMock); + Page page = logging.listMetrics(ListOption.pageSize(42), ListOption.pageToken(cursor)); + assertEquals(cursor, page.nextPageCursor()); + assertArrayEquals(sinkList.toArray(), Iterables.toArray(page.values(), Metric.class)); + } + + @Test + public void testListMetricsAsync() throws ExecutionException, InterruptedException { + String cursor = "cursor"; + EasyMock.replay(rpcFactoryMock); + logging = options.service(); + ListLogMetricsRequest request = + ListLogMetricsRequest.newBuilder().setProjectName(PROJECT_PB).build(); + List sinkList = ImmutableList.of( + new Metric(logging, new MetricInfo.BuilderImpl(METRIC_INFO)), + new Metric(logging, new MetricInfo.BuilderImpl(METRIC_INFO))); + ListLogMetricsResponse response = ListLogMetricsResponse.newBuilder() + .setNextPageToken(cursor) + .addAllMetrics(Lists.transform(sinkList, METRIC_TO_PB_FUNCTION)) + .build(); + Future futureResponse = Futures.immediateFuture(response); + EasyMock.expect(loggingRpcMock.list(request)).andReturn(futureResponse); + EasyMock.replay(loggingRpcMock); + AsyncPage page = logging.listMetricsAsync().get(); + assertEquals(cursor, page.nextPageCursor()); + assertArrayEquals(sinkList.toArray(), Iterables.toArray(page.values(), Metric.class)); + } + + @Test + public void testListMetricsAsyncNextPage() throws ExecutionException, InterruptedException { + String cursor1 = "cursor"; + EasyMock.replay(rpcFactoryMock); + logging = options.service(); + ListLogMetricsRequest request1 = + ListLogMetricsRequest.newBuilder().setProjectName(PROJECT_PB).build(); + ListLogMetricsRequest request2 = ListLogMetricsRequest.newBuilder() + .setProjectName(PROJECT_PB) + .setPageToken(cursor1) + .build(); + List sinkList1 = ImmutableList.of( + new Metric(logging, new MetricInfo.BuilderImpl(METRIC_INFO)), + new Metric(logging, new MetricInfo.BuilderImpl(METRIC_INFO))); + List sinkList2 = ImmutableList.of( + new Metric(logging, new MetricInfo.BuilderImpl(METRIC_INFO))); + ListLogMetricsResponse response1 = ListLogMetricsResponse.newBuilder() + .setNextPageToken(cursor1) + .addAllMetrics(Lists.transform(sinkList1, METRIC_TO_PB_FUNCTION)) + .build(); + String cursor2 = "nextCursor"; + ListLogMetricsResponse response2 = ListLogMetricsResponse.newBuilder() + .setNextPageToken(cursor2) + .addAllMetrics(Lists.transform(sinkList2, METRIC_TO_PB_FUNCTION)) + .build(); + Future futureResponse1 = Futures.immediateFuture(response1); + Future futureResponse2 = Futures.immediateFuture(response2); + EasyMock.expect(loggingRpcMock.list(request1)).andReturn(futureResponse1); + EasyMock.expect(loggingRpcMock.list(request2)).andReturn(futureResponse2); + EasyMock.replay(loggingRpcMock); + AsyncPage page = logging.listMetricsAsync().get(); + assertEquals(cursor1, page.nextPageCursor()); + assertArrayEquals(sinkList1.toArray(), Iterables.toArray(page.values(), Metric.class)); + page = page.nextPageAsync().get(); + assertEquals(cursor2, page.nextPageCursor()); + assertArrayEquals(sinkList2.toArray(), Iterables.toArray(page.values(), Metric.class)); + } + + @Test + public void testListMetricsAsyncEmpty() throws ExecutionException, InterruptedException { + EasyMock.replay(rpcFactoryMock); + logging = options.service(); + ListLogMetricsRequest request = + ListLogMetricsRequest.newBuilder().setProjectName(PROJECT_PB).build(); + List sinkList = ImmutableList.of(); + ListLogMetricsResponse response = ListLogMetricsResponse.newBuilder() + .setNextPageToken("") + .addAllMetrics(Lists.transform(sinkList, METRIC_TO_PB_FUNCTION)) + .build(); + Future futureResponse = Futures.immediateFuture(response); + EasyMock.expect(loggingRpcMock.list(request)).andReturn(futureResponse); + EasyMock.replay(loggingRpcMock); + AsyncPage page = logging.listMetricsAsync().get(); + assertNull(page.nextPageCursor()); + assertNull(page.nextPage()); + assertArrayEquals(sinkList.toArray(), Iterables.toArray(page.values(), Metric.class)); + } + + @Test + public void testListMetricsWithOptionsAsync() throws ExecutionException, InterruptedException { + String cursor = "cursor"; + EasyMock.replay(rpcFactoryMock); + logging = options.service(); + ListLogMetricsRequest request = ListLogMetricsRequest.newBuilder() + .setPageToken(cursor) + .setPageSize(42) + .setProjectName(PROJECT_PB) + .build(); + List sinkList = ImmutableList.of( + new Metric(logging, new MetricInfo.BuilderImpl(METRIC_INFO)), + new Metric(logging, new MetricInfo.BuilderImpl(METRIC_INFO))); + ListLogMetricsResponse response = ListLogMetricsResponse.newBuilder() + .setNextPageToken(cursor) + .addAllMetrics(Lists.transform(sinkList, METRIC_TO_PB_FUNCTION)) + .build(); + Future futureResponse = Futures.immediateFuture(response); + EasyMock.expect(loggingRpcMock.list(request)).andReturn(futureResponse); + EasyMock.replay(loggingRpcMock); + AsyncPage page = + logging.listMetricsAsync(ListOption.pageSize(42), ListOption.pageToken(cursor)).get(); + assertEquals(cursor, page.nextPageCursor()); + assertArrayEquals(sinkList.toArray(), Iterables.toArray(page.values(), Metric.class)); + } } diff --git a/gcloud-java-logging/src/test/java/com/google/cloud/logging/MetricTest.java b/gcloud-java-logging/src/test/java/com/google/cloud/logging/MetricTest.java new file mode 100644 index 000000000000..2a60fec15d15 --- /dev/null +++ b/gcloud-java-logging/src/test/java/com/google/cloud/logging/MetricTest.java @@ -0,0 +1,212 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * 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 com.google.cloud.logging; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.createStrictMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import com.google.common.util.concurrent.Futures; + +import org.junit.After; +import org.junit.Test; + +import java.util.concurrent.ExecutionException; + +public class MetricTest { + + private static final String NAME = "name"; + private static final String FILTER = "logName=projects/my-projectid/logs/syslog"; + private static final String DESCRIPTION = "description"; + private static final String NEW_NAME = "newName"; + private static final String NEW_FILTER = "logName=projects/my-projectid/logs/newSyslog"; + private static final String NEW_DESCRIPTION = "newDescription"; + private static final MetricInfo METRIC_INFO = MetricInfo.builder(NAME, FILTER) + .description(DESCRIPTION) + .build(); + private final Logging serviceMockReturnsOptions = createStrictMock(Logging.class); + private final LoggingOptions mockOptions = createMock(LoggingOptions.class); + private Logging pubsub; + private Metric expectedMetric; + private Metric metric; + + private void initializeExpectedMetric(int optionsCalls) { + expect(serviceMockReturnsOptions.options()).andReturn(mockOptions).times(optionsCalls); + replay(serviceMockReturnsOptions); + pubsub = createStrictMock(Logging.class); + expectedMetric = new Metric(serviceMockReturnsOptions, new Metric.BuilderImpl(METRIC_INFO)); + } + + private void initializeMetric() { + metric = new Metric(pubsub, new Metric.BuilderImpl(METRIC_INFO)); + } + + @After + public void tearDown() throws Exception { + verify(pubsub, serviceMockReturnsOptions); + } + + @Test + public void testBuilder() { + initializeExpectedMetric(2); + replay(pubsub); + MetricInfo builtMetric = expectedMetric.toBuilder() + .name(NEW_NAME) + .filter(NEW_FILTER) + .description(NEW_DESCRIPTION) + .build(); + assertEquals(NEW_NAME, builtMetric.name()); + assertEquals(NEW_DESCRIPTION, builtMetric.description()); + assertEquals(NEW_FILTER, builtMetric.filter()); + } + + @Test + public void testToBuilder() { + initializeExpectedMetric(2); + replay(pubsub); + compareMetric(expectedMetric, expectedMetric.toBuilder().build()); + } + + @Test + public void testReload() { + initializeExpectedMetric(2); + MetricInfo updatedInfo = METRIC_INFO.toBuilder().filter(NEW_FILTER).build(); + Metric expectedMetric = + new Metric(serviceMockReturnsOptions, new MetricInfo.BuilderImpl(updatedInfo)); + expect(pubsub.options()).andReturn(mockOptions); + expect(pubsub.getMetric(NAME)).andReturn(expectedMetric); + replay(pubsub); + initializeMetric(); + Metric updatedMetric = metric.reload(); + compareMetric(expectedMetric, updatedMetric); + } + + @Test + public void testReloadNull() { + initializeExpectedMetric(1); + expect(pubsub.options()).andReturn(mockOptions); + expect(pubsub.getMetric(NAME)).andReturn(null); + replay(pubsub); + initializeMetric(); + assertNull(metric.reload()); + } + + @Test + public void testReloadAsync() throws ExecutionException, InterruptedException { + initializeExpectedMetric(2); + MetricInfo updatedInfo = METRIC_INFO.toBuilder().filter(NEW_FILTER).build(); + Metric expectedMetric = new Metric(serviceMockReturnsOptions, new MetricInfo.BuilderImpl(updatedInfo)); + expect(pubsub.options()).andReturn(mockOptions); + expect(pubsub.getMetricAsync(NAME)) + .andReturn(Futures.immediateFuture(expectedMetric)); + replay(pubsub); + initializeMetric(); + Metric updatedMetric = metric.reloadAsync().get(); + compareMetric(expectedMetric, updatedMetric); + } + + @Test + public void testReloadAsyncNull() throws ExecutionException, InterruptedException { + initializeExpectedMetric(1); + expect(pubsub.options()).andReturn(mockOptions); + expect(pubsub.getMetricAsync(NAME)).andReturn(Futures.immediateFuture(null)); + replay(pubsub); + initializeMetric(); + assertNull(metric.reloadAsync().get()); + } + + @Test + public void testUpdate() { + initializeExpectedMetric(2); + MetricInfo updatedInfo = METRIC_INFO.toBuilder().filter(NEW_FILTER).build(); + Metric expectedMetric = new Metric(serviceMockReturnsOptions, new MetricInfo.BuilderImpl(updatedInfo)); + expect(pubsub.options()).andReturn(mockOptions); + expect(pubsub.update(updatedInfo)).andReturn(expectedMetric); + replay(pubsub); + initializeMetric(); + Metric updatedMetric = metric.update(updatedInfo); + compareMetric(expectedMetric, updatedMetric); + } + + @Test + public void testUpdateAsync() throws ExecutionException, InterruptedException { + initializeExpectedMetric(2); + MetricInfo updatedInfo = METRIC_INFO.toBuilder().filter(NEW_FILTER).build(); + Metric expectedMetric = new Metric(serviceMockReturnsOptions, new MetricInfo.BuilderImpl(updatedInfo)); + expect(pubsub.options()).andReturn(mockOptions); + expect(pubsub.updateAsync(updatedInfo)).andReturn(Futures.immediateFuture(expectedMetric)); + replay(pubsub); + initializeMetric(); + Metric updatedMetric = metric.updateAsync(updatedInfo).get(); + compareMetric(expectedMetric, updatedMetric); + } + + @Test + public void testDeleteTrue() { + initializeExpectedMetric(1); + expect(pubsub.options()).andReturn(mockOptions); + expect(pubsub.deleteMetric(NAME)).andReturn(true); + replay(pubsub); + initializeMetric(); + assertTrue(metric.delete()); + } + + @Test + public void testDeleteFalse() { + initializeExpectedMetric(1); + expect(pubsub.options()).andReturn(mockOptions); + expect(pubsub.deleteMetric(NAME)).andReturn(false); + replay(pubsub); + initializeMetric(); + assertFalse(metric.delete()); + } + + @Test + public void testDeleteAsyncTrue() throws ExecutionException, InterruptedException { + initializeExpectedMetric(1); + expect(pubsub.options()).andReturn(mockOptions); + expect(pubsub.deleteMetricAsync(NAME)).andReturn(Futures.immediateFuture(true)); + replay(pubsub); + initializeMetric(); + assertTrue(metric.deleteAsync().get()); + } + + @Test + public void testDeleteAsyncFalse() throws ExecutionException, InterruptedException { + initializeExpectedMetric(1); + expect(pubsub.options()).andReturn(mockOptions); + expect(pubsub.deleteMetricAsync(NAME)).andReturn(Futures.immediateFuture(false)); + replay(pubsub); + initializeMetric(); + assertFalse(metric.deleteAsync().get()); + } + + private void compareMetric(Metric expected, Metric value) { + assertEquals(expected, value); + assertEquals(expected.name(), value.name()); + assertEquals(expected.description(), value.description()); + assertEquals(expected.filter(), value.filter()); + assertEquals(expected.hashCode(), value.hashCode()); + assertEquals(expected.toString(), value.toString()); + } +}