From 62fac46e9f1177523816f4185484e403f73b7260 Mon Sep 17 00:00:00 2001 From: Sanjay Vasandani Date: Mon, 18 Mar 2024 12:50:55 -0700 Subject: [PATCH] Update cross-media-measurement-api dep for DataProvider capabilities. --- MODULE.bazel | 2 +- MODULE.bazel.lock | 890 +----------------- .../integration/common/InProcessKingdom.kt | 1 + .../common/server/V2alphaPublicApiServer.kt | 4 +- .../spanner/SpannerDataProvidersService.kt | 14 + .../ReplaceDataProviderCapabilities.kt | 52 + .../kingdom/service/api/v2alpha/BUILD.bazel | 4 + .../api/v2alpha/DataProvidersService.kt | 56 ++ .../api/v2alpha/MeasurementsService.kt | 256 ++++- .../service/api/v2alpha/ProtoConversions.kt | 114 +-- .../testing/DataProvidersServiceTest.kt | 30 +- .../internal/kingdom/data_provider.proto | 5 + .../kingdom/data_providers_service.proto | 10 + .../api/v2alpha/DataProvidersServiceTest.kt | 82 +- .../api/v2alpha/MeasurementsServiceTest.kt | 121 ++- 15 files changed, 618 insertions(+), 1023 deletions(-) create mode 100644 src/main/kotlin/org/wfanet/measurement/kingdom/deploy/gcloud/spanner/writers/ReplaceDataProviderCapabilities.kt diff --git a/MODULE.bazel b/MODULE.bazel index b938ad057dd..aab0973b834 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -136,7 +136,7 @@ bazel_dep( ) bazel_dep( name = "cross-media-measurement-api", - version = "0.60.0", + version = "0.61.0", repo_name = "wfa_measurement_proto", ) bazel_dep( diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 2d6913ff6db..f8e15160e24 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -1,6 +1,6 @@ { "lockFileVersion": 3, - "moduleFileHash": "8708b0e5573f4f48c1b66c6194d5342e976a77caa74e2c763582c858911e0e1f", + "moduleFileHash": "345c54701e235e16f5d2ac9ff668f36233ded6b5815699397a3ab517fd0dcb1d", "flags": { "cmdRegistries": [ "https://raw.githubusercontent.com/world-federation-of-advertisers/bazel-registry/main", @@ -377,7 +377,7 @@ "wfa_rules_cue": "rules_cue@0.4.0", "wfa_common_jvm": "common-jvm@0.78.0", "wfa_common_cpp": "common-cpp@0.12.0", - "wfa_measurement_proto": "cross-media-measurement-api@0.60.0", + "wfa_measurement_proto": "cross-media-measurement-api@0.61.0", "wfa_consent_signaling_client": "consent-signaling-client@0.20.0", "any_sketch": "any-sketch@0.6.0", "any_sketch_java": "any-sketch-java@0.5.0", @@ -2566,10 +2566,10 @@ } } }, - "cross-media-measurement-api@0.60.0": { + "cross-media-measurement-api@0.61.0": { "name": "cross-media-measurement-api", - "version": "0.60.0", - "key": "cross-media-measurement-api@0.60.0", + "version": "0.61.0", + "key": "cross-media-measurement-api@0.61.0", "repoName": "wfa_measurement_proto", "executionPlatformsToRegister": [], "toolchainsToRegister": [], @@ -2577,9 +2577,9 @@ { "extensionBzlFile": "@wfa_measurement_proto//build:non_module_deps.bzl", "extensionName": "non_module_deps", - "usingModule": "cross-media-measurement-api@0.60.0", + "usingModule": "cross-media-measurement-api@0.61.0", "location": { - "file": "https://raw.githubusercontent.com/world-federation-of-advertisers/bazel-registry/main/modules/cross-media-measurement-api/0.60.0/MODULE.bazel", + "file": "https://raw.githubusercontent.com/world-federation-of-advertisers/bazel-registry/main/modules/cross-media-measurement-api/0.61.0/MODULE.bazel", "line": 25, "column": 32 }, @@ -2603,14 +2603,14 @@ "bzlFile": "@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_archive", "attributes": { - "name": "cross-media-measurement-api~0.60.0", + "name": "cross-media-measurement-api~0.61.0", "urls": [ - "https://github.com/world-federation-of-advertisers/cross-media-measurement-api/archive/refs/tags/v0.60.0.tar.gz" + "https://github.com/world-federation-of-advertisers/cross-media-measurement-api/archive/refs/tags/v0.61.0.tar.gz" ], - "integrity": "sha256-m9CFeMyEBWFe5lENTk7tT2e3trguJvfteLU+QcWjZQ4=", - "strip_prefix": "cross-media-measurement-api-0.60.0", + "integrity": "sha256-LxTxZaCEMxYOhM3yGXIMZAKqP3jh+LlU8iaAHnt/qbs=", + "strip_prefix": "cross-media-measurement-api-0.61.0", "remote_patches": { - "https://raw.githubusercontent.com/world-federation-of-advertisers/bazel-registry/main/modules/cross-media-measurement-api/0.60.0/patches/module_dot_bazel.patch": "sha256-/0FKi8ly/uVbEhju1Jt/h+4baPJNSCeyPvM+N2IoNeU=" + "https://raw.githubusercontent.com/world-federation-of-advertisers/bazel-registry/main/modules/cross-media-measurement-api/0.61.0/patches/module_dot_bazel.patch": "sha256-vZgm5Ygz0+70mx6CqZeIx8UKshFL0ii5cn6GiLbaGtM=" }, "remote_patch_strip": 0 } @@ -2626,7 +2626,7 @@ "extensionUsages": [], "deps": { "wfa_rules_kotlin_jvm": "rules_kotlin_jvm@0.2.0", - "wfa_measurement_proto": "cross-media-measurement-api@0.60.0", + "wfa_measurement_proto": "cross-media-measurement-api@0.61.0", "wfa_common_jvm": "common-jvm@0.78.0", "bazel_tools": "bazel_tools@_", "local_config_platform": "local_config_platform@_" @@ -135754,98 +135754,6 @@ ] } }, - "@@aspect_rules_js~1.35.0//npm:extensions.bzl%pnpm": { - "general": { - "bzlTransitiveDigest": "3BWr8ZsvZTwntulRRqcGZCVb90v4t4GABUv6HWF+qKM=", - "accumulatedFileDigests": {}, - "envVariables": {}, - "generatedRepoSpecs": { - "pnpm__links": { - "bzlFile": "@@aspect_rules_js~1.35.0//npm/private:npm_import.bzl", - "ruleClassName": "npm_import_links", - "attributes": { - "name": "aspect_rules_js~1.35.0~pnpm~pnpm__links", - "package": "pnpm", - "version": "8.6.7", - "dev": false, - "root_package": "", - "link_packages": {}, - "deps": {}, - "transitive_closure": {}, - "lifecycle_build_target": false, - "lifecycle_hooks_env": [], - "lifecycle_hooks_execution_requirements": [ - "no-sandbox" - ], - "bins": {}, - "npm_translate_lock_repo": "", - "package_visibility": [ - "//visibility:public" - ] - } - }, - "pnpm": { - "bzlFile": "@@aspect_rules_js~1.35.0//npm/private:npm_import.bzl", - "ruleClassName": "npm_import_rule", - "attributes": { - "name": "aspect_rules_js~1.35.0~pnpm~pnpm", - "package": "pnpm", - "version": "8.6.7", - "root_package": "", - "link_workspace": "", - "link_packages": {}, - "integrity": "sha512-vRIWpD/L4phf9Bk2o/O2TDR8fFoJnpYrp2TKqTIZF/qZ2/rgL3qKXzHofHgbXsinwMoSEigz28sqk3pQ+yMEQQ==", - "url": "", - "commit": "", - "patch_args": [ - "-p0" - ], - "patches": [], - "custom_postinstall": "", - "npm_auth": "", - "npm_auth_basic": "", - "npm_auth_username": "", - "npm_auth_password": "", - "lifecycle_hooks": [], - "extra_build_content": "load(\"@aspect_rules_js//js:defs.bzl\", \"js_binary\")\njs_binary(name = \"pnpm\", data = glob([\"package/**\"]), entry_point = \"package/dist/pnpm.cjs\", visibility = [\"//visibility:public\"])", - "generate_bzl_library_targets": false - } - } - }, - "recordedRepoMappingEntries": [ - [ - "aspect_bazel_lib~1.39.0", - "bazel_skylib", - "bazel_skylib~1.5.0" - ], - [ - "aspect_bazel_lib~1.39.0", - "bazel_tools", - "bazel_tools" - ], - [ - "aspect_rules_js~1.35.0", - "aspect_bazel_lib", - "aspect_bazel_lib~1.39.0" - ], - [ - "aspect_rules_js~1.35.0", - "bazel_features", - "bazel_features~1.1.1" - ], - [ - "aspect_rules_js~1.35.0", - "bazel_skylib", - "bazel_skylib~1.5.0" - ], - [ - "aspect_rules_js~1.35.0", - "bazel_tools", - "bazel_tools" - ] - ] - } - }, "@@bazel_features~1.2.0//private:extensions.bzl%version_extension": { "general": { "bzlTransitiveDigest": "xm7Skm1Las5saxzFWt2hbS+e68BWi+MXyt6+lKIhjPA=", @@ -135918,34 +135826,6 @@ ] } }, - "@@bazel_tools//tools/android:android_extensions.bzl%remote_android_tools_extensions": { - "general": { - "bzlTransitiveDigest": "4x/FXzwoadac6uV9ItZ4eGOyCculGHHrKUhLFNWo3lA=", - "accumulatedFileDigests": {}, - "envVariables": {}, - "generatedRepoSpecs": { - "android_tools": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "bazel_tools~remote_android_tools_extensions~android_tools", - "sha256": "2b661a761a735b41c41b3a78089f4fc1982626c76ddb944604ae3ff8c545d3c2", - "url": "https://mirror.bazel.build/bazel_android_tools/android_tools_pkg-0.30.0.tar" - } - }, - "android_gmaven_r8": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_jar", - "attributes": { - "name": "bazel_tools~remote_android_tools_extensions~android_gmaven_r8", - "sha256": "57a696749695a09381a87bc2f08c3a8ed06a717a5caa3ef878a3077e0d3af19d", - "url": "https://maven.google.com/com/android/tools/r8/8.1.56/r8-8.1.56.jar" - } - } - }, - "recordedRepoMappingEntries": [] - } - }, "@@bazel_tools//tools/cpp:cc_configure.bzl%cc_configure_extension": { "general": { "bzlTransitiveDigest": "mcsWHq3xORJexV5/4eCvNOLxFOQKV6eli3fkr+tEaqE=", @@ -136886,548 +136766,6 @@ ] } }, - "@@grpc~1.48.1.bcr.2//bazel:grpc_deps.bzl%grpc_repo_deps_ext": { - "general": { - "bzlTransitiveDigest": "Vi/A+pHz0UslIVgXw0k4nRhXybndFcXR259m5TlMQXA=", - "accumulatedFileDigests": {}, - "envVariables": {}, - "generatedRepoSpecs": { - "io_opencensus_cpp": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~io_opencensus_cpp", - "sha256": "90d6fafa8b1a2ea613bf662731d3086e1c2ed286f458a95c81744df2dbae41b1", - "strip_prefix": "opencensus-cpp-c9a4da319bc669a772928ffc55af4a61be1a1176", - "urls": [ - "https://storage.googleapis.com/grpc-bazel-mirror/github.com/census-instrumentation/opencensus-cpp/archive/c9a4da319bc669a772928ffc55af4a61be1a1176.tar.gz", - "https://github.com/census-instrumentation/opencensus-cpp/archive/c9a4da319bc669a772928ffc55af4a61be1a1176.tar.gz" - ] - } - }, - "com_github_libuv_libuv": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~com_github_libuv_libuv", - "build_file": "@@grpc~1.48.1.bcr.2//third_party:libuv.BUILD", - "sha256": "5ca4e9091f3231d8ad8801862dc4e851c23af89c69141d27723157776f7291e7", - "strip_prefix": "libuv-02a9e1be252b623ee032a3137c0b0c94afbe6809", - "urls": [ - "https://storage.googleapis.com/grpc-bazel-mirror/github.com/libuv/libuv/archive/02a9e1be252b623ee032a3137c0b0c94afbe6809.tar.gz", - "https://github.com/libuv/libuv/archive/02a9e1be252b623ee032a3137c0b0c94afbe6809.tar.gz" - ] - } - }, - "com_google_googleapis": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~com_google_googleapis", - "sha256": "5bb6b0253ccf64b53d6c7249625a7e3f6c3bc6402abd52d3778bfa48258703a0", - "strip_prefix": "googleapis-2f9af297c84c55c8b871ba4495e01ade42476c92", - "urls": [ - "https://storage.googleapis.com/grpc-bazel-mirror/github.com/googleapis/googleapis/archive/2f9af297c84c55c8b871ba4495e01ade42476c92.tar.gz", - "https://github.com/googleapis/googleapis/archive/2f9af297c84c55c8b871ba4495e01ade42476c92.tar.gz" - ] - } - }, - "upb": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~upb", - "sha256": "d0fe259d650bf9547e75896a1307bfc7034195e4ae89f5139814d295991ba681", - "strip_prefix": "upb-bef53686ec702607971bd3ea4d4fefd80c6cc6e8", - "urls": [ - "https://storage.googleapis.com/grpc-bazel-mirror/github.com/protocolbuffers/upb/archive/bef53686ec702607971bd3ea4d4fefd80c6cc6e8.tar.gz", - "https://github.com/protocolbuffers/upb/archive/bef53686ec702607971bd3ea4d4fefd80c6cc6e8.tar.gz" - ] - } - }, - "rules_cc": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~rules_cc", - "sha256": "35f2fb4ea0b3e61ad64a369de284e4fbbdcdba71836a5555abb5e194cf119509", - "strip_prefix": "rules_cc-624b5d59dfb45672d4239422fa1e3de1822ee110", - "urls": [ - "https://storage.googleapis.com/grpc-bazel-mirror/github.com/bazelbuild/rules_cc/archive/624b5d59dfb45672d4239422fa1e3de1822ee110.tar.gz", - "https://github.com/bazelbuild/rules_cc/archive/624b5d59dfb45672d4239422fa1e3de1822ee110.tar.gz" - ] - } - }, - "boringssl": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~boringssl", - "sha256": "534fa658bd845fd974b50b10f444d392dfd0d93768c4a51b61263fd37d851c40", - "strip_prefix": "boringssl-b9232f9e27e5668bc0414879dcdedb2a59ea75f2", - "urls": [ - "https://storage.googleapis.com/grpc-bazel-mirror/github.com/google/boringssl/archive/b9232f9e27e5668bc0414879dcdedb2a59ea75f2.tar.gz", - "https://github.com/google/boringssl/archive/b9232f9e27e5668bc0414879dcdedb2a59ea75f2.tar.gz" - ] - } - }, - "bazel_gazelle": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~bazel_gazelle", - "sha256": "de69a09dc70417580aabf20a28619bb3ef60d038470c7cf8442fafcf627c21cb", - "urls": [ - "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.24.0/bazel-gazelle-v0.24.0.tar.gz", - "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.24.0/bazel-gazelle-v0.24.0.tar.gz" - ] - } - }, - "opencensus_proto": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~opencensus_proto", - "sha256": "b7e13f0b4259e80c3070b583c2f39e53153085a6918718b1c710caf7037572b0", - "strip_prefix": "opencensus-proto-0.3.0/src", - "urls": [ - "https://storage.googleapis.com/grpc-bazel-mirror/github.com/census-instrumentation/opencensus-proto/archive/v0.3.0.tar.gz", - "https://github.com/census-instrumentation/opencensus-proto/archive/v0.3.0.tar.gz" - ], - "patches": [ - "@@grpc~1.48.1.bcr.2//third_party:opencensus-proto.patch" - ], - "patch_args": [ - "-p2" - ] - } - }, - "com_googlesource_code_re2": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~com_googlesource_code_re2", - "sha256": "319a58a58d8af295db97dfeecc4e250179c5966beaa2d842a82f0a013b6a239b", - "strip_prefix": "re2-8e08f47b11b413302749c0d8b17a1c94777495d5", - "urls": [ - "https://storage.googleapis.com/grpc-bazel-mirror/github.com/google/re2/archive/8e08f47b11b413302749c0d8b17a1c94777495d5.tar.gz", - "https://github.com/google/re2/archive/8e08f47b11b413302749c0d8b17a1c94777495d5.tar.gz" - ] - } - }, - "bazel_skylib": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~bazel_skylib", - "urls": [ - "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.0.2/bazel-skylib-1.0.2.tar.gz", - "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.2/bazel-skylib-1.0.2.tar.gz" - ], - "sha256": "97e70364e9249702246c0e9444bccdc4b847bed1eb03c5a3ece4f83dfe6abc44" - } - }, - "com_github_cares_cares": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~com_github_cares_cares", - "build_file": "@@grpc~1.48.1.bcr.2//third_party:cares/cares.BUILD", - "sha256": "ec76c5e79db59762776bece58b69507d095856c37b81fd35bfb0958e74b61d93", - "strip_prefix": "c-ares-6654436a307a5a686b008c1d4c93b0085da6e6d8", - "urls": [ - "https://storage.googleapis.com/grpc-bazel-mirror/github.com/c-ares/c-ares/archive/6654436a307a5a686b008c1d4c93b0085da6e6d8.tar.gz", - "https://github.com/c-ares/c-ares/archive/6654436a307a5a686b008c1d4c93b0085da6e6d8.tar.gz" - ] - } - }, - "build_bazel_apple_support": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~build_bazel_apple_support", - "sha256": "76df040ade90836ff5543888d64616e7ba6c3a7b33b916aa3a4b68f342d1b447", - "urls": [ - "https://storage.googleapis.com/grpc-bazel-mirror/github.com/bazelbuild/apple_support/releases/download/0.11.0/apple_support.0.11.0.tar.gz", - "https://github.com/bazelbuild/apple_support/releases/download/0.11.0/apple_support.0.11.0.tar.gz" - ] - } - }, - "zlib": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~zlib", - "build_file": "@@grpc~1.48.1.bcr.2//third_party:zlib.BUILD", - "sha256": "ef47b0fbe646d69a2fc5ba012cb278de8e8946a8e9649f83a807cc05559f0eff", - "strip_prefix": "zlib-21767c654d31d2dccdde4330529775c6c5fd5389", - "urls": [ - "https://storage.googleapis.com/grpc-bazel-mirror/github.com/madler/zlib/archive/21767c654d31d2dccdde4330529775c6c5fd5389.tar.gz", - "https://github.com/madler/zlib/archive/21767c654d31d2dccdde4330529775c6c5fd5389.tar.gz" - ] - } - }, - "com_google_googletest": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~com_google_googletest", - "sha256": "c8de6c60e12ad014a28225c5247ee735861d85cf906df617f6a29954ca05f547", - "strip_prefix": "googletest-0e402173c97aea7a00749e825b194bfede4f2e45", - "urls": [ - "https://github.com/google/googletest/archive/0e402173c97aea7a00749e825b194bfede4f2e45.tar.gz" - ] - } - }, - "envoy_api": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~envoy_api", - "sha256": "c5807010b67033330915ca5a20483e30538ae5e689aa14b3631d6284beca4630", - "strip_prefix": "data-plane-api-9c42588c956220b48eb3099d186487c2f04d32ec", - "urls": [ - "https://storage.googleapis.com/grpc-bazel-mirror/github.com/envoyproxy/data-plane-api/archive/9c42588c956220b48eb3099d186487c2f04d32ec.tar.gz", - "https://github.com/envoyproxy/data-plane-api/archive/9c42588c956220b48eb3099d186487c2f04d32ec.tar.gz" - ] - } - }, - "build_bazel_rules_apple": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~build_bazel_rules_apple", - "sha256": "0052d452af7742c8f3a4e0929763388a66403de363775db7e90adecb2ba4944b", - "urls": [ - "https://storage.googleapis.com/grpc-bazel-mirror/github.com/bazelbuild/rules_apple/releases/download/0.31.3/rules_apple.0.31.3.tar.gz", - "https://github.com/bazelbuild/rules_apple/releases/download/0.31.3/rules_apple.0.31.3.tar.gz" - ] - } - }, - "com_github_cncf_udpa": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~com_github_cncf_udpa", - "sha256": "5bc8365613fe2f8ce6cc33959b7667b13b7fe56cb9d16ba740c06e1a7c4242fc", - "strip_prefix": "xds-cb28da3451f158a947dfc45090fe92b07b243bc1", - "urls": [ - "https://storage.googleapis.com/grpc-bazel-mirror/github.com/cncf/xds/archive/cb28da3451f158a947dfc45090fe92b07b243bc1.tar.gz", - "https://github.com/cncf/xds/archive/cb28da3451f158a947dfc45090fe92b07b243bc1.tar.gz" - ] - } - }, - "com_github_google_benchmark": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~com_github_google_benchmark", - "sha256": "0b921a3bc39e35f4275c8dcc658af2391c150fb966102341287b0401ff2e6f21", - "strip_prefix": "benchmark-0baacde3618ca617da95375e0af13ce1baadea47", - "urls": [ - "https://storage.googleapis.com/grpc-bazel-mirror/github.com/google/benchmark/archive/0baacde3618ca617da95375e0af13ce1baadea47.tar.gz", - "https://github.com/google/benchmark/archive/0baacde3618ca617da95375e0af13ce1baadea47.tar.gz" - ] - } - }, - "com_envoyproxy_protoc_gen_validate": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~com_envoyproxy_protoc_gen_validate", - "strip_prefix": "protoc-gen-validate-4694024279bdac52b77e22dc87808bd0fd732b69", - "sha256": "1e490b98005664d149b379a9529a6aa05932b8a11b76b4cd86f3d22d76346f47", - "urls": [ - "https://github.com/envoyproxy/protoc-gen-validate/archive/4694024279bdac52b77e22dc87808bd0fd732b69.tar.gz" - ], - "patches": [ - "@@grpc~1.48.1.bcr.2//third_party:protoc-gen-validate.patch" - ], - "patch_args": [ - "-p1" - ] - } - }, - "com_google_absl": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~com_google_absl", - "sha256": "4208129b49006089ba1d6710845a45e31c59b0ab6bff9e5788a87f55c5abd602", - "strip_prefix": "abseil-cpp-20220623.0", - "urls": [ - "https://storage.googleapis.com/grpc-bazel-mirror/github.com/abseil/abseil-cpp/archive/20220623.0.tar.gz", - "https://github.com/abseil/abseil-cpp/archive/20220623.0.tar.gz" - ] - } - }, - "bazel_toolchains": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~bazel_toolchains", - "sha256": "179ec02f809e86abf56356d8898c8bd74069f1bd7c56044050c2cd3d79d0e024", - "strip_prefix": "bazel-toolchains-4.1.0", - "urls": [ - "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/releases/download/4.1.0/bazel-toolchains-4.1.0.tar.gz", - "https://github.com/bazelbuild/bazel-toolchains/releases/download/4.1.0/bazel-toolchains-4.1.0.tar.gz" - ] - } - }, - "bazel_compdb": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~bazel_compdb", - "sha256": "bcecfd622c4ef272fd4ba42726a52e140b961c4eac23025f18b346c968a8cfb4", - "strip_prefix": "bazel-compilation-database-0.4.5", - "urls": [ - "https://storage.googleapis.com/grpc-bazel-mirror/github.com/grailbio/bazel-compilation-database/archive/0.4.5.tar.gz", - "https://github.com/grailbio/bazel-compilation-database/archive/0.4.5.tar.gz" - ] - } - } - }, - "recordedRepoMappingEntries": [ - [ - "grpc~1.48.1.bcr.2", - "bazel_tools", - "bazel_tools" - ], - [ - "grpc~1.48.1.bcr.2", - "com_github_grpc_grpc", - "grpc~1.48.1.bcr.2" - ] - ] - } - }, - "@@grpc~1.48.1.bcr.2//bazel:grpc_extra_deps.bzl%grpc_extra_deps_ext": { - "general": { - "bzlTransitiveDigest": "345t06/pCjxWelhkboLiBDO4rDWn6LUuR5uHtoQ55Uk=", - "accumulatedFileDigests": {}, - "envVariables": {}, - "generatedRepoSpecs": { - "com_google_googleapis_imports": { - "bzlFile": "@@grpc~1.48.1.bcr.2~grpc_repo_deps_ext~com_google_googleapis//:repository_rules.bzl", - "ruleClassName": "switched_rules", - "attributes": { - "name": "grpc~1.48.1.bcr.2~grpc_extra_deps_ext~com_google_googleapis_imports", - "rules": { - "proto_library_with_info": [ - "", - "" - ], - "moved_proto_library": [ - "", - "" - ], - "java_proto_library": [ - "", - "" - ], - "java_grpc_library": [ - "", - "" - ], - "java_gapic_library": [ - "", - "" - ], - "java_gapic_test": [ - "", - "" - ], - "java_gapic_assembly_gradle_pkg": [ - "", - "" - ], - "py_proto_library": [ - "@com_github_grpc_grpc//bazel:python_rules.bzl", - "" - ], - "py_grpc_library": [ - "@com_github_grpc_grpc//bazel:python_rules.bzl", - "" - ], - "py_gapic_library": [ - "", - "" - ], - "py_gapic_assembly_pkg": [ - "", - "" - ], - "go_proto_library": [ - "", - "" - ], - "go_library": [ - "", - "" - ], - "go_test": [ - "", - "" - ], - "go_gapic_library": [ - "", - "" - ], - "go_gapic_assembly_pkg": [ - "", - "" - ], - "cc_proto_library": [ - "native.cc_proto_library", - "" - ], - "cc_grpc_library": [ - "@com_github_grpc_grpc//bazel:cc_grpc_library.bzl", - "" - ], - "cc_gapic_library": [ - "", - "" - ], - "php_proto_library": [ - "", - "php_proto_library" - ], - "php_grpc_library": [ - "", - "php_grpc_library" - ], - "php_gapic_library": [ - "", - "php_gapic_library" - ], - "php_gapic_assembly_pkg": [ - "", - "php_gapic_assembly_pkg" - ], - "nodejs_gapic_library": [ - "", - "typescript_gapic_library" - ], - "nodejs_gapic_assembly_pkg": [ - "", - "typescript_gapic_assembly_pkg" - ], - "ruby_proto_library": [ - "", - "" - ], - "ruby_grpc_library": [ - "", - "" - ], - "ruby_ads_gapic_library": [ - "", - "" - ], - "ruby_cloud_gapic_library": [ - "", - "" - ], - "ruby_gapic_assembly_pkg": [ - "", - "" - ], - "csharp_proto_library": [ - "", - "" - ], - "csharp_grpc_library": [ - "", - "" - ], - "csharp_gapic_library": [ - "", - "" - ], - "csharp_gapic_assembly_pkg": [ - "", - "" - ] - } - } - } - }, - "recordedRepoMappingEntries": [ - [ - "grpc~1.48.1.bcr.2", - "com_envoyproxy_protoc_gen_validate", - "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~com_envoyproxy_protoc_gen_validate" - ], - [ - "grpc~1.48.1.bcr.2", - "com_google_googleapis", - "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~com_google_googleapis" - ], - [ - "grpc~1.48.1.bcr.2", - "com_google_protobuf", - "protobuf~23.1" - ], - [ - "grpc~1.48.1.bcr.2", - "envoy_api", - "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~envoy_api" - ], - [ - "grpc~1.48.1.bcr.2", - "io_bazel_rules_go", - "rules_go~0.43.0" - ], - [ - "grpc~1.48.1.bcr.2", - "upb", - "upb~0.0.0-20230516-61a97ef" - ], - [ - "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~bazel_gazelle", - "bazel_gazelle", - "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~bazel_gazelle" - ], - [ - "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~bazel_gazelle", - "bazel_tools", - "bazel_tools" - ], - [ - "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~com_envoyproxy_protoc_gen_validate", - "bazel_gazelle", - "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~bazel_gazelle" - ], - [ - "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~envoy_api", - "bazel_tools", - "bazel_tools" - ], - [ - "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~envoy_api", - "envoy_api", - "grpc~1.48.1.bcr.2~grpc_repo_deps_ext~envoy_api" - ], - [ - "protobuf~23.1", - "bazel_tools", - "bazel_tools" - ], - [ - "rules_go~0.43.0", - "bazel_tools", - "bazel_tools" - ], - [ - "upb~0.0.0-20230516-61a97ef", - "bazel_tools", - "bazel_tools" - ] - ] - } - }, "@@protobuf~23.1//:non_module_deps.bzl%non_module_deps": { "general": { "bzlTransitiveDigest": "y/QVhgwEpduKjMEF4YflykcfvrWnGGE8W8O3bTxy5eI=", @@ -137456,43 +136794,6 @@ ] } }, - "@@pybind11_bazel~2.11.1//:python_configure.bzl%extension": { - "general": { - "bzlTransitiveDigest": "RRKqSt2cSs5zINkktNrz0SiynLK0sYNcl2xJ5+cTTLQ=", - "accumulatedFileDigests": { - "@@pybind11_bazel~2.11.1//:MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e" - }, - "envVariables": {}, - "generatedRepoSpecs": { - "local_config_python": { - "bzlFile": "@@pybind11_bazel~2.11.1//:python_configure.bzl", - "ruleClassName": "python_configure", - "attributes": { - "name": "pybind11_bazel~2.11.1~extension~local_config_python" - } - }, - "pybind11": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "name": "pybind11_bazel~2.11.1~extension~pybind11", - "build_file": "@@pybind11_bazel~2.11.1//:pybind11.BUILD", - "strip_prefix": "pybind11-2.11.1", - "urls": [ - "https://github.com/pybind/pybind11/archive/v2.11.1.zip" - ] - } - } - }, - "recordedRepoMappingEntries": [ - [ - "pybind11_bazel~2.11.1", - "bazel_tools", - "bazel_tools" - ] - ] - } - }, "@@riegeli~0.0.0-20240122-ac83dce//:MODULE.bazel%_repo_rules": { "general": { "bzlTransitiveDigest": "nSp+oEsTv5DYy1r8s+aEwKWUHoJDhXelRNB5mFkZmdU=", @@ -138486,143 +137787,6 @@ ] } }, - "@@rules_jvm_external~6.0//:MODULE.bazel%_repo_rules": { - "general": { - "bzlTransitiveDigest": "nSp+oEsTv5DYy1r8s+aEwKWUHoJDhXelRNB5mFkZmdU=", - "accumulatedFileDigests": {}, - "envVariables": {}, - "generatedRepoSpecs": { - "coursier_cli": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_file", - "attributes": { - "sha256": "2b78bfdd3ef13fd1f42f158de0f029d7cbb1f4f652d51773445cf2b6f7918a87", - "urls": [ - "https://github.com/coursier/coursier/releases/download/v2.1.8/coursier.jar" - ], - "name": "rules_jvm_external~6.0~_repo_rules~coursier_cli" - } - }, - "buildifier-linux-arm64": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_file", - "attributes": { - "sha256": "917d599dbb040e63ae7a7e1adb710d2057811902fdc9e35cce925ebfd966eeb8", - "urls": [ - "https://github.com/bazelbuild/buildtools/releases/download/5.1.0/buildifier-linux-arm64" - ], - "name": "rules_jvm_external~6.0~_repo_rules~buildifier-linux-arm64" - } - }, - "buildifier-linux-x86_64": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_file", - "attributes": { - "sha256": "52bf6b102cb4f88464e197caac06d69793fa2b05f5ad50a7e7bf6fbd656648a3", - "urls": [ - "https://github.com/bazelbuild/buildtools/releases/download/5.1.0/buildifier-linux-amd64" - ], - "name": "rules_jvm_external~6.0~_repo_rules~buildifier-linux-x86_64" - } - }, - "buildifier-macos-arm64": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_file", - "attributes": { - "sha256": "745feb5ea96cb6ff39a76b2821c57591fd70b528325562486d47b5d08900e2e4", - "urls": [ - "https://github.com/bazelbuild/buildtools/releases/download/5.1.0/buildifier-darwin-arm64" - ], - "name": "rules_jvm_external~6.0~_repo_rules~buildifier-macos-arm64" - } - }, - "buildifier-macos-x86_64": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_file", - "attributes": { - "sha256": "c9378d9f4293fc38ec54a08fbc74e7a9d28914dae6891334401e59f38f6e65dc", - "urls": [ - "https://github.com/bazelbuild/buildtools/releases/download/5.1.0/buildifier-darwin-amd64" - ], - "name": "rules_jvm_external~6.0~_repo_rules~buildifier-macos-x86_64" - } - }, - "com.google.ar.sceneform_rendering": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_file", - "attributes": { - "downloaded_file_path": "rendering-1.10.0.aar", - "sha256": "d2f6cd1d54eee0d5557518d1edcf77a3ba37494ae94f9bb862e570ee426a3431", - "urls": [ - "https://dl.google.com/android/maven2/com/google/ar/sceneform/rendering/1.10.0/rendering-1.10.0.aar" - ], - "name": "rules_jvm_external~6.0~_repo_rules~com.google.ar.sceneform_rendering" - } - }, - "hamcrest_core_for_test": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_file", - "attributes": { - "downloaded_file_path": "hamcrest-core-1.3.jar", - "sha256": "66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9", - "urls": [ - "https://repo1.maven.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar" - ], - "name": "rules_jvm_external~6.0~_repo_rules~hamcrest_core_for_test" - } - }, - "hamcrest_core_srcs_for_test": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_file", - "attributes": { - "downloaded_file_path": "hamcrest-core-1.3-sources.jar", - "sha256": "e223d2d8fbafd66057a8848cc94222d63c3cedd652cc48eddc0ab5c39c0f84df", - "urls": [ - "https://repo1.maven.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3-sources.jar" - ], - "name": "rules_jvm_external~6.0~_repo_rules~hamcrest_core_srcs_for_test" - } - }, - "gson_for_test": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_file", - "attributes": { - "downloaded_file_path": "gson-2.9.0.jar", - "sha256": "c96d60551331a196dac54b745aa642cd078ef89b6f267146b705f2c2cbef052d", - "urls": [ - "https://repo1.maven.org/maven2/com/google/code/gson/gson/2.9.0/gson-2.9.0.jar" - ], - "name": "rules_jvm_external~6.0~_repo_rules~gson_for_test" - } - }, - "junit_platform_commons_for_test": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_file", - "attributes": { - "downloaded_file_path": "junit-platform-commons-1.8.2.jar", - "sha256": "d2e015fca7130e79af2f4608dc54415e4b10b592d77333decb4b1a274c185050", - "urls": [ - "https://repo1.maven.org/maven2/org/junit/platform/junit-platform-commons/1.8.2/junit-platform-commons-1.8.2.jar" - ], - "name": "rules_jvm_external~6.0~_repo_rules~junit_platform_commons_for_test" - } - }, - "google_api_services_compute_javadoc_for_test": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_file", - "attributes": { - "downloaded_file_path": "google-api-services-compute-v1-rev235-1.25.0-javadoc.jar", - "sha256": "b03be5ee8effba3bfbaae53891a9c01d70e2e3bd82ad8889d78e641b22bd76c2", - "urls": [ - "https://repo1.maven.org/maven2/com/google/apis/google-api-services-compute/v1-rev235-1.25.0/google-api-services-compute-v1-rev235-1.25.0-javadoc.jar" - ], - "name": "rules_jvm_external~6.0~_repo_rules~google_api_services_compute_javadoc_for_test" - } - } - }, - "recordedRepoMappingEntries": [] - } - }, "@@rules_jvm_external~6.0//:extensions.bzl%maven": { "general": { "bzlTransitiveDigest": "vOkppYc6wE/3x4HnEmuAd/USfcKbaNTBbUEl7bKmhsY=", @@ -151912,34 +151076,6 @@ ] ] } - }, - "@@upb~0.0.0-20230516-61a97ef//:non_module_deps.bzl%non_module_deps": { - "general": { - "bzlTransitiveDigest": "y/QVhgwEpduKjMEF4YflykcfvrWnGGE8W8O3bTxy5eI=", - "accumulatedFileDigests": {}, - "envVariables": {}, - "generatedRepoSpecs": { - "utf8_range": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_archive", - "attributes": { - "urls": [ - "https://github.com/protocolbuffers/utf8_range/archive/de0b4a8ff9b5d4c98108bdfe723291a33c52c54f.zip" - ], - "strip_prefix": "utf8_range-de0b4a8ff9b5d4c98108bdfe723291a33c52c54f", - "name": "upb~0.0.0-20230516-61a97ef~non_module_deps~utf8_range", - "sha256": "5da960e5e5d92394c809629a03af3c7709d2d3d0ca731dacb3a9fb4bf28f7702" - } - } - }, - "recordedRepoMappingEntries": [ - [ - "upb~0.0.0-20230516-61a97ef", - "bazel_tools", - "bazel_tools" - ] - ] - } } } } 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 385427fb988..a97aaf9f3c3 100644 --- a/src/main/kotlin/org/wfanet/measurement/integration/common/InProcessKingdom.kt +++ b/src/main/kotlin/org/wfanet/measurement/integration/common/InProcessKingdom.kt @@ -157,6 +157,7 @@ class InProcessKingdom( .withApiKeyAuthenticationServerInterceptor(internalApiKeysClient), MeasurementsService( internalMeasurementsClient, + internalDataProvidersClient, MEASUREMENT_NOISE_MECHANISMS, reachOnlyLlV2Enabled = true, ) 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 123b6782325..46ff248aab7 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 @@ -118,6 +118,7 @@ private fun run( val internalApiKeysCoroutineStub = InternalApiKeysCoroutineStub(channel) val internalRecurringExchangesCoroutineStub = InternalRecurringExchangesCoroutineStub(channel) val internalExchangeStepsCoroutineStub = InternalExchangeStepsCoroutineStub(channel) + val internalDataProvidersStub = InternalDataProvidersCoroutineStub(channel) // TODO: do we need something similar to .withDuchyIdentities() for EDP and MC? val services: List = @@ -135,7 +136,7 @@ private fun run( CertificatesService(InternalCertificatesCoroutineStub(channel)) .withPrincipalsFromX509AuthorityKeyIdentifiers(principalLookup) .withApiKeyAuthenticationServerInterceptor(internalApiKeysCoroutineStub), - DataProvidersService(InternalDataProvidersCoroutineStub(channel)) + DataProvidersService(internalDataProvidersStub) .withPrincipalsFromX509AuthorityKeyIdentifiers(principalLookup) .withApiKeyAuthenticationServerInterceptor(internalApiKeysCoroutineStub), EventGroupsService(InternalEventGroupsCoroutineStub(channel)) @@ -148,6 +149,7 @@ private fun run( .withApiKeyAuthenticationServerInterceptor(internalApiKeysCoroutineStub), MeasurementsService( InternalMeasurementsCoroutineStub(channel), + internalDataProvidersStub, v2alphaFlags.directNoiseMechanisms, v2alphaFlags.reachOnlyLlV2Enabled, ) diff --git a/src/main/kotlin/org/wfanet/measurement/kingdom/deploy/gcloud/spanner/SpannerDataProvidersService.kt b/src/main/kotlin/org/wfanet/measurement/kingdom/deploy/gcloud/spanner/SpannerDataProvidersService.kt index 4dbfa949cfe..c5d7e7e8515 100644 --- a/src/main/kotlin/org/wfanet/measurement/kingdom/deploy/gcloud/spanner/SpannerDataProvidersService.kt +++ b/src/main/kotlin/org/wfanet/measurement/kingdom/deploy/gcloud/spanner/SpannerDataProvidersService.kt @@ -28,6 +28,7 @@ import org.wfanet.measurement.internal.kingdom.DataProvider import org.wfanet.measurement.internal.kingdom.DataProvidersGrpcKt.DataProvidersCoroutineImplBase import org.wfanet.measurement.internal.kingdom.GetDataProviderRequest import org.wfanet.measurement.internal.kingdom.ReplaceDataAvailabilityIntervalRequest +import org.wfanet.measurement.internal.kingdom.ReplaceDataProviderCapabilitiesRequest import org.wfanet.measurement.internal.kingdom.ReplaceDataProviderRequiredDuchiesRequest import org.wfanet.measurement.internal.kingdom.batchGetDataProvidersResponse import org.wfanet.measurement.kingdom.deploy.gcloud.spanner.common.DataProviderNotFoundException @@ -35,6 +36,7 @@ import org.wfanet.measurement.kingdom.deploy.gcloud.spanner.common.KingdomIntern import org.wfanet.measurement.kingdom.deploy.gcloud.spanner.readers.DataProviderReader import org.wfanet.measurement.kingdom.deploy.gcloud.spanner.writers.CreateDataProvider import org.wfanet.measurement.kingdom.deploy.gcloud.spanner.writers.ReplaceDataAvailabilityInterval +import org.wfanet.measurement.kingdom.deploy.gcloud.spanner.writers.ReplaceDataProviderCapabilities import org.wfanet.measurement.kingdom.deploy.gcloud.spanner.writers.ReplaceDataProviderRequiredDuchies class SpannerDataProvidersService( @@ -99,4 +101,16 @@ class SpannerDataProvidersService( throw e.asStatusRuntimeException(Status.Code.NOT_FOUND, "DataProvider not found.") } } + + override suspend fun replaceDataProviderCapabilities( + request: ReplaceDataProviderCapabilitiesRequest + ): DataProvider { + grpcRequire(request.externalDataProviderId != 0L) { "external_data_provider_id is missing." } + + try { + return ReplaceDataProviderCapabilities(request).execute(client, idGenerator) + } catch (e: DataProviderNotFoundException) { + throw e.asStatusRuntimeException(Status.Code.NOT_FOUND, "DataProvider not found.") + } + } } diff --git a/src/main/kotlin/org/wfanet/measurement/kingdom/deploy/gcloud/spanner/writers/ReplaceDataProviderCapabilities.kt b/src/main/kotlin/org/wfanet/measurement/kingdom/deploy/gcloud/spanner/writers/ReplaceDataProviderCapabilities.kt new file mode 100644 index 00000000000..b3090e13725 --- /dev/null +++ b/src/main/kotlin/org/wfanet/measurement/kingdom/deploy/gcloud/spanner/writers/ReplaceDataProviderCapabilities.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2023 The Cross-Media Measurement Authors + * + * 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 org.wfanet.measurement.kingdom.deploy.gcloud.spanner.writers + +import org.wfanet.measurement.common.identity.ExternalId +import org.wfanet.measurement.gcloud.spanner.bufferUpdateMutation +import org.wfanet.measurement.gcloud.spanner.set +import org.wfanet.measurement.gcloud.spanner.setJson +import org.wfanet.measurement.internal.kingdom.DataProvider +import org.wfanet.measurement.internal.kingdom.ReplaceDataProviderCapabilitiesRequest +import org.wfanet.measurement.internal.kingdom.copy +import org.wfanet.measurement.kingdom.deploy.gcloud.spanner.common.DataProviderNotFoundException +import org.wfanet.measurement.kingdom.deploy.gcloud.spanner.readers.DataProviderReader + +class ReplaceDataProviderCapabilities(private val request: ReplaceDataProviderCapabilitiesRequest) : + SpannerWriter() { + override suspend fun TransactionScope.runTransaction(): DataProvider { + val externalDataProviderId = ExternalId(request.externalDataProviderId) + val dataProviderResult: DataProviderReader.Result = + DataProviderReader().readByExternalDataProviderId(transactionContext, externalDataProviderId) + ?: throw DataProviderNotFoundException(externalDataProviderId) + + val updatedDetails: DataProvider.Details = + dataProviderResult.dataProvider.details.copy { capabilities = request.capabilities } + + transactionContext.bufferUpdateMutation("DataProviders") { + set("DataProviderId" to dataProviderResult.dataProviderId) + set("DataProviderDetails" to updatedDetails) + setJson("DataProviderDetailsJson" to updatedDetails) + } + + return dataProviderResult.dataProvider.copy { details = updatedDetails } + } + + override fun ResultScope.buildResult(): DataProvider { + return checkNotNull(transactionResult) + } +} 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 0465f91c73c..1b1f81b9f5c 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 @@ -247,6 +247,9 @@ kt_jvm_library( "//src/main/kotlin/org/wfanet/measurement/api:public_api_version", "//src/main/kotlin/org/wfanet/measurement/api/v2alpha:principal_server_interceptor", "//src/main/kotlin/org/wfanet/measurement/api/v2alpha:resource_key", + "//src/main/kotlin/org/wfanet/measurement/kingdom/deploy/common:hmss_protocol_config", + "//src/main/kotlin/org/wfanet/measurement/kingdom/deploy/common:llv2_protocol_config", + "//src/main/kotlin/org/wfanet/measurement/kingdom/deploy/common:ro_llv2_protocol_config", "//src/main/proto/wfa/measurement/api/v2alpha:crypto_kt_jvm_proto", "//src/main/proto/wfa/measurement/api/v2alpha:differential_privacy_kt_jvm_proto", "//src/main/proto/wfa/measurement/api/v2alpha:measurement_kt_jvm_proto", @@ -254,6 +257,7 @@ kt_jvm_library( "//src/main/proto/wfa/measurement/api/v2alpha:measurements_service_kt_jvm_grpc_proto", "//src/main/proto/wfa/measurement/api/v2alpha:page_token_kt_jvm_proto", "//src/main/proto/wfa/measurement/api/v2alpha:protocol_config_kt_jvm_proto", + "//src/main/proto/wfa/measurement/internal/kingdom:data_providers_service_kt_jvm_grpc_proto", "//src/main/proto/wfa/measurement/internal/kingdom:measurement_kt_jvm_proto", "//src/main/proto/wfa/measurement/internal/kingdom:measurements_service_kt_jvm_grpc_proto", "@wfa_common_jvm//imports/kotlin/kotlinx/coroutines:core", diff --git a/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/DataProvidersService.kt b/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/DataProvidersService.kt index f75b2516bba..7a30045e1bf 100644 --- a/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/DataProvidersService.kt +++ b/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/DataProvidersService.kt @@ -22,6 +22,7 @@ import org.wfanet.measurement.api.Version import org.wfanet.measurement.api.v2alpha.DataProvider import org.wfanet.measurement.api.v2alpha.DataProviderCertificateKey import org.wfanet.measurement.api.v2alpha.DataProviderKey +import org.wfanet.measurement.api.v2alpha.DataProviderKt import org.wfanet.measurement.api.v2alpha.DataProviderPrincipal import org.wfanet.measurement.api.v2alpha.DataProvidersGrpcKt.DataProvidersCoroutineImplBase as DataProvidersCoroutineService import org.wfanet.measurement.api.v2alpha.DuchyKey @@ -31,6 +32,7 @@ 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.ReplaceDataAvailabilityIntervalRequest +import org.wfanet.measurement.api.v2alpha.ReplaceDataProviderCapabilitiesRequest import org.wfanet.measurement.api.v2alpha.ReplaceDataProviderRequiredDuchiesRequest import org.wfanet.measurement.api.v2alpha.dataProvider import org.wfanet.measurement.api.v2alpha.principalFromCurrentContext @@ -40,12 +42,15 @@ import org.wfanet.measurement.common.ProtoReflection import org.wfanet.measurement.common.grpc.failGrpc import org.wfanet.measurement.common.grpc.grpcRequire import org.wfanet.measurement.common.grpc.grpcRequireNotNull +import org.wfanet.measurement.common.identity.ApiId import org.wfanet.measurement.common.identity.apiIdToExternalId import org.wfanet.measurement.common.identity.externalIdToApiId import org.wfanet.measurement.internal.kingdom.DataProvider as InternalDataProvider +import org.wfanet.measurement.internal.kingdom.DataProviderKt as InternalDataProviderKt import org.wfanet.measurement.internal.kingdom.DataProvidersGrpcKt.DataProvidersCoroutineStub import org.wfanet.measurement.internal.kingdom.getDataProviderRequest import org.wfanet.measurement.internal.kingdom.replaceDataAvailabilityIntervalRequest +import org.wfanet.measurement.internal.kingdom.replaceDataProviderCapabilitiesRequest import org.wfanet.measurement.internal.kingdom.replaceDataProviderRequiredDuchiesRequest class DataProvidersService(private val internalClient: DataProvidersCoroutineStub) : @@ -177,6 +182,42 @@ class DataProvidersService(private val internalClient: DataProvidersCoroutineStu } return internalDataProvider.toDataProvider() } + + override suspend fun replaceDataProviderCapabilities( + request: ReplaceDataProviderCapabilitiesRequest + ): DataProvider { + val key: DataProviderKey = + grpcRequireNotNull(DataProviderKey.fromName(request.name)) { + "Resource name unspecified or invalid" + } + + val principal: MeasurementPrincipal = principalFromCurrentContext + if (principal.resourceKey != key) { + failGrpc(Status.PERMISSION_DENIED) { + "Permission for method replaceDataProviderCapabilities denied on resource $request.name" + } + } + + val response: InternalDataProvider = + try { + internalClient.replaceDataProviderCapabilities( + replaceDataProviderCapabilitiesRequest { + externalDataProviderId = ApiId(key.dataProviderId).externalId.value + capabilities = request.capabilities.toInternal() + } + ) + } catch (e: StatusException) { + throw when (e.status.code) { + Status.Code.DEADLINE_EXCEEDED -> Status.DEADLINE_EXCEEDED + Status.Code.CANCELLED -> Status.CANCELLED + Status.Code.NOT_FOUND -> Status.NOT_FOUND.withDescription("DataProvider not found") + else -> Status.UNKNOWN + } + .withCause(e) + .asRuntimeException() + } + return response.toDataProvider() + } } private fun InternalDataProvider.toDataProvider(): DataProvider { @@ -204,5 +245,20 @@ private fun InternalDataProvider.toDataProvider(): DataProvider { } requiredDuchies += source.requiredExternalDuchyIdsList.map { DuchyKey(it).toName() } dataAvailabilityInterval = source.details.dataAvailabilityInterval + capabilities = source.details.capabilities.toCapabilities() + } +} + +private fun InternalDataProvider.Capabilities.toCapabilities(): DataProvider.Capabilities { + val source = this + return DataProviderKt.capabilities { + honestMajorityShareShuffleSupported = source.honestMajorityShareShuffleSupported + } +} + +private fun DataProvider.Capabilities.toInternal(): InternalDataProvider.Capabilities { + val source = this + return InternalDataProviderKt.capabilities { + honestMajorityShareShuffleSupported = source.honestMajorityShareShuffleSupported } } diff --git a/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/MeasurementsService.kt b/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/MeasurementsService.kt index 951994992c3..0364f9ff1f6 100644 --- a/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/MeasurementsService.kt +++ b/src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/MeasurementsService.kt @@ -63,23 +63,34 @@ import org.wfanet.measurement.common.base64UrlEncode import org.wfanet.measurement.common.grpc.failGrpc import org.wfanet.measurement.common.grpc.grpcRequire 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.internal.kingdom.CreateMeasurementRequest as InternalCreateMeasurementRequest +import org.wfanet.measurement.internal.kingdom.DataProvider as InternalDataProvider +import org.wfanet.measurement.internal.kingdom.DataProvidersGrpcKt.DataProvidersCoroutineStub as InternalDataProvidersCoroutineStub import org.wfanet.measurement.internal.kingdom.Measurement as InternalMeasurement import org.wfanet.measurement.internal.kingdom.Measurement.DataProviderValue import org.wfanet.measurement.internal.kingdom.Measurement.View as InternalMeasurementView import org.wfanet.measurement.internal.kingdom.MeasurementKt.dataProviderValue -import org.wfanet.measurement.internal.kingdom.MeasurementsGrpcKt.MeasurementsCoroutineStub +import org.wfanet.measurement.internal.kingdom.MeasurementsGrpcKt.MeasurementsCoroutineStub as InternalMeasurementsCoroutineStub +import org.wfanet.measurement.internal.kingdom.ProtocolConfig as InternalProtocolConfig +import org.wfanet.measurement.internal.kingdom.ProtocolConfigKt import org.wfanet.measurement.internal.kingdom.StreamMeasurementsRequest import org.wfanet.measurement.internal.kingdom.StreamMeasurementsRequestKt import org.wfanet.measurement.internal.kingdom.StreamMeasurementsRequestKt.filter import org.wfanet.measurement.internal.kingdom.batchCreateMeasurementsRequest +import org.wfanet.measurement.internal.kingdom.batchGetDataProvidersRequest import org.wfanet.measurement.internal.kingdom.batchGetMeasurementsRequest import org.wfanet.measurement.internal.kingdom.cancelMeasurementRequest import org.wfanet.measurement.internal.kingdom.createMeasurementRequest as internalCreateMeasurementRequest import org.wfanet.measurement.internal.kingdom.getMeasurementRequest import org.wfanet.measurement.internal.kingdom.measurementKey +import org.wfanet.measurement.internal.kingdom.protocolConfig import org.wfanet.measurement.internal.kingdom.streamMeasurementsRequest +import org.wfanet.measurement.kingdom.deploy.common.HmssProtocolConfig +import org.wfanet.measurement.kingdom.deploy.common.Llv2ProtocolConfig +import org.wfanet.measurement.kingdom.deploy.common.RoLlv2ProtocolConfig private const val DEFAULT_PAGE_SIZE = 50 private const val MAX_PAGE_SIZE = 1000 @@ -88,9 +99,16 @@ private const val MAX_BATCH_SIZE = 50 private const val MISSING_RESOURCE_NAME_ERROR = "Resource name is either unspecified or invalid" class MeasurementsService( - private val internalMeasurementsStub: MeasurementsCoroutineStub, + private val internalMeasurementsStub: InternalMeasurementsCoroutineStub, + private val internalDataProvidersStub: InternalDataProvidersCoroutineStub, private val noiseMechanisms: List, private val reachOnlyLlV2Enabled: Boolean, + /** + * Whether Honest Majority Share Shuffle (HMSS) is enabled. + * + * TODO(@renjiezh): Set this based on feature flag. + */ + private val hmssEnabled: Boolean = false, ) : MeasurementsCoroutineImplBase() { override suspend fun getMeasurement(request: GetMeasurementRequest): Measurement { @@ -124,20 +142,53 @@ class MeasurementsService( } override suspend fun createMeasurement(request: CreateMeasurementRequest): Measurement { - val authenticatedMeasurementConsumerKey = getAuthenticatedMeasurementConsumerKey() - + val authenticatedPrincipal: MeasurementPrincipal = principalFromCurrentContext val parentKey = grpcRequireNotNull(MeasurementConsumerKey.fromName(request.parent)) { "parent is either unspecified or invalid" } - - if (parentKey != authenticatedMeasurementConsumerKey) { + if (parentKey != authenticatedPrincipal.resourceKey) { failGrpc(Status.PERMISSION_DENIED) { "Cannot create a Measurement for another MeasurementConsumer" } } - val internalRequest = request.buildInternalCreateMeasurementRequest(parentKey) + grpcRequire(request.measurement.dataProvidersList.isNotEmpty()) { + "measurement.data_providers is empty" + } + + val externalDataProviderIds: List = + request.measurement.dataProvidersList.map { + val key = + grpcRequireNotNull(DataProviderKey.fromName(it.key)) { + "DataProvider resource name unspecified or invalid" + } + ApiId(key.dataProviderId).externalId + } + val dataProviderCapabilities: List = + try { + internalDataProvidersStub.batchGetDataProviders( + batchGetDataProvidersRequest { + this.externalDataProviderIds += externalDataProviderIds.map { it.value } + } + ) + } catch (e: StatusException) { + throw when (e.status.code) { + Status.Code.NOT_FOUND -> Status.NOT_FOUND.withDescription("DataProvider not found") + Status.Code.DEADLINE_EXCEEDED -> Status.DEADLINE_EXCEEDED + Status.Code.INTERNAL -> Status.INTERNAL + else -> Status.UNKNOWN + } + .withCause(e) + .asRuntimeException() + } + .dataProvidersList + .map { it.details.capabilities } + + // TODO(@SanjayVas): Check required capabilities once we have any. + + val internalRequest = + request.buildInternalCreateMeasurementRequest(dataProviderCapabilities, parentKey) val internalMeasurement = try { @@ -258,6 +309,41 @@ class MeasurementsService( failGrpc { "Number of elements in requests exceeds the maximum batch size." } } + val allExternalDataProviderIds: List = + request.requestsList + .flatMap { it.measurement.dataProvidersList } + .map { it.key } + .distinct() + .map { dataProviderName -> + val key = + grpcRequireNotNull(DataProviderKey.fromName(dataProviderName)) { + "DataProvider resource name unspecified or invalid" + } + ApiId(key.dataProviderId).externalId + } + val allDataProviderCapabilities: Map = + try { + internalDataProvidersStub.batchGetDataProviders( + batchGetDataProvidersRequest { + this.externalDataProviderIds += allExternalDataProviderIds.map { it.value } + } + ) + } catch (e: StatusException) { + throw when (e.status.code) { + Status.Code.NOT_FOUND -> Status.NOT_FOUND.withDescription("DataProvider not found") + Status.Code.DEADLINE_EXCEEDED -> Status.DEADLINE_EXCEEDED + Status.Code.INTERNAL -> Status.INTERNAL + else -> Status.UNKNOWN + } + .withCause(e) + .asRuntimeException() + } + .dataProvidersList + .associateBy { ExternalId(it.externalDataProviderId) } + .mapValues { it.value.details.capabilities } + + // TODO(@SanjayVas): Check required capabilities once we have any. + val internalCreateMeasurementRequests = mutableListOf() var isParentEmpty = false var isParentNotEmpty = false @@ -289,8 +375,15 @@ class MeasurementsService( } } + val externalDataProviderIds: Set = + createMeasurementRequest.measurement.dataProvidersList + .map { ApiId(DataProviderKey.fromName(it.key)!!.dataProviderId).externalId } + .toSet() val internalCreateMeasurementRequest = - createMeasurementRequest.buildInternalCreateMeasurementRequest(parentKey) + createMeasurementRequest.buildInternalCreateMeasurementRequest( + allDataProviderCapabilities.filterKeys { it in externalDataProviderIds }.values, + parentKey, + ) internalCreateMeasurementRequests.add(internalCreateMeasurementRequest) } @@ -381,8 +474,124 @@ class MeasurementsService( } } + private fun buildInternalProtocolConfig( + measurementSpec: MeasurementSpec, + dataProviderCapabilities: Collection, + ): InternalProtocolConfig { + val dataProvidersCount = dataProviderCapabilities.size + val internalNoiseMechanisms = noiseMechanisms.map { it.toInternal() } + @Suppress("WHEN_ENUM_CAN_BE_NULL_IN_JAVA") // Proto enum fields are never null. + return when (measurementSpec.measurementTypeCase) { + MeasurementSpec.MeasurementTypeCase.REACH -> { + if (dataProvidersCount == 1) { + protocolConfig { + direct = + ProtocolConfigKt.direct { + this.noiseMechanisms += internalNoiseMechanisms + customDirectMethodology = + InternalProtocolConfig.Direct.CustomDirectMethodology.getDefaultInstance() + deterministicCountDistinct = + InternalProtocolConfig.Direct.DeterministicCountDistinct.getDefaultInstance() + liquidLegionsCountDistinct = + InternalProtocolConfig.Direct.LiquidLegionsCountDistinct.getDefaultInstance() + } + } + } else { + if ( + hmssEnabled && dataProviderCapabilities.all { it.honestMajorityShareShuffleSupported } + ) { + protocolConfig { + externalProtocolConfigId = HmssProtocolConfig.name + honestMajorityShareShuffle = HmssProtocolConfig.protocolConfig + } + } else if (reachOnlyLlV2Enabled) { + protocolConfig { + externalProtocolConfigId = RoLlv2ProtocolConfig.name + reachOnlyLiquidLegionsV2 = RoLlv2ProtocolConfig.protocolConfig + } + } else { + protocolConfig { + externalProtocolConfigId = Llv2ProtocolConfig.name + liquidLegionsV2 = Llv2ProtocolConfig.protocolConfig + } + } + } + } + MeasurementSpec.MeasurementTypeCase.REACH_AND_FREQUENCY -> { + if (dataProvidersCount == 1) { + protocolConfig { + direct = + ProtocolConfigKt.direct { + this.noiseMechanisms += internalNoiseMechanisms + customDirectMethodology = + InternalProtocolConfig.Direct.CustomDirectMethodology.getDefaultInstance() + deterministicCountDistinct = + InternalProtocolConfig.Direct.DeterministicCountDistinct.getDefaultInstance() + liquidLegionsCountDistinct = + InternalProtocolConfig.Direct.LiquidLegionsCountDistinct.getDefaultInstance() + deterministicDistribution = + InternalProtocolConfig.Direct.DeterministicDistribution.getDefaultInstance() + liquidLegionsDistribution = + InternalProtocolConfig.Direct.LiquidLegionsDistribution.getDefaultInstance() + } + } + } else { + if ( + hmssEnabled && dataProviderCapabilities.all { it.honestMajorityShareShuffleSupported } + ) { + protocolConfig { + externalProtocolConfigId = HmssProtocolConfig.name + honestMajorityShareShuffle = HmssProtocolConfig.protocolConfig + } + } else { + protocolConfig { + externalProtocolConfigId = Llv2ProtocolConfig.name + liquidLegionsV2 = Llv2ProtocolConfig.protocolConfig + } + } + } + } + MeasurementSpec.MeasurementTypeCase.IMPRESSION -> { + protocolConfig { + direct = + ProtocolConfigKt.direct { + this.noiseMechanisms += internalNoiseMechanisms + customDirectMethodology = + InternalProtocolConfig.Direct.CustomDirectMethodology.getDefaultInstance() + deterministicCount = + InternalProtocolConfig.Direct.DeterministicCount.getDefaultInstance() + } + } + } + MeasurementSpec.MeasurementTypeCase.DURATION -> { + protocolConfig { + direct = + ProtocolConfigKt.direct { + this.noiseMechanisms += internalNoiseMechanisms + customDirectMethodology = + InternalProtocolConfig.Direct.CustomDirectMethodology.getDefaultInstance() + deterministicSum = InternalProtocolConfig.Direct.DeterministicSum.getDefaultInstance() + } + } + } + MeasurementSpec.MeasurementTypeCase.POPULATION -> { + protocolConfig { + direct = + ProtocolConfigKt.direct { + this.noiseMechanisms += internalNoiseMechanisms + deterministicCount = + InternalProtocolConfig.Direct.DeterministicCount.getDefaultInstance() + } + } + } + MeasurementSpec.MeasurementTypeCase.MEASUREMENTTYPE_NOT_SET -> + error("MeasurementType not set.") + } + } + private fun CreateMeasurementRequest.buildInternalCreateMeasurementRequest( - parentKey: MeasurementConsumerKey + dataProviderCapabilities: Collection, + parentKey: MeasurementConsumerKey, ): InternalCreateMeasurementRequest { val measurementConsumerCertificateKey = grpcRequireNotNull( @@ -409,13 +618,14 @@ class MeasurementsService( measurementSpec.validate() grpcRequire(measurement.dataProvidersList.isNotEmpty()) { "Data Providers list is empty" } - val dataProvidersMap = mutableMapOf() - measurement.dataProvidersList.forEach { - with(it.validateAndMap()) { - grpcRequire(!dataProvidersMap.containsKey(key)) { - "Duplicated keys found in the data_providers." + val dataProviderValues: Map = buildMap { + for (dataProviderEntry in measurement.dataProvidersList) { + val mapEntry: Map.Entry = + dataProviderEntry.toValidatedInternalMapEntry() + grpcRequire(!containsKey(mapEntry.key)) { + "Duplicated keys found in measurement.data_providers." } - dataProvidersMap[key] = value + put(mapEntry.key, mapEntry.value) } } @@ -426,10 +636,8 @@ class MeasurementsService( val internalMeasurement = measurement.toInternal( measurementConsumerCertificateKey, - dataProvidersMap, - measurementSpec, - noiseMechanisms.map { it.toInternal() }, - reachOnlyLlV2Enabled, + dataProviderValues, + buildInternalProtocolConfig(measurementSpec, dataProviderCapabilities), ) val requestId = this.requestId @@ -510,11 +718,9 @@ private fun MeasurementSpec.validate() { } /** Validates a [DataProviderEntry] for a request and then creates a map entry from it. */ -private fun DataProviderEntry.validateAndMap(): Map.Entry { - val dataProviderKey = - grpcRequireNotNull(DataProviderKey.fromName(key)) { - "Data Provider resource name is either unspecified or invalid" - } +private fun DataProviderEntry.toValidatedInternalMapEntry(): + Map.Entry { + val dataProviderKey = checkNotNull(DataProviderKey.fromName(key)) val dataProviderCertificateKey = grpcRequireNotNull(DataProviderCertificateKey.fromName(value.dataProviderCertificate)) { @@ -546,7 +752,7 @@ private fun DataProviderEntry.validateAndMap(): Map.Entry.toDataProviderEntry(apiVersion: Version): * Converts a public [Measurement] to an internal [InternalMeasurement] for creation. * * @throws [IllegalStateException] if MeasurementType not specified - * - * TODO(@renjie): Enable HMSS protocol based on feature flag. */ fun Measurement.toInternal( measurementConsumerCertificateKey: MeasurementConsumerCertificateKey, - dataProvidersMap: Map, - measurementSpecProto: MeasurementSpec, - internalNoiseMechanisms: List, - reachOnlyLlV2Enabled: Boolean, + dataProviderValues: Map, + internalProtocolConfig: InternalProtocolConfig, ): InternalMeasurement { val source = this return internalMeasurement { @@ -1006,108 +999,29 @@ fun Measurement.toInternal( apiIdToExternalId(measurementConsumerCertificateKey.measurementConsumerId) externalMeasurementConsumerCertificateId = apiIdToExternalId(measurementConsumerCertificateKey.certificateId) - dataProviders.putAll(dataProvidersMap) + dataProviders.putAll(dataProviderValues.mapKeys { it.key.value }) details = details { apiVersion = Version.V2_ALPHA.string measurementSpec = source.measurementSpec.message.value measurementSpecSignature = source.measurementSpec.signature measurementSpecSignatureAlgorithmOid = source.measurementSpec.signatureAlgorithmOid + protocolConfig = internalProtocolConfig @Suppress("WHEN_ENUM_CAN_BE_NULL_IN_JAVA") // Proto enum fields are never null. - when (measurementSpecProto.measurementTypeCase) { - MeasurementSpec.MeasurementTypeCase.REACH -> { - if (dataProvidersCount > 1) { - if (reachOnlyLlV2Enabled) { - protocolConfig = internalProtocolConfig { - externalProtocolConfigId = RoLlv2ProtocolConfig.name - reachOnlyLiquidLegionsV2 = RoLlv2ProtocolConfig.protocolConfig - } - duchyProtocolConfig = duchyProtocolConfig { - reachOnlyLiquidLegionsV2 = RoLlv2ProtocolConfig.duchyProtocolConfig - } - } else { - protocolConfig = internalProtocolConfig { - externalProtocolConfigId = Llv2ProtocolConfig.name - liquidLegionsV2 = Llv2ProtocolConfig.protocolConfig - } - duchyProtocolConfig = duchyProtocolConfig { - liquidLegionsV2 = Llv2ProtocolConfig.duchyProtocolConfig - } - } - } else if (dataProvidersCount == 1) { - protocolConfig = internalProtocolConfig { - direct = - InternalProtocolConfigKt.direct { - noiseMechanisms += internalNoiseMechanisms - customDirectMethodology = - InternalProtocolConfig.Direct.CustomDirectMethodology.getDefaultInstance() - deterministicCountDistinct = - InternalProtocolConfig.Direct.DeterministicCountDistinct.getDefaultInstance() - liquidLegionsCountDistinct = - InternalProtocolConfig.Direct.LiquidLegionsCountDistinct.getDefaultInstance() - } - } - } - } - MeasurementSpec.MeasurementTypeCase.REACH_AND_FREQUENCY -> { - if (dataProvidersCount > 1) { - protocolConfig = internalProtocolConfig { - externalProtocolConfigId = Llv2ProtocolConfig.name - liquidLegionsV2 = Llv2ProtocolConfig.protocolConfig - } - duchyProtocolConfig = duchyProtocolConfig { - liquidLegionsV2 = Llv2ProtocolConfig.duchyProtocolConfig - } - } else if (dataProvidersCount == 1) { - protocolConfig = internalProtocolConfig { - direct = - InternalProtocolConfigKt.direct { - noiseMechanisms += internalNoiseMechanisms - customDirectMethodology = - InternalProtocolConfig.Direct.CustomDirectMethodology.getDefaultInstance() - deterministicCountDistinct = - InternalProtocolConfig.Direct.DeterministicCountDistinct.getDefaultInstance() - liquidLegionsCountDistinct = - InternalProtocolConfig.Direct.LiquidLegionsCountDistinct.getDefaultInstance() - deterministicDistribution = - InternalProtocolConfig.Direct.DeterministicDistribution.getDefaultInstance() - liquidLegionsDistribution = - InternalProtocolConfig.Direct.LiquidLegionsDistribution.getDefaultInstance() - } - } - } - } - MeasurementSpec.MeasurementTypeCase.IMPRESSION -> { - protocolConfig = internalProtocolConfig { - direct = - InternalProtocolConfigKt.direct { - noiseMechanisms += internalNoiseMechanisms - customDirectMethodology = - InternalProtocolConfig.Direct.CustomDirectMethodology.getDefaultInstance() - deterministicCount = - InternalProtocolConfig.Direct.DeterministicCount.getDefaultInstance() - } - } - } - MeasurementSpec.MeasurementTypeCase.DURATION -> { - protocolConfig = internalProtocolConfig { - direct = - InternalProtocolConfigKt.direct { - noiseMechanisms += internalNoiseMechanisms - customDirectMethodology = - InternalProtocolConfig.Direct.CustomDirectMethodology.getDefaultInstance() - deterministicSum = - InternalProtocolConfig.Direct.DeterministicSum.getDefaultInstance() - } + when (protocolConfig.protocolCase) { + InternalProtocolConfig.ProtocolCase.LIQUID_LEGIONS_V2 -> { + duchyProtocolConfig = duchyProtocolConfig { + liquidLegionsV2 = Llv2ProtocolConfig.duchyProtocolConfig } } - MeasurementSpec.MeasurementTypeCase.POPULATION -> { - protocolConfig = internalProtocolConfig { - direct = InternalProtocolConfig.Direct.getDefaultInstance() + InternalProtocolConfig.ProtocolCase.REACH_ONLY_LIQUID_LEGIONS_V2 -> { + duchyProtocolConfig = duchyProtocolConfig { + reachOnlyLiquidLegionsV2 = RoLlv2ProtocolConfig.duchyProtocolConfig } } - MeasurementSpec.MeasurementTypeCase.MEASUREMENTTYPE_NOT_SET -> - error("MeasurementType not set.") + InternalProtocolConfig.ProtocolCase.HONEST_MAJORITY_SHARE_SHUFFLE, + InternalProtocolConfig.ProtocolCase.DIRECT -> {} + InternalProtocolConfig.ProtocolCase.PROTOCOL_NOT_SET -> error("protocol not set") } } } diff --git a/src/main/kotlin/org/wfanet/measurement/kingdom/service/internal/testing/DataProvidersServiceTest.kt b/src/main/kotlin/org/wfanet/measurement/kingdom/service/internal/testing/DataProvidersServiceTest.kt index e35bfc78db1..e7bab231cbe 100644 --- a/src/main/kotlin/org/wfanet/measurement/kingdom/service/internal/testing/DataProvidersServiceTest.kt +++ b/src/main/kotlin/org/wfanet/measurement/kingdom/service/internal/testing/DataProvidersServiceTest.kt @@ -44,6 +44,7 @@ import org.wfanet.measurement.internal.kingdom.copy import org.wfanet.measurement.internal.kingdom.dataProvider import org.wfanet.measurement.internal.kingdom.getDataProviderRequest import org.wfanet.measurement.internal.kingdom.replaceDataAvailabilityIntervalRequest +import org.wfanet.measurement.internal.kingdom.replaceDataProviderCapabilitiesRequest import org.wfanet.measurement.internal.kingdom.replaceDataProviderRequiredDuchiesRequest import org.wfanet.measurement.kingdom.deploy.common.testing.DuchyIdSetter import org.wfanet.measurement.kingdom.service.internal.testing.Population.Companion.DUCHIES @@ -93,7 +94,7 @@ abstract class DataProvidersServiceTest { } @Test - fun `createDataProvider succeeds`() = runBlocking { + fun `createDataProvider returns created DataProvider`() = runBlocking { val request = CREATE_DATA_PROVIDER_REQUEST val response: DataProvider = dataProvidersService.createDataProvider(request) @@ -113,7 +114,7 @@ abstract class DataProvidersServiceTest { fun `createDataProvider succeeds when requiredExternalDuchyIds is empty`() = runBlocking { val request = CREATE_DATA_PROVIDER_REQUEST.copy { requiredExternalDuchyIds.clear() } - val response = dataProvidersService.createDataProvider(request) + val response: DataProvider = dataProvidersService.createDataProvider(request) assertThat(response) .ignoringRepeatedFieldOrderOfFieldDescriptors(UNORDERED_FIELD_DESCRIPTORS) @@ -308,6 +309,31 @@ abstract class DataProvidersServiceTest { assertThat(exception.status.code).isEqualTo(Status.Code.INVALID_ARGUMENT) } + @Test + fun `replaceDataProviderCapabilites updates DataProvider`() = runBlocking { + val dataProvider: DataProvider = + dataProvidersService.createDataProvider(CREATE_DATA_PROVIDER_REQUEST) + val capabilities = DataProviderKt.capabilities { honestMajorityShareShuffleSupported = true } + + val response: DataProvider = + dataProvidersService.replaceDataProviderCapabilities( + replaceDataProviderCapabilitiesRequest { + externalDataProviderId = dataProvider.externalDataProviderId + this.capabilities = capabilities + } + ) + + assertThat(response.details.capabilities).isEqualTo(capabilities) + // Ensure changes were persisted. + assertThat( + dataProvidersService.getDataProvider( + getDataProviderRequest { externalDataProviderId = dataProvider.externalDataProviderId } + ) + ) + .ignoringRepeatedFieldOrderOfFieldDescriptors(UNORDERED_FIELD_DESCRIPTORS) + .isEqualTo(response) + } + /** Random [IdGenerator] which records generated IDs. */ private class RecordingIdGenerator : IdGenerator { private val delegate = RandomIdGenerator() diff --git a/src/main/proto/wfa/measurement/internal/kingdom/data_provider.proto b/src/main/proto/wfa/measurement/internal/kingdom/data_provider.proto index 73e2a57cea0..60a2728f98f 100644 --- a/src/main/proto/wfa/measurement/internal/kingdom/data_provider.proto +++ b/src/main/proto/wfa/measurement/internal/kingdom/data_provider.proto @@ -30,6 +30,10 @@ message DataProvider { // verification. Certificate certificate = 2; + message Capabilities { + // Whether the Honest Majority Share Shuffle (HMSS) protocol is supported. + bool honest_majority_share_shuffle_supported = 1; + } message Details { // Version the public API for serialized message definitions. string api_version = 1; @@ -40,6 +44,7 @@ message DataProvider { string public_key_signature_algorithm_oid = 4; google.type.Interval data_availability_interval = 5; + Capabilities capabilities = 6; } Details details = 3; diff --git a/src/main/proto/wfa/measurement/internal/kingdom/data_providers_service.proto b/src/main/proto/wfa/measurement/internal/kingdom/data_providers_service.proto index f7db11a58fb..30f0a9e732a 100644 --- a/src/main/proto/wfa/measurement/internal/kingdom/data_providers_service.proto +++ b/src/main/proto/wfa/measurement/internal/kingdom/data_providers_service.proto @@ -36,6 +36,9 @@ service DataProviders { rpc ReplaceDataAvailabilityInterval(ReplaceDataAvailabilityIntervalRequest) returns (DataProvider); + + rpc ReplaceDataProviderCapabilities(ReplaceDataProviderCapabilitiesRequest) + returns (DataProvider); } message ReplaceDataProviderRequiredDuchiesRequest { @@ -59,3 +62,10 @@ message ReplaceDataAvailabilityIntervalRequest { fixed64 external_data_provider_id = 1; google.type.Interval data_availability_interval = 2; } + +message ReplaceDataProviderCapabilitiesRequest { + fixed64 external_data_provider_id = 1; + + // New value for `capabilities`. + DataProvider.Capabilities capabilities = 2; +} diff --git a/src/test/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/DataProvidersServiceTest.kt b/src/test/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/DataProvidersServiceTest.kt index c9a26e22e1e..9ce61387513 100644 --- a/src/test/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/DataProvidersServiceTest.kt +++ b/src/test/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/DataProvidersServiceTest.kt @@ -31,13 +31,17 @@ import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 import org.mockito.kotlin.any +import org.mockito.kotlin.stub import org.mockito.kotlin.whenever import org.wfanet.measurement.api.Version +import org.wfanet.measurement.api.v2alpha.DataProvider +import org.wfanet.measurement.api.v2alpha.DataProviderKt import org.wfanet.measurement.api.v2alpha.DuchyKey import org.wfanet.measurement.api.v2alpha.copy import org.wfanet.measurement.api.v2alpha.dataProvider import org.wfanet.measurement.api.v2alpha.getDataProviderRequest import org.wfanet.measurement.api.v2alpha.replaceDataAvailabilityIntervalRequest +import org.wfanet.measurement.api.v2alpha.replaceDataProviderCapabilitiesRequest import org.wfanet.measurement.api.v2alpha.replaceDataProviderRequiredDuchiesRequest import org.wfanet.measurement.api.v2alpha.setMessage import org.wfanet.measurement.api.v2alpha.signedMessage @@ -58,7 +62,7 @@ import org.wfanet.measurement.common.toProtoTime import org.wfanet.measurement.consent.client.common.toEncryptionPublicKey import org.wfanet.measurement.internal.kingdom.CertificateKt import org.wfanet.measurement.internal.kingdom.DataProvider as InternalDataProvider -import org.wfanet.measurement.internal.kingdom.DataProviderKt.details +import org.wfanet.measurement.internal.kingdom.DataProviderKt as InternalDataProviderKt import org.wfanet.measurement.internal.kingdom.DataProvidersGrpcKt.DataProvidersCoroutineImplBase as InternalDataProvidersService import org.wfanet.measurement.internal.kingdom.DataProvidersGrpcKt.DataProvidersCoroutineStub as InternalDataProvidersClient import org.wfanet.measurement.internal.kingdom.certificate as internalCertificate @@ -66,6 +70,7 @@ import org.wfanet.measurement.internal.kingdom.copy import org.wfanet.measurement.internal.kingdom.dataProvider as internalDataProvider import org.wfanet.measurement.internal.kingdom.getDataProviderRequest as internalGetDataProviderRequest import org.wfanet.measurement.internal.kingdom.replaceDataAvailabilityIntervalRequest as internalReplaceDataAvailabilityIntervalRequest +import org.wfanet.measurement.internal.kingdom.replaceDataProviderCapabilitiesRequest as internalReplaceDataProviderCapabilitiesRequest import org.wfanet.measurement.internal.kingdom.replaceDataProviderRequiredDuchiesRequest as internalReplaceDataProviderRequiredDuchiesRequest private const val DATA_PROVIDER_ID = 123L @@ -527,6 +532,61 @@ class DataProvidersServiceTest { assertThat(exception.status.code).isEqualTo(Status.Code.PERMISSION_DENIED) } + @Test + fun `replaceDataProviderCapabilities returns updated DataProvider`() { + val internalDataProvider = + INTERNAL_DATA_PROVIDER.copy { + details = + details.copy { + capabilities = capabilities.copy { honestMajorityShareShuffleSupported = true } + } + } + internalServiceMock.stub { + onBlocking { replaceDataProviderCapabilities(any()) }.thenReturn(internalDataProvider) + } + val request = replaceDataProviderCapabilitiesRequest { + name = DATA_PROVIDER_NAME + capabilities = DataProviderKt.capabilities { honestMajorityShareShuffleSupported = true } + } + + val response: DataProvider = runBlocking { + withDataProviderPrincipal(DATA_PROVIDER_NAME) { + service.replaceDataProviderCapabilities(request) + } + } + + assertThat(response).isEqualTo(DATA_PROVIDER.copy { capabilities = request.capabilities }) + verifyProtoArgument( + internalServiceMock, + InternalDataProvidersService::replaceDataProviderCapabilities, + ) + .isEqualTo( + internalReplaceDataProviderCapabilitiesRequest { + externalDataProviderId = internalDataProvider.externalDataProviderId + capabilities = internalDataProvider.details.capabilities + } + ) + } + + @Test + fun `replaceDataProviderCapabilities throws PERMISSION_DENIED for incorrect principal`() { + val request = replaceDataProviderCapabilitiesRequest { + name = DATA_PROVIDER_NAME + capabilities = DataProviderKt.capabilities { honestMajorityShareShuffleSupported = true } + } + + val exception = + assertFailsWith { + runBlocking { + withDataProviderPrincipal(DATA_PROVIDER_NAME_2) { + service.replaceDataProviderCapabilities(request) + } + } + } + + assertThat(exception.status.code).isEqualTo(Status.Code.PERMISSION_DENIED) + } + companion object { private val API_VERSION = Version.V2_ALPHA @@ -544,16 +604,17 @@ class DataProvidersServiceTest { private val INTERNAL_DATA_PROVIDER: InternalDataProvider = internalDataProvider { externalDataProviderId = DATA_PROVIDER_ID - details = details { - apiVersion = API_VERSION.string - publicKey = SIGNED_PUBLIC_KEY.message.value - publicKeySignature = SIGNED_PUBLIC_KEY.signature - publicKeySignatureAlgorithmOid = SIGNED_PUBLIC_KEY.signatureAlgorithmOid - dataAvailabilityInterval = interval { - startTime = timestamp { seconds = 100 } - endTime = timestamp { seconds = 200 } + details = + InternalDataProviderKt.details { + apiVersion = API_VERSION.string + publicKey = SIGNED_PUBLIC_KEY.message.value + publicKeySignature = SIGNED_PUBLIC_KEY.signature + publicKeySignatureAlgorithmOid = SIGNED_PUBLIC_KEY.signatureAlgorithmOid + dataAvailabilityInterval = interval { + startTime = timestamp { seconds = 100 } + endTime = timestamp { seconds = 200 } + } } - } certificate = internalCertificate { externalDataProviderId = DATA_PROVIDER_ID externalCertificateId = CERTIFICATE_ID @@ -572,6 +633,7 @@ class DataProvidersServiceTest { publicKey = SIGNED_PUBLIC_KEY requiredDuchies += DUCHY_NAMES dataAvailabilityInterval = INTERNAL_DATA_PROVIDER.details.dataAvailabilityInterval + capabilities = DataProvider.Capabilities.getDefaultInstance() } } } diff --git a/src/test/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/MeasurementsServiceTest.kt b/src/test/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/MeasurementsServiceTest.kt index 0cd23e7b1a2..a483f6f2af9 100644 --- a/src/test/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/MeasurementsServiceTest.kt +++ b/src/test/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha/MeasurementsServiceTest.kt @@ -17,6 +17,7 @@ package org.wfanet.measurement.kingdom.service.api.v2alpha import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage import com.google.common.truth.extensions.proto.ProtoTruth.assertThat import com.google.protobuf.ByteString import com.google.protobuf.Timestamp @@ -102,6 +103,10 @@ import org.wfanet.measurement.common.testing.captureFirst import org.wfanet.measurement.common.testing.verifyProtoArgument import org.wfanet.measurement.common.toByteString import org.wfanet.measurement.common.toProtoTime +import org.wfanet.measurement.internal.kingdom.BatchGetDataProvidersRequest +import org.wfanet.measurement.internal.kingdom.DataProvider as InternalDataProvider +import org.wfanet.measurement.internal.kingdom.DataProviderKt as InternalDataProviderKt +import org.wfanet.measurement.internal.kingdom.DataProvidersGrpcKt import org.wfanet.measurement.internal.kingdom.DuchyProtocolConfig import org.wfanet.measurement.internal.kingdom.Measurement as InternalMeasurement import org.wfanet.measurement.internal.kingdom.Measurement.State as InternalState @@ -116,11 +121,13 @@ import org.wfanet.measurement.internal.kingdom.StreamMeasurementsRequest import org.wfanet.measurement.internal.kingdom.StreamMeasurementsRequestKt import org.wfanet.measurement.internal.kingdom.batchCreateMeasurementsRequest as internalBatchCreateMeasurementsRequest import org.wfanet.measurement.internal.kingdom.batchCreateMeasurementsResponse as internalBatchCreateMeasurementsResponse +import org.wfanet.measurement.internal.kingdom.batchGetDataProvidersResponse as internalBatchGetDataProvidersResponse import org.wfanet.measurement.internal.kingdom.batchGetMeasurementsRequest as internalBatchGetMeasurementsRequest import org.wfanet.measurement.internal.kingdom.batchGetMeasurementsResponse as internalBatchGetMeasurementsResponse import org.wfanet.measurement.internal.kingdom.cancelMeasurementRequest as internalCancelMeasurementRequest import org.wfanet.measurement.internal.kingdom.copy import org.wfanet.measurement.internal.kingdom.createMeasurementRequest as internalCreateMeasurementRequest +import org.wfanet.measurement.internal.kingdom.dataProvider as internalDataProvider import org.wfanet.measurement.internal.kingdom.differentialPrivacyParams as internalDifferentialPrivacyParams import org.wfanet.measurement.internal.kingdom.duchyProtocolConfig import org.wfanet.measurement.internal.kingdom.getMeasurementRequest as internalGetMeasurementRequest @@ -129,12 +136,11 @@ import org.wfanet.measurement.internal.kingdom.measurement as internalMeasuremen import org.wfanet.measurement.internal.kingdom.measurementKey import org.wfanet.measurement.internal.kingdom.protocolConfig as internalProtocolConfig import org.wfanet.measurement.internal.kingdom.streamMeasurementsRequest +import org.wfanet.measurement.kingdom.deploy.common.HmssProtocolConfig import org.wfanet.measurement.kingdom.deploy.common.Llv2ProtocolConfig import org.wfanet.measurement.kingdom.deploy.common.RoLlv2ProtocolConfig private const val DEFAULT_LIMIT = 50 -private const val DATA_PROVIDERS_CERTIFICATE_NAME = - "dataProviders/AAAAAAAAAHs/certificates/AAAAAAAAAHs" private const val DATA_PROVIDERS_RESULT_CERTIFICATE_NAME = "dataProviders/AAAAAAAAALs/certificates/AAAAAAAAALs" private const val MEASUREMENT_CONSUMER_NAME = "measurementConsumers/AAAAAAAAAHs" @@ -198,8 +204,24 @@ class MeasurementsServiceTest { } ) } + private val internalDataProvidersMock: DataProvidersGrpcKt.DataProvidersCoroutineImplBase = + mockService { + onBlocking { batchGetDataProviders(any()) } + .thenAnswer { invocation -> + val request: BatchGetDataProvidersRequest = invocation.getArgument(0) + val internalDataProviders: List = + request.externalDataProviderIdsList.map { + internalDataProvider { externalDataProviderId = it } + } + internalBatchGetDataProvidersResponse { dataProviders.addAll(internalDataProviders) } + } + } - @get:Rule val grpcTestServerRule = GrpcTestServerRule { addService(internalMeasurementsMock) } + @get:Rule + val grpcTestServerRule = GrpcTestServerRule { + addService(internalMeasurementsMock) + addService(internalDataProvidersMock) + } private lateinit var service: MeasurementsService @@ -208,8 +230,10 @@ class MeasurementsServiceTest { service = MeasurementsService( MeasurementsGrpcKt.MeasurementsCoroutineStub(grpcTestServerRule.channel), + DataProvidersGrpcKt.DataProvidersCoroutineStub(grpcTestServerRule.channel), NOISE_MECHANISMS, reachOnlyLlV2Enabled = true, + hmssEnabled = true, ) } @@ -738,6 +762,66 @@ class MeasurementsServiceTest { ) } + @Test + fun `createMeasurement with HMSS enabled and EDPs capable specifies HMSS protocol`() { + internalDataProvidersMock.stub { + onBlocking { batchGetDataProviders(any()) } + .thenReturn( + internalBatchGetDataProvidersResponse { + for (externalDataProviderId in EXTERNAL_DATA_PROVIDER_IDS) { + dataProviders += internalDataProvider { + this.externalDataProviderId = externalDataProviderId.value + details = + details.copy { + capabilities = + InternalDataProviderKt.capabilities { + honestMajorityShareShuffleSupported = true + } + } + } + } + } + ) + } + val measurement = + MEASUREMENT.copy { + clearFailure() + results.clear() + clearProtocolConfig() + } + val request = createMeasurementRequest { + parent = MEASUREMENT_CONSUMER_NAME + this.measurement = measurement + requestId = "foo" + } + + withMeasurementConsumerPrincipal(MEASUREMENT_CONSUMER_NAME) { + runBlocking { service.createMeasurement(request) } + } + + verifyProtoArgument( + internalMeasurementsMock, + MeasurementsGrpcKt.MeasurementsCoroutineImplBase::createMeasurement, + ) + .isEqualTo( + internalCreateMeasurementRequest { + this.measurement = + INTERNAL_MEASUREMENT.copy { + clearExternalMeasurementId() + clearUpdateTime() + results.clear() + details = + details.copy { + clearFailure() + protocolConfig = HMSS_INTERNAL_PROTOCOL_CONFIG + clearDuchyProtocolConfig() + } + } + requestId = request.requestId + } + ) + } + @Test fun `createMeasurement throws INVALID_ARGUMENT when model line is missing for POPULATION measurement`() { val exception = @@ -809,11 +893,19 @@ class MeasurementsServiceTest { assertFailsWith { withDataProviderPrincipal(DATA_PROVIDERS_NAME) { runBlocking { - service.createMeasurement(createMeasurementRequest { measurement = MEASUREMENT }) + service.createMeasurement( + createMeasurementRequest { + parent = MEASUREMENT_CONSUMER_NAME + measurement = MEASUREMENT + } + ) } } } - assertThat(exception.status.code).isEqualTo(Status.Code.PERMISSION_DENIED) + + assertWithMessage(exception.toString()) + .that(exception.status.code) + .isEqualTo(Status.Code.PERMISSION_DENIED) } @Test @@ -824,7 +916,9 @@ class MeasurementsServiceTest { service.createMeasurement(createMeasurementRequest { measurement = MEASUREMENT }) } } - assertThat(exception.status.code).isEqualTo(Status.Code.UNAUTHENTICATED) + assertWithMessage(exception.toString()) + .that(exception.status.code) + .isEqualTo(Status.Code.UNAUTHENTICATED) } @Test @@ -1968,6 +2062,7 @@ class MeasurementsServiceTest { assertThat(exception.status.code).isEqualTo(Status.Code.INVALID_ARGUMENT) } + @Test fun `batchCreateMeasurements throws INVALID_ARGUMENT when child parent doesn't match`() { val createMeasurementRequest = createMeasurementRequest { parent = MEASUREMENT_CONSUMER_NAME_2 @@ -2336,6 +2431,10 @@ class MeasurementsServiceTest { setOf("aggregator"), 2, ) + HmssProtocolConfig.setForTest( + HMSS_INTERNAL_PROTOCOL_CONFIG.honestMajorityShareShuffle, + setOf("aggregator", "worker1", "worker2"), + ) } private val API_VERSION = Version.V2_ALPHA @@ -2431,6 +2530,11 @@ class MeasurementsServiceTest { reachOnlyLiquidLegionsV2 = DuchyProtocolConfig.LiquidLegionsV2.getDefaultInstance() } + private val HMSS_INTERNAL_PROTOCOL_CONFIG = internalProtocolConfig { + externalProtocolConfigId = "hmss" + honestMajorityShareShuffle = InternalProtocolConfigKt.honestMajorityShareShuffle {} + } + private val DATA_PROVIDER_PUBLIC_KEY = encryptionPublicKey { data = UPDATE_TIME.toByteString() } private val MEASUREMENT_PUBLIC_KEY = encryptionPublicKey { data = UPDATE_TIME.toByteString() } @@ -2652,7 +2756,10 @@ class MeasurementsServiceTest { } private val DEFAULT_INTERNAL_DIRECT_POPULATION_PROTOCOL_CONFIG: InternalProtocolConfig.Direct = - InternalProtocolConfig.Direct.getDefaultInstance() + direct { + noiseMechanisms += DEFAULT_INTERNAL_DIRECT_NOISE_MECHANISMS + deterministicCount = InternalProtocolConfig.Direct.DeterministicCount.getDefaultInstance() + } private const val BATCH_LIMIT = 50 }