From 38fb66feb1bc09ae5f69649bbaebf304fdf56e60 Mon Sep 17 00:00:00 2001 From: Sanjay Vasandani Date: Thu, 1 Jun 2023 14:04:34 -0700 Subject: [PATCH] Ignore party field in public API GetExchangeRequest. This also exposes the Exchanges and RecurringExchanges services in the public API server. Since these services were never exposed, this is not a breaking change. --- .../measurement/api/v2alpha/ExchangeKey.kt | 53 ++--- .../measurement/api/v2alpha/Providers.kt | 2 +- .../integration/common/BUILD.bazel | 1 + .../integration/common/InProcessKingdom.kt | 42 ++-- .../common/server/V2alphaPublicApiServer.kt | 24 ++- .../gcloud/spanner/SpannerExchangesService.kt | 4 - .../gcloud/spanner/readers/ExchangeReader.kt | 4 +- .../kingdom/service/api/v2alpha/BUILD.bazel | 1 + .../service/api/v2alpha/ExchangesService.kt | 125 ++++++++---- .../service/api/v2alpha/ProtoConversions.kt | 31 ++- .../api/v2alpha/RecurringExchangesService.kt | 12 +- .../service/internal/testing/Assertion.kt | 1 - .../testing/ExchangeStepsServiceTest.kt | 1 - .../internal/testing/ExchangesServiceTest.kt | 1 - .../measurement/internal/kingdom/BUILD.bazel | 1 - .../internal/kingdom/exchanges_service.proto | 2 - .../api/v2alpha/ExchangesServiceTest.kt | 192 ++++++++++++------ .../v2alpha/RecurringExchangesServiceTest.kt | 43 ++-- 18 files changed, 319 insertions(+), 221 deletions(-) diff --git a/src/main/kotlin/org/wfanet/measurement/api/v2alpha/ExchangeKey.kt b/src/main/kotlin/org/wfanet/measurement/api/v2alpha/ExchangeKey.kt index 4edbdb53d08..56efe7d5b2e 100644 --- a/src/main/kotlin/org/wfanet/measurement/api/v2alpha/ExchangeKey.kt +++ b/src/main/kotlin/org/wfanet/measurement/api/v2alpha/ExchangeKey.kt @@ -17,53 +17,26 @@ package org.wfanet.measurement.api.v2alpha import org.wfanet.measurement.common.ResourceNameParser import org.wfanet.measurement.common.api.ResourceKey -private val parsers = - listOf( - ResourceNameParser("recurringExchanges/{recurring_exchange}/exchanges/{exchange}"), - ResourceNameParser( - "dataProviders/{data_provider}/recurringExchanges/{recurring_exchange}/exchanges/{exchange}" - ), - ResourceNameParser( - "modelProviders/{model_provider}/recurringExchanges/{recurring_exchange}/exchanges/{exchange}" - ) - ) - /** [ExchangeKey] of an Exchange. */ -data class ExchangeKey( - val dataProviderId: String?, - val modelProviderId: String?, - val recurringExchangeId: String, - val exchangeId: String -) : ResourceKey { - init { - require((dataProviderId == null) || (modelProviderId == null)) - } - +data class ExchangeKey(val recurringExchangeId: String, val exchangeId: String) : ResourceKey { override fun toName(): String { - return parsers - .first() - .assembleName( - mapOf( - IdVariable.RECURRING_EXCHANGE to recurringExchangeId, - IdVariable.EXCHANGE to exchangeId - ) - ) + return parser.assembleName( + mapOf(IdVariable.RECURRING_EXCHANGE to recurringExchangeId, IdVariable.EXCHANGE to exchangeId) + ) } companion object FACTORY : ResourceKey.Factory { - val defaultValue = ExchangeKey(null, null, "", "") + private val parser = + ResourceNameParser("recurringExchanges/{recurring_exchange}/exchanges/{exchange}") + + val defaultValue = ExchangeKey("", "") override fun fromName(resourceName: String): ExchangeKey? { - for (parser in parsers) { - val idVars = parser.parseIdVars(resourceName) ?: continue - return ExchangeKey( - idVars[IdVariable.DATA_PROVIDER], - idVars[IdVariable.MODEL_PROVIDER], - idVars.getValue(IdVariable.RECURRING_EXCHANGE), - idVars.getValue(IdVariable.EXCHANGE) - ) - } - return null + val idVars = parser.parseIdVars(resourceName) ?: return null + return ExchangeKey( + idVars.getValue(IdVariable.RECURRING_EXCHANGE), + idVars.getValue(IdVariable.EXCHANGE) + ) } } } diff --git a/src/main/kotlin/org/wfanet/measurement/api/v2alpha/Providers.kt b/src/main/kotlin/org/wfanet/measurement/api/v2alpha/Providers.kt index 9ac117f5e9e..4aadea4eb90 100644 --- a/src/main/kotlin/org/wfanet/measurement/api/v2alpha/Providers.kt +++ b/src/main/kotlin/org/wfanet/measurement/api/v2alpha/Providers.kt @@ -22,7 +22,7 @@ import org.wfanet.measurement.common.identity.apiIdToExternalId import org.wfanet.measurement.internal.common.Provider import org.wfanet.measurement.internal.common.provider -fun ResourceKey.toProvider(): Provider? { +fun ResourceKey.toProvider(): Provider { return when (this) { is DataProviderKey -> provider { diff --git a/src/main/kotlin/org/wfanet/measurement/integration/common/BUILD.bazel b/src/main/kotlin/org/wfanet/measurement/integration/common/BUILD.bazel index 35703416022..2365198df99 100644 --- a/src/main/kotlin/org/wfanet/measurement/integration/common/BUILD.bazel +++ b/src/main/kotlin/org/wfanet/measurement/integration/common/BUILD.bazel @@ -25,6 +25,7 @@ java_library( "//src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha:measurement_consumers_service", "//src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha:measurements_service", "//src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha:public_keys_service", + "//src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha:recurring_exchanges_service", "//src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha:requisitions_service", ], ) diff --git a/src/main/kotlin/org/wfanet/measurement/integration/common/InProcessKingdom.kt b/src/main/kotlin/org/wfanet/measurement/integration/common/InProcessKingdom.kt index 50d46f6b99a..fd1ad293be7 100644 --- a/src/main/kotlin/org/wfanet/measurement/integration/common/InProcessKingdom.kt +++ b/src/main/kotlin/org/wfanet/measurement/integration/common/InProcessKingdom.kt @@ -39,6 +39,7 @@ import org.wfanet.measurement.internal.kingdom.MeasurementConsumersGrpcKt.Measur import org.wfanet.measurement.internal.kingdom.MeasurementLogEntriesGrpcKt.MeasurementLogEntriesCoroutineStub as InternalMeasurementLogEntriesCoroutineStub import org.wfanet.measurement.internal.kingdom.MeasurementsGrpcKt.MeasurementsCoroutineStub as InternalMeasurementsCoroutineStub import org.wfanet.measurement.internal.kingdom.PublicKeysGrpcKt.PublicKeysCoroutineStub as InternalPublicKeysCoroutineStub +import org.wfanet.measurement.internal.kingdom.RecurringExchangesGrpcKt.RecurringExchangesCoroutineStub as InternalRecurringExchangesCoroutineStub import org.wfanet.measurement.internal.kingdom.RequisitionsGrpcKt.RequisitionsCoroutineStub as InternalRequisitionsCoroutineStub import org.wfanet.measurement.kingdom.deploy.common.service.DataServices import org.wfanet.measurement.kingdom.deploy.common.service.toList @@ -54,13 +55,14 @@ import org.wfanet.measurement.kingdom.service.api.v2alpha.ExchangesService import org.wfanet.measurement.kingdom.service.api.v2alpha.MeasurementConsumersService import org.wfanet.measurement.kingdom.service.api.v2alpha.MeasurementsService import org.wfanet.measurement.kingdom.service.api.v2alpha.PublicKeysService +import org.wfanet.measurement.kingdom.service.api.v2alpha.RecurringExchangesService import org.wfanet.measurement.kingdom.service.api.v2alpha.RequisitionsService import org.wfanet.measurement.kingdom.service.api.v2alpha.withAccountAuthenticationServerInterceptor import org.wfanet.measurement.kingdom.service.api.v2alpha.withApiKeyAuthenticationServerInterceptor -import org.wfanet.measurement.kingdom.service.system.v1alpha.ComputationLogEntriesService as systemComputationLogEntriesService -import org.wfanet.measurement.kingdom.service.system.v1alpha.ComputationParticipantsService as systemComputationParticipantsService -import org.wfanet.measurement.kingdom.service.system.v1alpha.ComputationsService as systemComputationsService -import org.wfanet.measurement.kingdom.service.system.v1alpha.RequisitionsService as systemRequisitionsService +import org.wfanet.measurement.kingdom.service.system.v1alpha.ComputationLogEntriesService as SystemComputationLogEntriesService +import org.wfanet.measurement.kingdom.service.system.v1alpha.ComputationParticipantsService as SystemComputationParticipantsService +import org.wfanet.measurement.kingdom.service.system.v1alpha.ComputationsService as SystemComputationsService +import org.wfanet.measurement.kingdom.service.system.v1alpha.RequisitionsService as SystemRequisitionsService import org.wfanet.measurement.loadtest.panelmatchresourcesetup.PanelMatchResourceSetup /** TestRule that starts and stops all Kingdom gRPC services. */ @@ -110,6 +112,9 @@ class InProcessKingdom( InternalExchangeStepsCoroutineStub(internalApiChannel) } private val internalExchangesClient by lazy { InternalExchangesCoroutineStub(internalApiChannel) } + private val internalRecurringExchangesClient by lazy { + InternalRecurringExchangesCoroutineStub(internalApiChannel) + } private val internalDataServer = GrpcTestServerRule(logAllRequests = verboseGrpcLogging) { @@ -120,10 +125,10 @@ class InProcessKingdom( GrpcTestServerRule(logAllRequests = verboseGrpcLogging) { logger.info("Building Kingdom's system API services") listOf( - systemComputationsService(internalMeasurementsClient), - systemComputationLogEntriesService(internalMeasurementLogEntriesClient), - systemComputationParticipantsService(internalComputationParticipantsClient), - systemRequisitionsService(internalRequisitionsClient) + SystemComputationsService(internalMeasurementsClient), + SystemComputationLogEntriesService(internalMeasurementLogEntriesClient), + SystemComputationParticipantsService(internalComputationParticipantsClient), + SystemRequisitionsService(internalRequisitionsClient) ) .forEach { addService(it.withMetadataDuchyIdentities()) } } @@ -160,19 +165,18 @@ class InProcessKingdom( MeasurementConsumersService(internalMeasurementConsumersClient) .withMetadataPrincipalIdentities() .withAccountAuthenticationServerInterceptor(internalAccountsClient, redirectUri) - .withApiKeyAuthenticationServerInterceptor(internalApiKeysClient) - ) - .forEach { addService(it) } - - listOf( + .withApiKeyAuthenticationServerInterceptor(internalApiKeysClient), + RecurringExchangesService().withMetadataPrincipalIdentities(), + ExchangesService(internalRecurringExchangesClient, internalExchangesClient) + .withMetadataPrincipalIdentities(), + ExchangeStepsService(internalExchangeStepsClient).withMetadataPrincipalIdentities(), ExchangeStepAttemptsService( - internalExchangeStepAttemptsClient, - internalExchangeStepsClient - ), - ExchangeStepsService(internalExchangeStepsClient), - ExchangesService(internalExchangesClient) + internalExchangeStepAttemptsClient, + internalExchangeStepsClient + ) + .withMetadataPrincipalIdentities(), ) - .forEach { addService(it.withMetadataPrincipalIdentities()) } + .forEach { addService(it) } } /** Provides a gRPC channel to the Kingdom's public API. */ diff --git a/src/main/kotlin/org/wfanet/measurement/kingdom/deploy/common/server/V2alphaPublicApiServer.kt b/src/main/kotlin/org/wfanet/measurement/kingdom/deploy/common/server/V2alphaPublicApiServer.kt index ace95e8a231..7aaa595c91d 100644 --- a/src/main/kotlin/org/wfanet/measurement/kingdom/deploy/common/server/V2alphaPublicApiServer.kt +++ b/src/main/kotlin/org/wfanet/measurement/kingdom/deploy/common/server/V2alphaPublicApiServer.kt @@ -34,12 +34,14 @@ import org.wfanet.measurement.internal.kingdom.EventGroupMetadataDescriptorsGrpc import org.wfanet.measurement.internal.kingdom.EventGroupsGrpcKt.EventGroupsCoroutineStub as InternalEventGroupsCoroutineStub import org.wfanet.measurement.internal.kingdom.ExchangeStepAttemptsGrpcKt.ExchangeStepAttemptsCoroutineStub as InternalExchangeStepAttemptsCoroutineStub import org.wfanet.measurement.internal.kingdom.ExchangeStepsGrpcKt.ExchangeStepsCoroutineStub as InternalExchangeStepsCoroutineStub +import org.wfanet.measurement.internal.kingdom.ExchangesGrpcKt.ExchangesCoroutineStub as InternalExchangesCoroutineStub import org.wfanet.measurement.internal.kingdom.MeasurementConsumersGrpcKt.MeasurementConsumersCoroutineStub as InternalMeasurementConsumersCoroutineStub import org.wfanet.measurement.internal.kingdom.MeasurementsGrpcKt.MeasurementsCoroutineStub as InternalMeasurementsCoroutineStub import org.wfanet.measurement.internal.kingdom.ModelLinesGrpcKt.ModelLinesCoroutineStub as InternalModelLinesCoroutineStub import org.wfanet.measurement.internal.kingdom.ModelReleasesGrpcKt.ModelReleasesCoroutineStub as InternalModelReleasesCoroutineStub import org.wfanet.measurement.internal.kingdom.ModelSuitesGrpcKt.ModelSuitesCoroutineStub as InternalModelSuitesCoroutineStub import org.wfanet.measurement.internal.kingdom.PublicKeysGrpcKt.PublicKeysCoroutineStub as InternalPublicKeysCoroutineStub +import org.wfanet.measurement.internal.kingdom.RecurringExchangesGrpcKt.RecurringExchangesCoroutineStub as InternalRecurringExchangesCoroutineStub import org.wfanet.measurement.internal.kingdom.RequisitionsGrpcKt.RequisitionsCoroutineStub as InternalRequisitionsCoroutineStub import org.wfanet.measurement.kingdom.deploy.common.Llv2ProtocolConfig import org.wfanet.measurement.kingdom.deploy.common.Llv2ProtocolConfigFlags @@ -51,12 +53,14 @@ import org.wfanet.measurement.kingdom.service.api.v2alpha.EventGroupMetadataDesc import org.wfanet.measurement.kingdom.service.api.v2alpha.EventGroupsService import org.wfanet.measurement.kingdom.service.api.v2alpha.ExchangeStepAttemptsService import org.wfanet.measurement.kingdom.service.api.v2alpha.ExchangeStepsService +import org.wfanet.measurement.kingdom.service.api.v2alpha.ExchangesService import org.wfanet.measurement.kingdom.service.api.v2alpha.MeasurementConsumersService import org.wfanet.measurement.kingdom.service.api.v2alpha.MeasurementsService import org.wfanet.measurement.kingdom.service.api.v2alpha.ModelLinesService import org.wfanet.measurement.kingdom.service.api.v2alpha.ModelReleasesService import org.wfanet.measurement.kingdom.service.api.v2alpha.ModelSuitesService import org.wfanet.measurement.kingdom.service.api.v2alpha.PublicKeysService +import org.wfanet.measurement.kingdom.service.api.v2alpha.RecurringExchangesService import org.wfanet.measurement.kingdom.service.api.v2alpha.RequisitionsService import org.wfanet.measurement.kingdom.service.api.v2alpha.withAccountAuthenticationServerInterceptor import org.wfanet.measurement.kingdom.service.api.v2alpha.withApiKeyAuthenticationServerInterceptor @@ -128,13 +132,6 @@ private fun run( ) .withPrincipalsFromX509AuthorityKeyIdentifiers(principalLookup) .withApiKeyAuthenticationServerInterceptor(internalApiKeysCoroutineStub), - ExchangeStepAttemptsService( - InternalExchangeStepAttemptsCoroutineStub(channel), - internalExchangeStepsCoroutineStub - ) - .withPrincipalsFromX509AuthorityKeyIdentifiers(principalLookup), - ExchangeStepsService(internalExchangeStepsCoroutineStub) - .withPrincipalsFromX509AuthorityKeyIdentifiers(principalLookup), MeasurementsService( InternalMeasurementsCoroutineStub(channel), ) @@ -153,6 +150,19 @@ private fun run( RequisitionsService(InternalRequisitionsCoroutineStub(channel)) .withPrincipalsFromX509AuthorityKeyIdentifiers(principalLookup) .withApiKeyAuthenticationServerInterceptor(internalApiKeysCoroutineStub), + RecurringExchangesService().withPrincipalsFromX509AuthorityKeyIdentifiers(principalLookup), + ExchangesService( + InternalRecurringExchangesCoroutineStub(channel), + InternalExchangesCoroutineStub(channel) + ) + .withPrincipalsFromX509AuthorityKeyIdentifiers(principalLookup), + ExchangeStepsService(internalExchangeStepsCoroutineStub) + .withPrincipalsFromX509AuthorityKeyIdentifiers(principalLookup), + ExchangeStepAttemptsService( + InternalExchangeStepAttemptsCoroutineStub(channel), + internalExchangeStepsCoroutineStub + ) + .withPrincipalsFromX509AuthorityKeyIdentifiers(principalLookup), ModelLinesService(InternalModelLinesCoroutineStub(channel)) .withPrincipalsFromX509AuthorityKeyIdentifiers(principalLookup), ModelSuitesService(InternalModelSuitesCoroutineStub(channel)) diff --git a/src/main/kotlin/org/wfanet/measurement/kingdom/deploy/gcloud/spanner/SpannerExchangesService.kt b/src/main/kotlin/org/wfanet/measurement/kingdom/deploy/gcloud/spanner/SpannerExchangesService.kt index fe45c1bde0c..d3f3f4a1a9a 100644 --- a/src/main/kotlin/org/wfanet/measurement/kingdom/deploy/gcloud/spanner/SpannerExchangesService.kt +++ b/src/main/kotlin/org/wfanet/measurement/kingdom/deploy/gcloud/spanner/SpannerExchangesService.kt @@ -34,8 +34,6 @@ import org.wfanet.measurement.internal.kingdom.GetExchangeRequest import org.wfanet.measurement.internal.kingdom.StreamExchangesRequest import org.wfanet.measurement.kingdom.deploy.gcloud.spanner.common.ExchangeNotFoundException import org.wfanet.measurement.kingdom.deploy.gcloud.spanner.common.KingdomInternalException -import org.wfanet.measurement.kingdom.deploy.gcloud.spanner.common.PROVIDER_PARAM -import org.wfanet.measurement.kingdom.deploy.gcloud.spanner.common.providerFilter import org.wfanet.measurement.kingdom.deploy.gcloud.spanner.queries.StreamExchanges import org.wfanet.measurement.kingdom.deploy.gcloud.spanner.readers.ExchangeReader import org.wfanet.measurement.kingdom.deploy.gcloud.spanner.writers.BatchDeleteExchanges @@ -58,13 +56,11 @@ class SpannerExchangesService( """ WHERE RecurringExchanges.ExternalRecurringExchangeId = @external_recurring_exchange_id AND Exchanges.Date = @date - AND ${providerFilter(request.provider)} """ .trimIndent() ) bind("external_recurring_exchange_id" to request.externalRecurringExchangeId) bind("date" to request.date.toCloudDate()) - bind(PROVIDER_PARAM to request.provider.externalId) appendClause("LIMIT 1") } .execute(client.singleUse()) diff --git a/src/main/kotlin/org/wfanet/measurement/kingdom/deploy/gcloud/spanner/readers/ExchangeReader.kt b/src/main/kotlin/org/wfanet/measurement/kingdom/deploy/gcloud/spanner/readers/ExchangeReader.kt index 10ff00a7a77..39f28215b70 100644 --- a/src/main/kotlin/org/wfanet/measurement/kingdom/deploy/gcloud/spanner/readers/ExchangeReader.kt +++ b/src/main/kotlin/org/wfanet/measurement/kingdom/deploy/gcloud/spanner/readers/ExchangeReader.kt @@ -41,8 +41,6 @@ class ExchangeReader : SpannerReader() { SELECT $SELECT_COLUMNS_SQL FROM Exchanges JOIN RecurringExchanges USING (RecurringExchangeId) - LEFT JOIN ModelProviders USING (ModelProviderId) - LEFT JOIN DataProviders USING (DataProviderId) """ .trimIndent() @@ -111,6 +109,6 @@ class ExchangeReader : SpannerReader() { "RecurringExchanges.RecurringExchangeDetails" ) - val SELECT_COLUMNS_SQL = SELECT_COLUMNS.joinToString(", ") + private val SELECT_COLUMNS_SQL = SELECT_COLUMNS.joinToString(", ") } } diff --git a/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/BUILD.bazel b/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/BUILD.bazel index 860bda860d2..c8cf6fd7be6 100644 --- a/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/BUILD.bazel +++ b/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/BUILD.bazel @@ -196,6 +196,7 @@ kt_jvm_library( "//src/main/proto/wfa/measurement/internal/common:provider_kt_jvm_proto", "//src/main/proto/wfa/measurement/internal/kingdom:exchange_kt_jvm_proto", "//src/main/proto/wfa/measurement/internal/kingdom:exchanges_service_kt_jvm_grpc_proto", + "//src/main/proto/wfa/measurement/internal/kingdom:recurring_exchanges_service_kt_jvm_grpc_proto", "@wfa_common_jvm//imports/kotlin/kotlinx/coroutines:core", "@wfa_common_jvm//src/main/kotlin/org/wfanet/measurement/common", "@wfa_common_jvm//src/main/kotlin/org/wfanet/measurement/common/grpc", diff --git a/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/ExchangesService.kt b/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/ExchangesService.kt index 5b8885daa03..f97b9c3a4cc 100644 --- a/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/ExchangesService.kt +++ b/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/ExchangesService.kt @@ -18,66 +18,117 @@ import io.grpc.Status import io.grpc.StatusException import java.time.LocalDate import kotlinx.coroutines.flow.Flow +import org.wfanet.measurement.api.v2alpha.AccountPrincipal +import org.wfanet.measurement.api.v2alpha.DataProviderPrincipal +import org.wfanet.measurement.api.v2alpha.DuchyPrincipal import org.wfanet.measurement.api.v2alpha.Exchange import org.wfanet.measurement.api.v2alpha.ExchangeKey import org.wfanet.measurement.api.v2alpha.ExchangesGrpcKt.ExchangesCoroutineImplBase import org.wfanet.measurement.api.v2alpha.GetExchangeRequest -import org.wfanet.measurement.api.v2alpha.GetExchangeRequest.PartyCase import org.wfanet.measurement.api.v2alpha.ListExchangesRequest import org.wfanet.measurement.api.v2alpha.ListExchangesResponse +import org.wfanet.measurement.api.v2alpha.MeasurementConsumerPrincipal +import org.wfanet.measurement.api.v2alpha.MeasurementPrincipal +import org.wfanet.measurement.api.v2alpha.ModelProviderPrincipal import org.wfanet.measurement.api.v2alpha.UploadAuditTrailRequest -import org.wfanet.measurement.api.v2alpha.validateRequestProvider -import org.wfanet.measurement.common.grpc.failGrpc +import org.wfanet.measurement.api.v2alpha.principalFromCurrentContext import org.wfanet.measurement.common.grpc.grpcRequireNotNull +import org.wfanet.measurement.common.identity.ApiId +import org.wfanet.measurement.common.identity.ExternalId import org.wfanet.measurement.common.identity.apiIdToExternalId import org.wfanet.measurement.common.toProtoDate +import org.wfanet.measurement.internal.kingdom.Exchange as InternalExchange import org.wfanet.measurement.internal.kingdom.ExchangesGrpcKt.ExchangesCoroutineStub -import org.wfanet.measurement.internal.kingdom.getExchangeRequest +import org.wfanet.measurement.internal.kingdom.RecurringExchange as InternalRecurringExchange +import org.wfanet.measurement.internal.kingdom.RecurringExchangesGrpcKt.RecurringExchangesCoroutineStub +import org.wfanet.measurement.internal.kingdom.getExchangeRequest as internalGetExchangeRequest +import org.wfanet.measurement.internal.kingdom.getRecurringExchangeRequest as internalGetRecurringExchangeRequest -class ExchangesService(private val internalExchanges: ExchangesCoroutineStub) : - ExchangesCoroutineImplBase() { +class ExchangesService( + private val internalRecurringExchanges: RecurringExchangesCoroutineStub, + private val internalExchanges: ExchangesCoroutineStub +) : ExchangesCoroutineImplBase() { override suspend fun getExchange(request: GetExchangeRequest): Exchange { - val provider = validateRequestProvider(getProvider(request)) + fun permissionDeniedStatus() = + Status.PERMISSION_DENIED.withDescription( + "Permission denied on resource ${request.name} (or it might not exist)" + ) - val key = grpcRequireNotNull(ExchangeKey.fromName(request.name)) - val getRequest = getExchangeRequest { - externalRecurringExchangeId = apiIdToExternalId(key.recurringExchangeId) - date = LocalDate.parse(key.exchangeId).toProtoDate() - this.provider = provider - } - val internalExchange = + val authenticatedPrincipal: MeasurementPrincipal = principalFromCurrentContext + val key = + grpcRequireNotNull(ExchangeKey.fromName(request.name)) { + "Resource name not specified or invalid" + } + + val internalRecurringExchange: InternalRecurringExchange = try { - internalExchanges.getExchange(getRequest) - } catch (ex: StatusException) { - when (ex.status.code) { - Status.Code.NOT_FOUND -> failGrpc(Status.NOT_FOUND, ex) { "Exchange not found" } - else -> failGrpc(Status.UNKNOWN, ex) { "Unknown exception." } - } + internalRecurringExchanges.getRecurringExchange( + internalGetRecurringExchangeRequest { + externalRecurringExchangeId = ApiId(key.recurringExchangeId).externalId.value + } + ) + } catch (e: StatusException) { + throw when (e.status.code) { + Status.Code.NOT_FOUND -> permissionDeniedStatus() + Status.Code.DEADLINE_EXCEEDED -> Status.DEADLINE_EXCEEDED + else -> Status.UNKNOWN + } + .withCause(e) + .asRuntimeException() } - return try { - internalExchange.toV2Alpha() - } catch (e: Throwable) { - failGrpc(Status.INVALID_ARGUMENT) { e.message ?: "Failed to convert InternalExchange" } + when (authenticatedPrincipal) { + is DataProviderPrincipal -> { + val authenticatedExternalId = + ApiId(authenticatedPrincipal.resourceKey.dataProviderId).externalId + if ( + ExternalId(internalRecurringExchange.externalDataProviderId) != authenticatedExternalId + ) { + throw permissionDeniedStatus().asRuntimeException() + } + } + is ModelProviderPrincipal -> { + val authenticatedExternalId = + ApiId(authenticatedPrincipal.resourceKey.modelProviderId).externalId + if ( + ExternalId(internalRecurringExchange.externalModelProviderId) != authenticatedExternalId + ) { + throw permissionDeniedStatus().asRuntimeException() + } + } + is AccountPrincipal, + is DuchyPrincipal, + is MeasurementConsumerPrincipal -> throw permissionDeniedStatus().asRuntimeException() } + + val internalExchange: InternalExchange = + try { + internalExchanges.getExchange( + internalGetExchangeRequest { + externalRecurringExchangeId = apiIdToExternalId(key.recurringExchangeId) + date = LocalDate.parse(key.exchangeId).toProtoDate() + } + ) + } catch (e: StatusException) { + throw when (e.status.code) { + Status.Code.NOT_FOUND -> Status.NOT_FOUND.withDescription("Exchange not found") + Status.Code.DEADLINE_EXCEEDED -> Status.DEADLINE_EXCEEDED + else -> Status.UNKNOWN + } + .withCause(e) + .asRuntimeException() + } + + return internalExchange.toExchange() } override suspend fun listExchanges(request: ListExchangesRequest): ListExchangesResponse { - TODO("world-federation-of-advertisers/cross-media-measurement#3: implement this") + // TODO(world-federation-of-advertisers/cross-media-measurement#3): Implement this. + return super.listExchanges(request) } override suspend fun uploadAuditTrail(requests: Flow): Exchange { - TODO("world-federation-of-advertisers/cross-media-measurement#3: implement this") - } -} - -private fun getProvider(request: GetExchangeRequest): String { - return when (request.partyCase) { - PartyCase.DATA_PROVIDER -> request.dataProvider - PartyCase.MODEL_PROVIDER -> request.modelProvider - else -> - failGrpc(Status.UNAUTHENTICATED) { - "Caller identity is neither DataProvider nor ModelProvider" - } + // TODO(world-federation-of-advertisers/cross-media-measurement#3): Implement this. + return super.uploadAuditTrail(requests) } } diff --git a/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/ProtoConversions.kt b/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/ProtoConversions.kt index 6513589bc4b..c2535bbdd8a 100644 --- a/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/ProtoConversions.kt +++ b/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/ProtoConversions.kt @@ -505,35 +505,32 @@ fun Measurement.toInternal( } /** @throws [IllegalStateException] if InternalExchange.State not specified */ -fun InternalExchange.toV2Alpha(): Exchange { +fun InternalExchange.toExchange(): Exchange { + val source = this val exchangeKey = ExchangeKey( - dataProviderId = null, - modelProviderId = null, recurringExchangeId = externalIdToApiId(externalRecurringExchangeId), exchangeId = date.toLocalDate().toString() ) - return exchange { - name = exchangeKey.toName() - date = this@toV2Alpha.date - state = v2AlphaState - auditTrailHash = details.auditTrailHash - // TODO(@yunyeng): Add graphvizRepresentation to Exchange proto. - graphvizRepresentation = "" - } -} - -private val InternalExchange.v2AlphaState: Exchange.State - get() { - @Suppress("WHEN_ENUM_CAN_BE_NULL_IN_JAVA") - return when (this.state) { + @Suppress("WHEN_ENUM_CAN_BE_NULL_IN_JAVA") // ProtoBuf enum fields cannot be null. + val state = + when (source.state) { InternalExchange.State.ACTIVE -> Exchange.State.ACTIVE InternalExchange.State.SUCCEEDED -> Exchange.State.SUCCEEDED InternalExchange.State.FAILED -> Exchange.State.FAILED InternalExchange.State.STATE_UNSPECIFIED, InternalExchange.State.UNRECOGNIZED -> error("Invalid InternalExchange state.") } + + return exchange { + name = exchangeKey.toName() + date = source.date + this.state = state + auditTrailHash = source.details.auditTrailHash + // TODO(@yunyeng): Add graphvizRepresentation to Exchange proto. + graphvizRepresentation = "" } +} /** @throws [IllegalStateException] if InternalExchangeStep.State not specified */ fun InternalExchangeStep.toV2Alpha(): ExchangeStep { diff --git a/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/RecurringExchangesService.kt b/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/RecurringExchangesService.kt index 8838a10b64d..f61130d7b98 100644 --- a/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/RecurringExchangesService.kt +++ b/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/RecurringExchangesService.kt @@ -26,24 +26,28 @@ class RecurringExchangesService : RecurringExchangesCoroutineImplBase() { override suspend fun createRecurringExchange( request: CreateRecurringExchangeRequest ): RecurringExchange { - TODO("world-federation-of-advertisers/cross-media-measurement#3: implement this") + // TODO(world-federation-of-advertisers/cross-media-measurement#3): Implement this. + return super.createRecurringExchange(request) } override suspend fun getRecurringExchange( request: GetRecurringExchangeRequest ): RecurringExchange { - TODO("world-federation-of-advertisers/cross-media-measurement#3: implement this") + // TODO(world-federation-of-advertisers/cross-media-measurement#3): Implement this. + return super.getRecurringExchange(request) } override suspend fun listRecurringExchanges( request: ListRecurringExchangesRequest ): ListRecurringExchangesResponse { - TODO("world-federation-of-advertisers/cross-media-measurement#3: implement this") + // TODO(world-federation-of-advertisers/cross-media-measurement#3): Implement this. + return super.listRecurringExchanges(request) } override suspend fun retireRecurringExchange( request: RetireRecurringExchangeRequest ): RecurringExchange { - TODO("world-federation-of-advertisers/cross-media-measurement#3: implement this") + // TODO(world-federation-of-advertisers/cross-media-measurement#3): Implement this. + return super.retireRecurringExchange(request) } } diff --git a/src/main/kotlin/org/wfanet/measurement/kingdom/service/internal/testing/Assertion.kt b/src/main/kotlin/org/wfanet/measurement/kingdom/service/internal/testing/Assertion.kt index 48c5f9ff47d..0f80627e863 100644 --- a/src/main/kotlin/org/wfanet/measurement/kingdom/service/internal/testing/Assertion.kt +++ b/src/main/kotlin/org/wfanet/measurement/kingdom/service/internal/testing/Assertion.kt @@ -64,7 +64,6 @@ internal suspend fun ExchangesCoroutineImplBase.assertTestExchangeHasState( getExchangeRequest { externalRecurringExchangeId = EXTERNAL_RECURRING_EXCHANGE_ID date = EXCHANGE_DATE - provider = PROVIDER } ) ) diff --git a/src/main/kotlin/org/wfanet/measurement/kingdom/service/internal/testing/ExchangeStepsServiceTest.kt b/src/main/kotlin/org/wfanet/measurement/kingdom/service/internal/testing/ExchangeStepsServiceTest.kt index b613b937249..72f6dde678f 100644 --- a/src/main/kotlin/org/wfanet/measurement/kingdom/service/internal/testing/ExchangeStepsServiceTest.kt +++ b/src/main/kotlin/org/wfanet/measurement/kingdom/service/internal/testing/ExchangeStepsServiceTest.kt @@ -354,7 +354,6 @@ abstract class ExchangeStepsServiceTest { getExchangeRequest { externalRecurringExchangeId = EXTERNAL_RECURRING_EXCHANGE_ID date = exchangeDate - provider = response.exchangeStep.provider } ) assertThat(exchange.state).isEqualTo(Exchange.State.FAILED) diff --git a/src/main/kotlin/org/wfanet/measurement/kingdom/service/internal/testing/ExchangesServiceTest.kt b/src/main/kotlin/org/wfanet/measurement/kingdom/service/internal/testing/ExchangesServiceTest.kt index f34882ae629..5a6eee5eef3 100644 --- a/src/main/kotlin/org/wfanet/measurement/kingdom/service/internal/testing/ExchangesServiceTest.kt +++ b/src/main/kotlin/org/wfanet/measurement/kingdom/service/internal/testing/ExchangesServiceTest.kt @@ -458,7 +458,6 @@ abstract class ExchangesServiceTest { .apply { externalRecurringExchangeId = EXTERNAL_RECURRING_EXCHANGE_ID date = EXCHANGE_DATE - provider = PROVIDER } .build() return runBlocking { exchanges.getExchange(request) } diff --git a/src/main/proto/wfa/measurement/internal/kingdom/BUILD.bazel b/src/main/proto/wfa/measurement/internal/kingdom/BUILD.bazel index b0b2cfae071..5a9ce317c37 100644 --- a/src/main/proto/wfa/measurement/internal/kingdom/BUILD.bazel +++ b/src/main/proto/wfa/measurement/internal/kingdom/BUILD.bazel @@ -247,7 +247,6 @@ proto_library( strip_import_prefix = IMPORT_PREFIX, deps = [ ":exchange_proto", - "//src/main/proto/wfa/measurement/internal/common:provider_proto", "@com_google_googleapis//google/type:date_proto", "@com_google_protobuf//:empty_proto", ], diff --git a/src/main/proto/wfa/measurement/internal/kingdom/exchanges_service.proto b/src/main/proto/wfa/measurement/internal/kingdom/exchanges_service.proto index a9e21e55376..3ca875f0f7a 100644 --- a/src/main/proto/wfa/measurement/internal/kingdom/exchanges_service.proto +++ b/src/main/proto/wfa/measurement/internal/kingdom/exchanges_service.proto @@ -18,7 +18,6 @@ package wfa.measurement.internal.kingdom; import "google/protobuf/empty.proto"; import "google/type/date.proto"; -import "wfa/measurement/internal/common/provider.proto"; import "wfa/measurement/internal/kingdom/exchange.proto"; option java_package = "org.wfanet.measurement.internal.kingdom"; @@ -52,7 +51,6 @@ message GetExchangeRequest { // there is at most one `Exchange` for any given date -- this is part of the // `Exchange`'s primary key. google.type.Date date = 2; - wfa.measurement.internal.common.Provider provider = 3; } // Request for `/Exchanges.CreateExchange`. diff --git a/src/test/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/ExchangesServiceTest.kt b/src/test/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/ExchangesServiceTest.kt index ab9140a7633..7080df59269 100644 --- a/src/test/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/ExchangesServiceTest.kt +++ b/src/test/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/ExchangesServiceTest.kt @@ -19,8 +19,8 @@ import com.google.common.truth.extensions.proto.ProtoTruth.assertThat import com.google.protobuf.ByteString import com.google.type.date import io.grpc.Status +import io.grpc.StatusException import io.grpc.StatusRuntimeException -import kotlin.test.assertFails import kotlin.test.assertFailsWith import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.runBlocking @@ -35,125 +35,183 @@ import org.wfanet.measurement.api.v2alpha.Exchange import org.wfanet.measurement.api.v2alpha.ExchangeKey import org.wfanet.measurement.api.v2alpha.GetExchangeRequestKt import org.wfanet.measurement.api.v2alpha.ListExchangesRequest +import org.wfanet.measurement.api.v2alpha.ModelProviderKey +import org.wfanet.measurement.api.v2alpha.ModelProviderPrincipal import org.wfanet.measurement.api.v2alpha.exchange import org.wfanet.measurement.api.v2alpha.getExchangeRequest -import org.wfanet.measurement.api.v2alpha.testing.makeDataProvider -import org.wfanet.measurement.api.v2alpha.testing.makeModelProvider import org.wfanet.measurement.api.v2alpha.withPrincipal import org.wfanet.measurement.common.grpc.testing.GrpcTestServerRule import org.wfanet.measurement.common.grpc.testing.mockService -import org.wfanet.measurement.common.identity.externalIdToApiId +import org.wfanet.measurement.common.identity.ExternalId import org.wfanet.measurement.common.testing.verifyProtoArgument -import org.wfanet.measurement.internal.common.Provider -import org.wfanet.measurement.internal.common.provider +import org.wfanet.measurement.common.toLocalDate import org.wfanet.measurement.internal.kingdom.Exchange.State import org.wfanet.measurement.internal.kingdom.ExchangesGrpcKt.ExchangesCoroutineImplBase as InternalExchangesCoroutineImplBase import org.wfanet.measurement.internal.kingdom.ExchangesGrpcKt.ExchangesCoroutineStub as InternalExchangesCoroutineStub +import org.wfanet.measurement.internal.kingdom.RecurringExchangesGrpcKt.RecurringExchangesCoroutineImplBase as InternalRecurringExchangesCoroutineImplBase +import org.wfanet.measurement.internal.kingdom.RecurringExchangesGrpcKt.RecurringExchangesCoroutineStub as InternalRecurringExchangesCoroutineStub import org.wfanet.measurement.internal.kingdom.exchange as internalExchange import org.wfanet.measurement.internal.kingdom.exchangeDetails import org.wfanet.measurement.internal.kingdom.getExchangeRequest as internalGetExchangeRequest - -private val DATA_PROVIDER = makeDataProvider(12345L) -private val MODEL_PROVIDER = makeModelProvider(23456L) -private const val RECURRING_EXCHANGE_ID = 1L -private val DATE = date { - year = 2021 - month = 3 - day = 14 -} -private const val EXCHANGE_ID = "2021-03-14" - -private val AUDIT_TRAIL_HASH = ByteString.copyFromUtf8("some arbitrary audit_trail_hash") - -private val INTERNAL_EXCHANGE = internalExchange { - externalRecurringExchangeId = RECURRING_EXCHANGE_ID - date = DATE - state = State.ACTIVE - details = exchangeDetails { auditTrailHash = AUDIT_TRAIL_HASH } -} +import org.wfanet.measurement.internal.kingdom.recurringExchange as internalRecurringExchange @RunWith(JUnit4::class) class ExchangesServiceTest { - private val internalService: InternalExchangesCoroutineImplBase = - mockService() { onBlocking { getExchange(any()) }.thenReturn(INTERNAL_EXCHANGE) } + private val internalRecurringExchangesServiceMock: InternalRecurringExchangesCoroutineImplBase = + mockService { + onBlocking { getRecurringExchange(any()) }.thenReturn(INTERNAL_RECURRING_EXCHANGE) + } + private val internalExchangesServiceMock: InternalExchangesCoroutineImplBase = mockService { + onBlocking { getExchange(any()) }.thenReturn(INTERNAL_EXCHANGE) + } - @get:Rule val grpcTestServerRule = GrpcTestServerRule { addService(internalService) } + @get:Rule + val grpcTestServerRule = GrpcTestServerRule { + addService(internalRecurringExchangesServiceMock) + addService(internalExchangesServiceMock) + } - private val service = ExchangesService(InternalExchangesCoroutineStub(grpcTestServerRule.channel)) + private val service = + ExchangesService( + InternalRecurringExchangesCoroutineStub(grpcTestServerRule.channel), + InternalExchangesCoroutineStub(grpcTestServerRule.channel) + ) - private fun getExchange(init: GetExchangeRequestKt.Dsl.() -> Unit): Exchange = runBlocking { - service.getExchange(getExchangeRequest(init)) - } + private fun getExchange(fillRequest: GetExchangeRequestKt.Dsl.() -> Unit): Exchange = + runBlocking { + service.getExchange(getExchangeRequest(fillRequest)) + } @Test - fun `getExchange unauthenticated`() { - val exchangeKey = ExchangeKey(null, null, externalIdToApiId(RECURRING_EXCHANGE_ID), EXCHANGE_ID) - val e = + fun `getExchange throws UNAUTHENTICATED for no principal`() { + val exception = assertFailsWith { getExchange { - name = exchangeKey.toName() - dataProvider = DATA_PROVIDER + name = EXCHANGE_KEY.toName() + dataProvider = DATA_PROVIDER_KEY.toName() } } - assertThat(e.status.code).isEqualTo(Status.Code.UNAUTHENTICATED) + + assertThat(exception.status.code).isEqualTo(Status.Code.UNAUTHENTICATED) } @Test - fun `getExchange for DataProvider`() = runBlocking { - val principal = DataProviderPrincipal(DataProviderKey(externalIdToApiId(12345L))) - val provider = provider { - type = Provider.Type.DATA_PROVIDER - externalId = 12345L - } + fun `getExchange returns Exchange for DataProvider principal`() = runBlocking { + val principal = DataProviderPrincipal(DATA_PROVIDER_KEY) - val exchangeKey = ExchangeKey(null, null, externalIdToApiId(RECURRING_EXCHANGE_ID), EXCHANGE_ID) - val response = - withPrincipal(principal) { - getExchange { - name = exchangeKey.toName() - dataProvider = DATA_PROVIDER + val response = withPrincipal(principal) { getExchange { name = EXCHANGE_KEY.toName() } } + + assertThat(response) + .isEqualTo( + exchange { + name = EXCHANGE_KEY.toName() + date = EXCHANGE_DATE + state = Exchange.State.ACTIVE + auditTrailHash = AUDIT_TRAIL_HASH } - } + ) + + verifyProtoArgument( + internalExchangesServiceMock, + InternalExchangesCoroutineImplBase::getExchange + ) + .isEqualTo( + internalGetExchangeRequest { + externalRecurringExchangeId = EXTERNAL_RECURRING_EXCHANGE_ID.value + date = EXCHANGE_DATE + } + ) + } + + @Test + fun `getExchange returns Exchange for ModelProvider principal`() = runBlocking { + val principal = ModelProviderPrincipal(MODEL_PROVIDER_KEY) + + val response = withPrincipal(principal) { getExchange { name = EXCHANGE_KEY.toName() } } assertThat(response) .isEqualTo( exchange { - name = exchangeKey.toName() - date = DATE + name = EXCHANGE_KEY.toName() + date = EXCHANGE_DATE state = Exchange.State.ACTIVE auditTrailHash = AUDIT_TRAIL_HASH } ) - verifyProtoArgument(internalService, InternalExchangesCoroutineImplBase::getExchange) + verifyProtoArgument( + internalExchangesServiceMock, + InternalExchangesCoroutineImplBase::getExchange + ) .isEqualTo( internalGetExchangeRequest { - externalRecurringExchangeId = RECURRING_EXCHANGE_ID - date = DATE - this.provider = provider + externalRecurringExchangeId = EXTERNAL_RECURRING_EXCHANGE_ID.value + date = EXCHANGE_DATE } ) } @Test - fun `getExchange for DataProvider with wrong parent in Request`() { - val principal = DataProviderPrincipal(DataProviderKey(externalIdToApiId(12345L))) + fun `getExchange throws PERMISSION_DENIED for incorrect principal`() { + val principal = DataProviderPrincipal(DataProviderKey(ExternalId(404).apiId.value)) + + val exception = + assertFailsWith { + withPrincipal(principal) { getExchange { name = EXCHANGE_KEY.toName() } } + } - withPrincipal(principal) { assertFails { getExchange { modelProvider = MODEL_PROVIDER } } } + assertThat(exception.status.code).isEqualTo(Status.Code.PERMISSION_DENIED) } @Test - fun listExchanges() = - runBlocking { - assertFailsWith(NotImplementedError::class) { + fun `listExchanges throws UNIMPLEMENTED`() = runBlocking { + val exception = + assertFailsWith(StatusException::class) { service.listExchanges(ListExchangesRequest.getDefaultInstance()) } - } + + assertThat(exception.status.code).isEqualTo(Status.Code.UNIMPLEMENTED) + } @Test - fun uploadAuditTrail() = - runBlocking { - assertFailsWith(NotImplementedError::class) { service.uploadAuditTrail(emptyFlow()) } + fun `uploadAuditTrail throws UNIMPLEMENTED`() = runBlocking { + val exception = + assertFailsWith(StatusException::class) { service.uploadAuditTrail(emptyFlow()) } + + assertThat(exception.status.code).isEqualTo(Status.Code.UNIMPLEMENTED) + } + + companion object { + private val EXTERNAL_RECURRING_EXCHANGE_ID = ExternalId(1) + private val EXCHANGE_DATE = date { + year = 2021 + month = 3 + day = 14 } + private val EXTERNAL_DATA_PROVIDER_ID = ExternalId(12345) + private val EXTERNAL_MODEL_PROVIDER_ID = ExternalId(23456) + + private val DATA_PROVIDER_KEY = DataProviderKey(EXTERNAL_DATA_PROVIDER_ID.apiId.value) + private val MODEL_PROVIDER_KEY = ModelProviderKey(EXTERNAL_MODEL_PROVIDER_ID.apiId.value) + private val EXCHANGE_KEY = + ExchangeKey( + EXTERNAL_RECURRING_EXCHANGE_ID.apiId.value, + EXCHANGE_DATE.toLocalDate().toString() + ) + + private val AUDIT_TRAIL_HASH = ByteString.copyFromUtf8("some arbitrary audit_trail_hash") + + private val INTERNAL_RECURRING_EXCHANGE = internalRecurringExchange { + externalRecurringExchangeId = EXTERNAL_RECURRING_EXCHANGE_ID.value + externalDataProviderId = EXTERNAL_DATA_PROVIDER_ID.value + externalModelProviderId = EXTERNAL_MODEL_PROVIDER_ID.value + } + private val INTERNAL_EXCHANGE = internalExchange { + externalRecurringExchangeId = INTERNAL_RECURRING_EXCHANGE.externalRecurringExchangeId + date = EXCHANGE_DATE + state = State.ACTIVE + details = exchangeDetails { auditTrailHash = AUDIT_TRAIL_HASH } + } + } } diff --git a/src/test/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/RecurringExchangesServiceTest.kt b/src/test/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/RecurringExchangesServiceTest.kt index 696eac5bfd7..79ee64bd8b3 100644 --- a/src/test/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/RecurringExchangesServiceTest.kt +++ b/src/test/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/RecurringExchangesServiceTest.kt @@ -14,6 +14,9 @@ package org.wfanet.measurement.kingdom.service.api.v2alpha +import com.google.common.truth.Truth.assertThat +import io.grpc.Status +import io.grpc.StatusException import kotlin.test.assertFailsWith import kotlinx.coroutines.runBlocking import org.junit.Test @@ -30,34 +33,42 @@ class RecurringExchangesServiceTest { val service = RecurringExchangesService() @Test - fun createRecurringExchange() = - runBlocking { - assertFailsWith(NotImplementedError::class) { + fun createRecurringExchange() = runBlocking { + val exception = + assertFailsWith { service.createRecurringExchange(CreateRecurringExchangeRequest.getDefaultInstance()) } - } + + assertThat(exception.status.code).isEqualTo(Status.Code.UNIMPLEMENTED) + } @Test - fun getRecurringExchange() = - runBlocking { - assertFailsWith(NotImplementedError::class) { + fun getRecurringExchange() = runBlocking { + val exception = + assertFailsWith { service.getRecurringExchange(GetRecurringExchangeRequest.getDefaultInstance()) } - } + + assertThat(exception.status.code).isEqualTo(Status.Code.UNIMPLEMENTED) + } @Test - fun listRecurringExchanges() = - runBlocking { - assertFailsWith(NotImplementedError::class) { + fun listRecurringExchanges() = runBlocking { + val exception = + assertFailsWith { service.listRecurringExchanges(ListRecurringExchangesRequest.getDefaultInstance()) } - } + + assertThat(exception.status.code).isEqualTo(Status.Code.UNIMPLEMENTED) + } @Test - fun retireRecurringExchange() = - runBlocking { - assertFailsWith(NotImplementedError::class) { + fun retireRecurringExchange() = runBlocking { + val exception = + assertFailsWith { service.retireRecurringExchange(RetireRecurringExchangeRequest.getDefaultInstance()) } - } + + assertThat(exception.status.code).isEqualTo(Status.Code.UNIMPLEMENTED) + } }