From 34422519ffa0a5a14f9832d73d016fda54b7b078 Mon Sep 17 00:00:00 2001 From: Lahiru Maramba Date: Tue, 16 Sep 2025 13:09:53 -0400 Subject: [PATCH 01/10] chore: update copyright headers from Google Inc. to Google LLC (#2974) Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- .eslintrc.js | 2 +- .github/actions/send-email/action.yml | 2 +- .github/actions/send-email/index.js | 2 +- .github/actions/send-tweet/action.yml | 2 +- .github/actions/send-tweet/index.js | 2 +- .github/scripts/publish_package.sh | 2 +- .github/scripts/publish_preflight_check.sh | 2 +- .github/scripts/run_integration_tests.sh | 2 +- .github/scripts/verify_package.sh | 2 +- .github/workflows/nightly.yml | 2 +- .github/workflows/release.yml | 2 +- docgen/post-process.js | 2 +- generate-esm-wrapper.js | 2 +- generate-reports.js | 2 +- gulpfile.js | 2 +- src/app-check/app-check-api-client-internal.ts | 2 +- src/app-check/app-check-api.ts | 2 +- src/app-check/app-check-namespace.ts | 2 +- src/app-check/app-check.ts | 2 +- src/app-check/index.ts | 2 +- src/app-check/token-generator.ts | 2 +- src/app-check/token-verifier.ts | 2 +- src/app/core.ts | 2 +- src/app/credential-factory.ts | 2 +- src/app/credential-internal.ts | 2 +- src/app/credential.ts | 2 +- src/app/firebase-app.ts | 2 +- src/app/firebase-namespace.ts | 2 +- src/app/index.ts | 2 +- src/app/lifecycle.ts | 2 +- src/auth/action-code-settings-builder.ts | 2 +- src/auth/auth-api-request.ts | 2 +- src/auth/auth-config.ts | 2 +- src/auth/auth-namespace.ts | 2 +- src/auth/auth.ts | 2 +- src/auth/base-auth.ts | 2 +- src/auth/identifier.ts | 2 +- src/auth/index.ts | 2 +- src/auth/project-config-manager.ts | 2 +- src/auth/project-config.ts | 2 +- src/auth/tenant-manager.ts | 2 +- src/auth/tenant.ts | 2 +- src/auth/token-generator.ts | 2 +- src/auth/token-verifier.ts | 2 +- src/auth/user-import-builder.ts | 2 +- src/auth/user-record.ts | 2 +- src/credential/index.ts | 2 +- src/data-connect/data-connect-api-client-internal.ts | 2 +- src/data-connect/data-connect-api.ts | 2 +- src/data-connect/data-connect.ts | 2 +- src/data-connect/index.ts | 2 +- src/database/database-namespace.ts | 2 +- src/database/database.ts | 2 +- src/database/index.ts | 2 +- src/default-namespace.d.ts | 2 +- src/default-namespace.ts | 2 +- src/eventarc/cloudevent.ts | 2 +- src/eventarc/eventarc-client-internal.ts | 2 +- src/eventarc/eventarc-utils.ts | 2 +- src/eventarc/eventarc.ts | 2 +- src/eventarc/index.ts | 2 +- src/extensions/extensions-api-client-internal.ts | 2 +- src/extensions/extensions-api.ts | 2 +- src/extensions/extensions.ts | 2 +- src/extensions/index.ts | 2 +- src/firebase-namespace-api.ts | 2 +- src/firestore/firestore-internal.ts | 2 +- src/firestore/firestore-namespace.ts | 2 +- src/firestore/index.ts | 2 +- src/functions/functions-api-client-internal.ts | 2 +- src/functions/functions-api.ts | 2 +- src/functions/functions.ts | 2 +- src/functions/index.ts | 2 +- src/index.d.ts | 2 +- src/index.ts | 2 +- src/installations/index.ts | 2 +- src/installations/installations-namespace.ts | 2 +- src/installations/installations-request-handler.ts | 2 +- src/installations/installations.ts | 2 +- src/instance-id/index.ts | 2 +- src/instance-id/instance-id.ts | 2 +- src/machine-learning/index.ts | 2 +- src/machine-learning/machine-learning-api-client.ts | 2 +- src/machine-learning/machine-learning-namespace.ts | 2 +- src/machine-learning/machine-learning-utils.ts | 2 +- src/machine-learning/machine-learning.ts | 2 +- src/messaging/index.ts | 2 +- src/messaging/messaging-api-request-internal.ts | 2 +- src/messaging/messaging-api.ts | 2 +- src/messaging/messaging-errors-internal.ts | 2 +- src/messaging/messaging-internal.ts | 2 +- src/messaging/messaging-namespace.ts | 2 +- src/messaging/messaging.ts | 2 +- src/project-management/android-app.ts | 2 +- src/project-management/app-metadata.ts | 2 +- src/project-management/index.ts | 2 +- src/project-management/ios-app.ts | 2 +- .../project-management-api-request-internal.ts | 2 +- src/project-management/project-management-namespace.ts | 2 +- src/project-management/project-management.ts | 2 +- src/remote-config/condition-evaluator-internal.ts | 2 +- src/remote-config/index.ts | 2 +- src/remote-config/internal/value-impl.ts | 2 +- src/remote-config/remote-config-api-client-internal.ts | 2 +- src/remote-config/remote-config-api.ts | 2 +- src/remote-config/remote-config-namespace.ts | 2 +- src/remote-config/remote-config.ts | 2 +- src/security-rules/index.ts | 2 +- src/security-rules/security-rules-api-client-internal.ts | 2 +- src/security-rules/security-rules-internal.ts | 2 +- src/security-rules/security-rules-namespace.ts | 2 +- src/security-rules/security-rules.ts | 2 +- src/storage/index.ts | 2 +- src/storage/storage-namespace.ts | 2 +- src/storage/storage.ts | 2 +- src/utils/api-request.ts | 2 +- src/utils/crypto-signer.ts | 2 +- src/utils/deep-copy.ts | 2 +- src/utils/error.ts | 2 +- src/utils/index.ts | 2 +- src/utils/jwt.ts | 2 +- src/utils/validator.ts | 2 +- test/integration/app-check.spec.ts | 2 +- test/integration/app.spec.ts | 2 +- test/integration/auth.spec.ts | 2 +- test/integration/data-connect.spec.ts | 2 +- test/integration/database.spec.ts | 2 +- test/integration/firestore.spec.ts | 2 +- test/integration/functions.spec.ts | 2 +- test/integration/installations.spec.ts | 2 +- test/integration/instance-id.spec.ts | 2 +- test/integration/machine-learning.spec.ts | 2 +- test/integration/messaging.spec.ts | 2 +- test/integration/postcheck/esm/example.test.js | 2 +- test/integration/postcheck/typescript/example-modular.test.ts | 2 +- test/integration/postcheck/typescript/example.test.ts | 2 +- test/integration/postcheck/typescript/example.ts | 2 +- test/integration/project-management.spec.ts | 2 +- test/integration/remote-config.spec.ts | 2 +- test/integration/security-rules.spec.ts | 2 +- test/integration/setup.ts | 2 +- test/integration/storage.spec.ts | 2 +- test/resources/mocks.ts | 2 +- test/unit/app-check/app-check-api-client-internal.spec.ts | 2 +- test/unit/app-check/app-check.spec.ts | 2 +- test/unit/app-check/token-generator.spec.ts | 2 +- test/unit/app-check/token-verifier.spec.ts | 2 +- test/unit/app/credential-internal.spec.ts | 2 +- test/unit/app/firebase-app.spec.ts | 2 +- test/unit/app/firebase-namespace.spec.ts | 2 +- test/unit/app/index.spec.ts | 2 +- test/unit/auth/action-code-settings-builder.spec.ts | 2 +- test/unit/auth/auth-api-request.spec.ts | 2 +- test/unit/auth/auth-config.spec.ts | 2 +- test/unit/auth/auth.spec.ts | 2 +- test/unit/auth/index.spec.ts | 2 +- test/unit/auth/project-config-manager.spec.ts | 2 +- test/unit/auth/project-config.spec.ts | 2 +- test/unit/auth/tenant-manager.spec.ts | 2 +- test/unit/auth/tenant.spec.ts | 2 +- test/unit/auth/token-generator.spec.ts | 2 +- test/unit/auth/token-verifier.spec.ts | 2 +- test/unit/auth/user-import-builder.spec.ts | 2 +- test/unit/auth/user-record.spec.ts | 2 +- test/unit/data-connect/data-connect-api-client-internal.spec.ts | 2 +- test/unit/data-connect/index.spec.ts | 2 +- test/unit/database/database.spec.ts | 2 +- test/unit/database/index.spec.ts | 2 +- test/unit/eventarc/eventarc-utils.spec.ts | 2 +- test/unit/eventarc/eventarc.spec.ts | 2 +- test/unit/extensions/extensions-api-client-internal.spec.ts | 2 +- test/unit/extensions/extensions.spec.ts | 2 +- test/unit/firebase.spec.ts | 2 +- test/unit/firestore/firestore.spec.ts | 2 +- test/unit/firestore/index.spec.ts | 2 +- test/unit/functions/functions-api-client-internal.spec.ts | 2 +- test/unit/functions/functions.spec.ts | 2 +- test/unit/functions/index.spec.ts | 2 +- test/unit/index.spec.ts | 2 +- test/unit/installations/installations-request-handler.spec.ts | 2 +- test/unit/installations/installations.spec.ts | 2 +- test/unit/instance-id/index.spec.ts | 2 +- test/unit/instance-id/instance-id.spec.ts | 2 +- test/unit/machine-learning/index.spec.ts | 2 +- test/unit/machine-learning/machine-learning-api-client.spec.ts | 2 +- test/unit/machine-learning/machine-learning.spec.ts | 2 +- test/unit/messaging/index.spec.ts | 2 +- test/unit/messaging/messaging.spec.ts | 2 +- test/unit/project-management/android-app.spec.ts | 2 +- test/unit/project-management/index.spec.ts | 2 +- test/unit/project-management/ios-app.spec.ts | 2 +- .../project-management/project-management-api-request.spec.ts | 2 +- test/unit/project-management/project-management.spec.ts | 2 +- test/unit/remote-config/condition-evaluator.spec.ts | 2 +- test/unit/remote-config/index.spec.ts | 2 +- test/unit/remote-config/internal/value-impl.spec.ts | 2 +- test/unit/remote-config/remote-config-api-client.spec.ts | 2 +- test/unit/remote-config/remote-config.spec.ts | 2 +- test/unit/security-rules/index.spec.ts | 2 +- test/unit/security-rules/security-rules-api-client.spec.ts | 2 +- test/unit/security-rules/security-rules.spec.ts | 2 +- test/unit/storage/index.spec.ts | 2 +- test/unit/storage/storage.spec.ts | 2 +- test/unit/utils.ts | 2 +- test/unit/utils/api-request.spec.ts | 2 +- test/unit/utils/crypto-signer.spec.ts | 2 +- test/unit/utils/error.spec.ts | 2 +- test/unit/utils/index.spec.ts | 2 +- test/unit/utils/jwt.spec.ts | 2 +- test/unit/utils/validator.spec.ts | 2 +- 210 files changed, 210 insertions(+), 210 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 64a352ee2a..c462e7c05a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/.github/actions/send-email/action.yml b/.github/actions/send-email/action.yml index eca721b842..98364e6cca 100644 --- a/.github/actions/send-email/action.yml +++ b/.github/actions/send-email/action.yml @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. +# Copyright 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/.github/actions/send-email/index.js b/.github/actions/send-email/index.js index 38be9f04fc..9fefbe8f1e 100644 --- a/.github/actions/send-email/index.js +++ b/.github/actions/send-email/index.js @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/.github/actions/send-tweet/action.yml b/.github/actions/send-tweet/action.yml index bb45748d4b..853273f54b 100644 --- a/.github/actions/send-tweet/action.yml +++ b/.github/actions/send-tweet/action.yml @@ -1,4 +1,4 @@ -# Copyright 2020 Google Inc. +# Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/.github/actions/send-tweet/index.js b/.github/actions/send-tweet/index.js index aa6140ba8b..6a90a92218 100644 --- a/.github/actions/send-tweet/index.js +++ b/.github/actions/send-tweet/index.js @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/.github/scripts/publish_package.sh b/.github/scripts/publish_package.sh index 42d40c199e..d4a7bb4e0b 100755 --- a/.github/scripts/publish_package.sh +++ b/.github/scripts/publish_package.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2020 Google Inc. +# Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/.github/scripts/publish_preflight_check.sh b/.github/scripts/publish_preflight_check.sh index 8d2f4a14cd..d52b49a531 100755 --- a/.github/scripts/publish_preflight_check.sh +++ b/.github/scripts/publish_preflight_check.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2020 Google Inc. +# Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/.github/scripts/run_integration_tests.sh b/.github/scripts/run_integration_tests.sh index 37dc7d1216..414fbab6f2 100755 --- a/.github/scripts/run_integration_tests.sh +++ b/.github/scripts/run_integration_tests.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2020 Google Inc. +# Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/.github/scripts/verify_package.sh b/.github/scripts/verify_package.sh index 2b295a63b9..8bfdf295ca 100755 --- a/.github/scripts/verify_package.sh +++ b/.github/scripts/verify_package.sh @@ -1,4 +1,4 @@ -# Copyright 2017 Google Inc. +# Copyright 2017 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index e3ce768cb5..277f1449e4 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -1,4 +1,4 @@ -# Copyright 2021 Google Inc. +# Copyright 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e2a7eb9cea..0589a5f669 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -# Copyright 2020 Google Inc. +# Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/docgen/post-process.js b/docgen/post-process.js index 9c0a79bdcd..8b350d5a10 100644 --- a/docgen/post-process.js +++ b/docgen/post-process.js @@ -1,6 +1,6 @@ /** * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/generate-esm-wrapper.js b/generate-esm-wrapper.js index 3d47c709d1..753927bb0b 100644 --- a/generate-esm-wrapper.js +++ b/generate-esm-wrapper.js @@ -1,6 +1,6 @@ /** * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/generate-reports.js b/generate-reports.js index 129c06e542..8d92b0d2b8 100644 --- a/generate-reports.js +++ b/generate-reports.js @@ -1,6 +1,6 @@ /** * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/gulpfile.js b/gulpfile.js index 749b6ff517..f5c2e06acd 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/app-check/app-check-api-client-internal.ts b/src/app-check/app-check-api-client-internal.ts index 6e896044f6..8a1afa872e 100644 --- a/src/app-check/app-check-api-client-internal.ts +++ b/src/app-check/app-check-api-client-internal.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/app-check/app-check-api.ts b/src/app-check/app-check-api.ts index de44a5a854..7766299fcd 100644 --- a/src/app-check/app-check-api.ts +++ b/src/app-check/app-check-api.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/app-check/app-check-namespace.ts b/src/app-check/app-check-namespace.ts index 13e5beb3a7..85ac284bb4 100644 --- a/src/app-check/app-check-namespace.ts +++ b/src/app-check/app-check-namespace.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/app-check/app-check.ts b/src/app-check/app-check.ts index c81a04acf5..c7e6cadbd1 100644 --- a/src/app-check/app-check.ts +++ b/src/app-check/app-check.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/app-check/index.ts b/src/app-check/index.ts index 54cd9291d8..7b4ba04709 100644 --- a/src/app-check/index.ts +++ b/src/app-check/index.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/app-check/token-generator.ts b/src/app-check/token-generator.ts index 0ae7032d64..be3383d9d0 100644 --- a/src/app-check/token-generator.ts +++ b/src/app-check/token-generator.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/app-check/token-verifier.ts b/src/app-check/token-verifier.ts index cde4878a42..074e556307 100644 --- a/src/app-check/token-verifier.ts +++ b/src/app-check/token-verifier.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/app/core.ts b/src/app/core.ts index f37e3c112c..c229b20408 100644 --- a/src/app/core.ts +++ b/src/app/core.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/app/credential-factory.ts b/src/app/credential-factory.ts index 9bb32a8869..e6bbdfcd44 100644 --- a/src/app/credential-factory.ts +++ b/src/app/credential-factory.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/app/credential-internal.ts b/src/app/credential-internal.ts index 2171d6e637..8ce61b4edd 100644 --- a/src/app/credential-internal.ts +++ b/src/app/credential-internal.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/app/credential.ts b/src/app/credential.ts index b5857903f8..4bd1fa3705 100644 --- a/src/app/credential.ts +++ b/src/app/credential.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/app/firebase-app.ts b/src/app/firebase-app.ts index 982c8d14e8..dcd30d8a1d 100644 --- a/src/app/firebase-app.ts +++ b/src/app/firebase-app.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/app/firebase-namespace.ts b/src/app/firebase-namespace.ts index ef5e17f0eb..edc022dd62 100644 --- a/src/app/firebase-namespace.ts +++ b/src/app/firebase-namespace.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/app/index.ts b/src/app/index.ts index 5308e414e2..b10323b8f9 100644 --- a/src/app/index.ts +++ b/src/app/index.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/app/lifecycle.ts b/src/app/lifecycle.ts index 5160e51f5c..d2b5898451 100644 --- a/src/app/lifecycle.ts +++ b/src/app/lifecycle.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/auth/action-code-settings-builder.ts b/src/auth/action-code-settings-builder.ts index 13dbcbc725..5ffd72d048 100644 --- a/src/auth/action-code-settings-builder.ts +++ b/src/auth/action-code-settings-builder.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/auth/auth-api-request.ts b/src/auth/auth-api-request.ts index cd9c145b1e..8e979d1aed 100644 --- a/src/auth/auth-api-request.ts +++ b/src/auth/auth-api-request.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/auth/auth-config.ts b/src/auth/auth-config.ts index d2d07e9536..1dd5565a88 100644 --- a/src/auth/auth-config.ts +++ b/src/auth/auth-config.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/auth/auth-namespace.ts b/src/auth/auth-namespace.ts index 486c0b488a..7c450e8a61 100644 --- a/src/auth/auth-namespace.ts +++ b/src/auth/auth-namespace.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/auth/auth.ts b/src/auth/auth.ts index 4808fbbdc0..75848a3731 100644 --- a/src/auth/auth.ts +++ b/src/auth/auth.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/auth/base-auth.ts b/src/auth/base-auth.ts index 56941056d9..25e1a3db2b 100644 --- a/src/auth/base-auth.ts +++ b/src/auth/base-auth.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/auth/identifier.ts b/src/auth/identifier.ts index 8bae483d1d..424dc77df1 100644 --- a/src/auth/identifier.ts +++ b/src/auth/identifier.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/auth/index.ts b/src/auth/index.ts index 69cb95bf2a..4650b25e27 100644 --- a/src/auth/index.ts +++ b/src/auth/index.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/auth/project-config-manager.ts b/src/auth/project-config-manager.ts index 847aa7d982..f956bac7e7 100644 --- a/src/auth/project-config-manager.ts +++ b/src/auth/project-config-manager.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2022 Google Inc. + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/auth/project-config.ts b/src/auth/project-config.ts index 259f6eebb2..1695ec3a0d 100644 --- a/src/auth/project-config.ts +++ b/src/auth/project-config.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2022 Google Inc. + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/auth/tenant-manager.ts b/src/auth/tenant-manager.ts index 71a505ecc7..19da5b4c83 100644 --- a/src/auth/tenant-manager.ts +++ b/src/auth/tenant-manager.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2019 Google Inc. + * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/auth/tenant.ts b/src/auth/tenant.ts index 4b66cace3e..19812c02e6 100644 --- a/src/auth/tenant.ts +++ b/src/auth/tenant.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2019 Google Inc. + * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/auth/token-generator.ts b/src/auth/token-generator.ts index ce6e21fbb6..3df1dcebe2 100644 --- a/src/auth/token-generator.ts +++ b/src/auth/token-generator.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/auth/token-verifier.ts b/src/auth/token-verifier.ts index 73fc8f678c..64da8ac516 100644 --- a/src/auth/token-verifier.ts +++ b/src/auth/token-verifier.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/auth/user-import-builder.ts b/src/auth/user-import-builder.ts index 23e4e5aba3..f396066b49 100644 --- a/src/auth/user-import-builder.ts +++ b/src/auth/user-import-builder.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/auth/user-record.ts b/src/auth/user-record.ts index 5b00151401..2310968849 100644 --- a/src/auth/user-record.ts +++ b/src/auth/user-record.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/credential/index.ts b/src/credential/index.ts index f83adcc148..f8a9cc47a7 100644 --- a/src/credential/index.ts +++ b/src/credential/index.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/data-connect/data-connect-api-client-internal.ts b/src/data-connect/data-connect-api-client-internal.ts index e12005b8bb..c100abd023 100644 --- a/src/data-connect/data-connect-api-client-internal.ts +++ b/src/data-connect/data-connect-api-client-internal.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2024 Google Inc. + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/data-connect/data-connect-api.ts b/src/data-connect/data-connect-api.ts index 1073437032..c60ef5a2eb 100644 --- a/src/data-connect/data-connect-api.ts +++ b/src/data-connect/data-connect-api.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2024 Google Inc. + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/data-connect/data-connect.ts b/src/data-connect/data-connect.ts index aa7d9d7e19..a689423257 100644 --- a/src/data-connect/data-connect.ts +++ b/src/data-connect/data-connect.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2024 Google Inc. + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/data-connect/index.ts b/src/data-connect/index.ts index ab262682f2..43ca313c90 100644 --- a/src/data-connect/index.ts +++ b/src/data-connect/index.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2024 Google Inc. + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/database/database-namespace.ts b/src/database/database-namespace.ts index 0bb190bc27..03d116b7d2 100644 --- a/src/database/database-namespace.ts +++ b/src/database/database-namespace.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/database/database.ts b/src/database/database.ts index 144f08d430..355c43b33a 100644 --- a/src/database/database.ts +++ b/src/database/database.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/database/index.ts b/src/database/index.ts index 7a17deb751..c078893d2c 100644 --- a/src/database/index.ts +++ b/src/database/index.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/default-namespace.d.ts b/src/default-namespace.d.ts index 5405cc67c6..c5c826a1e6 100644 --- a/src/default-namespace.d.ts +++ b/src/default-namespace.d.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/default-namespace.ts b/src/default-namespace.ts index 12e855e2d8..bd250cd79c 100644 --- a/src/default-namespace.ts +++ b/src/default-namespace.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/eventarc/cloudevent.ts b/src/eventarc/cloudevent.ts index 9fa0749f2e..1a5396868a 100644 --- a/src/eventarc/cloudevent.ts +++ b/src/eventarc/cloudevent.ts @@ -1,7 +1,7 @@ /*! * @license - * Copyright 2022 Google Inc. + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/eventarc/eventarc-client-internal.ts b/src/eventarc/eventarc-client-internal.ts index 3aecbdcc96..e4e5bda0a4 100644 --- a/src/eventarc/eventarc-client-internal.ts +++ b/src/eventarc/eventarc-client-internal.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2022 Google Inc. + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/eventarc/eventarc-utils.ts b/src/eventarc/eventarc-utils.ts index 6bf6531285..3737e5a906 100644 --- a/src/eventarc/eventarc-utils.ts +++ b/src/eventarc/eventarc-utils.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2022 Google Inc. + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/eventarc/eventarc.ts b/src/eventarc/eventarc.ts index a1edc4a011..c27e0b3acf 100644 --- a/src/eventarc/eventarc.ts +++ b/src/eventarc/eventarc.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2022 Google Inc. + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/eventarc/index.ts b/src/eventarc/index.ts index d1e6fc79bd..f3bb023947 100644 --- a/src/eventarc/index.ts +++ b/src/eventarc/index.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2022 Google Inc. + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/extensions/extensions-api-client-internal.ts b/src/extensions/extensions-api-client-internal.ts index a51c001304..a90c5b34fd 100644 --- a/src/extensions/extensions-api-client-internal.ts +++ b/src/extensions/extensions-api-client-internal.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2022 Google Inc. + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/extensions/extensions-api.ts b/src/extensions/extensions-api.ts index ce0c18f89d..e95a02c6de 100644 --- a/src/extensions/extensions-api.ts +++ b/src/extensions/extensions-api.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2022 Google Inc. + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/extensions/extensions.ts b/src/extensions/extensions.ts index e16f893aa0..f148d68dcb 100644 --- a/src/extensions/extensions.ts +++ b/src/extensions/extensions.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2022 Google Inc. + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/extensions/index.ts b/src/extensions/index.ts index 486ad645d0..e96401a774 100644 --- a/src/extensions/index.ts +++ b/src/extensions/index.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/firebase-namespace-api.ts b/src/firebase-namespace-api.ts index 5358e5bd2c..3808876910 100644 --- a/src/firebase-namespace-api.ts +++ b/src/firebase-namespace-api.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/firestore/firestore-internal.ts b/src/firestore/firestore-internal.ts index 65cb123d85..95c6e74414 100644 --- a/src/firestore/firestore-internal.ts +++ b/src/firestore/firestore-internal.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/firestore/firestore-namespace.ts b/src/firestore/firestore-namespace.ts index 67733fb8f6..84ffe51ab5 100644 --- a/src/firestore/firestore-namespace.ts +++ b/src/firestore/firestore-namespace.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/firestore/index.ts b/src/firestore/index.ts index f906f382e2..c41fd5ea1a 100644 --- a/src/firestore/index.ts +++ b/src/firestore/index.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/functions/functions-api-client-internal.ts b/src/functions/functions-api-client-internal.ts index e1fd04ee6d..d68a0b1c72 100644 --- a/src/functions/functions-api-client-internal.ts +++ b/src/functions/functions-api-client-internal.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/functions/functions-api.ts b/src/functions/functions-api.ts index a0473baee2..e685aeb99b 100644 --- a/src/functions/functions-api.ts +++ b/src/functions/functions-api.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/functions/functions.ts b/src/functions/functions.ts index f5a3ce6153..c8c0a30a71 100644 --- a/src/functions/functions.ts +++ b/src/functions/functions.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/functions/index.ts b/src/functions/index.ts index 11e05c6782..bd86d88c2d 100644 --- a/src/functions/index.ts +++ b/src/functions/index.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/index.d.ts b/src/index.d.ts index b893c88183..cb5413cd2d 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/index.ts b/src/index.ts index eb121bc023..cdc5b6c64d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/installations/index.ts b/src/installations/index.ts index 1d6a39dc35..e7fc00ab78 100644 --- a/src/installations/index.ts +++ b/src/installations/index.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/installations/installations-namespace.ts b/src/installations/installations-namespace.ts index 1bdca2fbce..f2bd180205 100644 --- a/src/installations/installations-namespace.ts +++ b/src/installations/installations-namespace.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/installations/installations-request-handler.ts b/src/installations/installations-request-handler.ts index 5314642d55..67c0f6f46f 100644 --- a/src/installations/installations-request-handler.ts +++ b/src/installations/installations-request-handler.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/installations/installations.ts b/src/installations/installations.ts index c5408299da..fb2bb5fa15 100644 --- a/src/installations/installations.ts +++ b/src/installations/installations.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/instance-id/index.ts b/src/instance-id/index.ts index 1a9c06d3bc..0486a927af 100644 --- a/src/instance-id/index.ts +++ b/src/instance-id/index.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/instance-id/instance-id.ts b/src/instance-id/instance-id.ts index e5dc2102b0..3a5baac09f 100644 --- a/src/instance-id/instance-id.ts +++ b/src/instance-id/instance-id.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/machine-learning/index.ts b/src/machine-learning/index.ts index 433832c358..2945c43d35 100644 --- a/src/machine-learning/index.ts +++ b/src/machine-learning/index.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/machine-learning/machine-learning-api-client.ts b/src/machine-learning/machine-learning-api-client.ts index c3ea7cee4f..b8fd5301f7 100644 --- a/src/machine-learning/machine-learning-api-client.ts +++ b/src/machine-learning/machine-learning-api-client.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/machine-learning/machine-learning-namespace.ts b/src/machine-learning/machine-learning-namespace.ts index 7c5786fdbb..5131390a67 100644 --- a/src/machine-learning/machine-learning-namespace.ts +++ b/src/machine-learning/machine-learning-namespace.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/machine-learning/machine-learning-utils.ts b/src/machine-learning/machine-learning-utils.ts index 1202314e93..3e2236975c 100644 --- a/src/machine-learning/machine-learning-utils.ts +++ b/src/machine-learning/machine-learning-utils.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/machine-learning/machine-learning.ts b/src/machine-learning/machine-learning.ts index 98b956c85c..a83bda71cc 100644 --- a/src/machine-learning/machine-learning.ts +++ b/src/machine-learning/machine-learning.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/messaging/index.ts b/src/messaging/index.ts index d0a39df2fb..16054c2c38 100644 --- a/src/messaging/index.ts +++ b/src/messaging/index.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/messaging/messaging-api-request-internal.ts b/src/messaging/messaging-api-request-internal.ts index 830fbed568..3c4e124d44 100644 --- a/src/messaging/messaging-api-request-internal.ts +++ b/src/messaging/messaging-api-request-internal.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/messaging/messaging-api.ts b/src/messaging/messaging-api.ts index d0c5d5af7e..a305ec68b3 100644 --- a/src/messaging/messaging-api.ts +++ b/src/messaging/messaging-api.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/messaging/messaging-errors-internal.ts b/src/messaging/messaging-errors-internal.ts index a04e4cfb2b..41aa667b1d 100644 --- a/src/messaging/messaging-errors-internal.ts +++ b/src/messaging/messaging-errors-internal.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2019 Google Inc. + * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/messaging/messaging-internal.ts b/src/messaging/messaging-internal.ts index c41f93a923..311d2af4be 100644 --- a/src/messaging/messaging-internal.ts +++ b/src/messaging/messaging-internal.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/messaging/messaging-namespace.ts b/src/messaging/messaging-namespace.ts index 40ca3a3ed2..43627c60ed 100644 --- a/src/messaging/messaging-namespace.ts +++ b/src/messaging/messaging-namespace.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/messaging/messaging.ts b/src/messaging/messaging.ts index 8e61cd679a..05367128f4 100644 --- a/src/messaging/messaging.ts +++ b/src/messaging/messaging.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/project-management/android-app.ts b/src/project-management/android-app.ts index 667b8be4d6..e4e503e946 100644 --- a/src/project-management/android-app.ts +++ b/src/project-management/android-app.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/project-management/app-metadata.ts b/src/project-management/app-metadata.ts index 8fd69255d2..0f07f89dca 100644 --- a/src/project-management/app-metadata.ts +++ b/src/project-management/app-metadata.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/project-management/index.ts b/src/project-management/index.ts index cc23066681..87a6d570ce 100644 --- a/src/project-management/index.ts +++ b/src/project-management/index.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/project-management/ios-app.ts b/src/project-management/ios-app.ts index 0789265f29..775d19a120 100644 --- a/src/project-management/ios-app.ts +++ b/src/project-management/ios-app.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/project-management/project-management-api-request-internal.ts b/src/project-management/project-management-api-request-internal.ts index d4a38cb2bc..bc502a5ebd 100644 --- a/src/project-management/project-management-api-request-internal.ts +++ b/src/project-management/project-management-api-request-internal.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/project-management/project-management-namespace.ts b/src/project-management/project-management-namespace.ts index f3d48d1fe7..ac58bc215e 100644 --- a/src/project-management/project-management-namespace.ts +++ b/src/project-management/project-management-namespace.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/project-management/project-management.ts b/src/project-management/project-management.ts index ac74004555..c29589be86 100644 --- a/src/project-management/project-management.ts +++ b/src/project-management/project-management.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/remote-config/condition-evaluator-internal.ts b/src/remote-config/condition-evaluator-internal.ts index b49b35dc8d..73a878109f 100644 --- a/src/remote-config/condition-evaluator-internal.ts +++ b/src/remote-config/condition-evaluator-internal.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2024 Google Inc. + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/remote-config/index.ts b/src/remote-config/index.ts index 42a2573227..790568735e 100644 --- a/src/remote-config/index.ts +++ b/src/remote-config/index.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/remote-config/internal/value-impl.ts b/src/remote-config/internal/value-impl.ts index 6d71476538..92dc79e0c9 100644 --- a/src/remote-config/internal/value-impl.ts +++ b/src/remote-config/internal/value-impl.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2024 Google Inc. + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/remote-config/remote-config-api-client-internal.ts b/src/remote-config/remote-config-api-client-internal.ts index 30a0b10a54..199c974c7d 100644 --- a/src/remote-config/remote-config-api-client-internal.ts +++ b/src/remote-config/remote-config-api-client-internal.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/remote-config/remote-config-api.ts b/src/remote-config/remote-config-api.ts index d315572192..ed7da05496 100644 --- a/src/remote-config/remote-config-api.ts +++ b/src/remote-config/remote-config-api.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/remote-config/remote-config-namespace.ts b/src/remote-config/remote-config-namespace.ts index 4efb3baee1..159204d316 100644 --- a/src/remote-config/remote-config-namespace.ts +++ b/src/remote-config/remote-config-namespace.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/remote-config/remote-config.ts b/src/remote-config/remote-config.ts index a984d67e1f..91eebfa0cb 100644 --- a/src/remote-config/remote-config.ts +++ b/src/remote-config/remote-config.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/security-rules/index.ts b/src/security-rules/index.ts index b3b78a265a..23d6ed10fd 100644 --- a/src/security-rules/index.ts +++ b/src/security-rules/index.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/security-rules/security-rules-api-client-internal.ts b/src/security-rules/security-rules-api-client-internal.ts index 7f10a27b93..33ad1ee886 100644 --- a/src/security-rules/security-rules-api-client-internal.ts +++ b/src/security-rules/security-rules-api-client-internal.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2019 Google Inc. + * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/security-rules/security-rules-internal.ts b/src/security-rules/security-rules-internal.ts index d21f5a0f0e..539359cb63 100644 --- a/src/security-rules/security-rules-internal.ts +++ b/src/security-rules/security-rules-internal.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2019 Google Inc. + * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/security-rules/security-rules-namespace.ts b/src/security-rules/security-rules-namespace.ts index 52ffc592a7..d61ba294fb 100644 --- a/src/security-rules/security-rules-namespace.ts +++ b/src/security-rules/security-rules-namespace.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/security-rules/security-rules.ts b/src/security-rules/security-rules.ts index 135e32459b..8dcd55c836 100644 --- a/src/security-rules/security-rules.ts +++ b/src/security-rules/security-rules.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2019 Google Inc. + * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/storage/index.ts b/src/storage/index.ts index 7b963593e8..2638192690 100644 --- a/src/storage/index.ts +++ b/src/storage/index.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/storage/storage-namespace.ts b/src/storage/storage-namespace.ts index f0dd80725d..29c88068cc 100644 --- a/src/storage/storage-namespace.ts +++ b/src/storage/storage-namespace.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/storage/storage.ts b/src/storage/storage.ts index 6e155b379e..b78898fb34 100644 --- a/src/storage/storage.ts +++ b/src/storage/storage.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/utils/api-request.ts b/src/utils/api-request.ts index f26fcf9326..3e5aa754d8 100644 --- a/src/utils/api-request.ts +++ b/src/utils/api-request.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/utils/crypto-signer.ts b/src/utils/crypto-signer.ts index f2b8bcbafd..a4b1f20be5 100644 --- a/src/utils/crypto-signer.ts +++ b/src/utils/crypto-signer.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/utils/deep-copy.ts b/src/utils/deep-copy.ts index 1dde902c03..e4c2bddf85 100644 --- a/src/utils/deep-copy.ts +++ b/src/utils/deep-copy.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/utils/error.ts b/src/utils/error.ts index a80254b5b8..db013b8967 100644 --- a/src/utils/error.ts +++ b/src/utils/error.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/utils/index.ts b/src/utils/index.ts index c7c331206d..7754e59b01 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/utils/jwt.ts b/src/utils/jwt.ts index 1b1fea6af9..9a66494482 100644 --- a/src/utils/jwt.ts +++ b/src/utils/jwt.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/utils/validator.ts b/src/utils/validator.ts index d63d14772c..fb738904e1 100644 --- a/src/utils/validator.ts +++ b/src/utils/validator.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/integration/app-check.spec.ts b/test/integration/app-check.spec.ts index 83a71a6f53..89a920daea 100644 --- a/test/integration/app-check.spec.ts +++ b/test/integration/app-check.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/integration/app.spec.ts b/test/integration/app.spec.ts index 3470ae088a..d679dc5766 100644 --- a/test/integration/app.spec.ts +++ b/test/integration/app.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/integration/auth.spec.ts b/test/integration/auth.spec.ts index 560cd47005..3c538d3319 100644 --- a/test/integration/auth.spec.ts +++ b/test/integration/auth.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/integration/data-connect.spec.ts b/test/integration/data-connect.spec.ts index f9ced89ece..8ab24925df 100644 --- a/test/integration/data-connect.spec.ts +++ b/test/integration/data-connect.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2024 Google Inc. + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/integration/database.spec.ts b/test/integration/database.spec.ts index 40b35484eb..7f4dce576f 100644 --- a/test/integration/database.spec.ts +++ b/test/integration/database.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/integration/firestore.spec.ts b/test/integration/firestore.spec.ts index 9127ef2e17..18cb9e75a8 100644 --- a/test/integration/firestore.spec.ts +++ b/test/integration/firestore.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/integration/functions.spec.ts b/test/integration/functions.spec.ts index 9b1f5277f9..ef65176935 100644 --- a/test/integration/functions.spec.ts +++ b/test/integration/functions.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2022 Google Inc. + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/integration/installations.spec.ts b/test/integration/installations.spec.ts index 98eb3ad72a..3dd68b72e7 100644 --- a/test/integration/installations.spec.ts +++ b/test/integration/installations.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/integration/instance-id.spec.ts b/test/integration/instance-id.spec.ts index 2155205990..fe3cccc935 100644 --- a/test/integration/instance-id.spec.ts +++ b/test/integration/instance-id.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/integration/machine-learning.spec.ts b/test/integration/machine-learning.spec.ts index b44ebdc1e2..e7a5b42567 100644 --- a/test/integration/machine-learning.spec.ts +++ b/test/integration/machine-learning.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/integration/messaging.spec.ts b/test/integration/messaging.spec.ts index 97adc8e008..94304edce8 100644 --- a/test/integration/messaging.spec.ts +++ b/test/integration/messaging.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/integration/postcheck/esm/example.test.js b/test/integration/postcheck/esm/example.test.js index 29d0654374..6ac8540196 100644 --- a/test/integration/postcheck/esm/example.test.js +++ b/test/integration/postcheck/esm/example.test.js @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/integration/postcheck/typescript/example-modular.test.ts b/test/integration/postcheck/typescript/example-modular.test.ts index c5ccf3c8a2..67332e01af 100644 --- a/test/integration/postcheck/typescript/example-modular.test.ts +++ b/test/integration/postcheck/typescript/example-modular.test.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/integration/postcheck/typescript/example.test.ts b/test/integration/postcheck/typescript/example.test.ts index 2d97196477..5de1eadae0 100644 --- a/test/integration/postcheck/typescript/example.test.ts +++ b/test/integration/postcheck/typescript/example.test.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/integration/postcheck/typescript/example.ts b/test/integration/postcheck/typescript/example.ts index b44dbcc163..f626ba374c 100644 --- a/test/integration/postcheck/typescript/example.ts +++ b/test/integration/postcheck/typescript/example.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/integration/project-management.spec.ts b/test/integration/project-management.spec.ts index 0d24566394..b2eaa24b32 100644 --- a/test/integration/project-management.spec.ts +++ b/test/integration/project-management.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/integration/remote-config.spec.ts b/test/integration/remote-config.spec.ts index 79689a64e6..0818bf0ab7 100644 --- a/test/integration/remote-config.spec.ts +++ b/test/integration/remote-config.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/integration/security-rules.spec.ts b/test/integration/security-rules.spec.ts index eda5f000f4..85bae9dd47 100644 --- a/test/integration/security-rules.spec.ts +++ b/test/integration/security-rules.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2019 Google Inc. + * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/integration/setup.ts b/test/integration/setup.ts index 8c700d4c41..5c23270f3b 100644 --- a/test/integration/setup.ts +++ b/test/integration/setup.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/integration/storage.spec.ts b/test/integration/storage.spec.ts index 78d1c731b5..406cfa94bf 100644 --- a/test/integration/storage.spec.ts +++ b/test/integration/storage.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/resources/mocks.ts b/test/resources/mocks.ts index c6b86886a0..e0f2ed00a9 100644 --- a/test/resources/mocks.ts +++ b/test/resources/mocks.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/app-check/app-check-api-client-internal.spec.ts b/test/unit/app-check/app-check-api-client-internal.spec.ts index 743f6ed89f..b6a5060bb5 100644 --- a/test/unit/app-check/app-check-api-client-internal.spec.ts +++ b/test/unit/app-check/app-check-api-client-internal.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/app-check/app-check.spec.ts b/test/unit/app-check/app-check.spec.ts index 62b8eeac64..e849be9850 100644 --- a/test/unit/app-check/app-check.spec.ts +++ b/test/unit/app-check/app-check.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/app-check/token-generator.spec.ts b/test/unit/app-check/token-generator.spec.ts index f892c9fa08..a2e72e9f08 100644 --- a/test/unit/app-check/token-generator.spec.ts +++ b/test/unit/app-check/token-generator.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/app-check/token-verifier.spec.ts b/test/unit/app-check/token-verifier.spec.ts index ecc664783b..38d42ac897 100644 --- a/test/unit/app-check/token-verifier.spec.ts +++ b/test/unit/app-check/token-verifier.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/app/credential-internal.spec.ts b/test/unit/app/credential-internal.spec.ts index f37716e998..d57cc5e91a 100644 --- a/test/unit/app/credential-internal.spec.ts +++ b/test/unit/app/credential-internal.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/app/firebase-app.spec.ts b/test/unit/app/firebase-app.spec.ts index a99e15d8f0..56a7edae39 100644 --- a/test/unit/app/firebase-app.spec.ts +++ b/test/unit/app/firebase-app.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/app/firebase-namespace.spec.ts b/test/unit/app/firebase-namespace.spec.ts index 38a4c49886..694c6f5ffe 100644 --- a/test/unit/app/firebase-namespace.spec.ts +++ b/test/unit/app/firebase-namespace.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/app/index.spec.ts b/test/unit/app/index.spec.ts index 896fd2534e..af0d0ebf75 100644 --- a/test/unit/app/index.spec.ts +++ b/test/unit/app/index.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/auth/action-code-settings-builder.spec.ts b/test/unit/auth/action-code-settings-builder.spec.ts index 4608f711f1..f8eed33d74 100644 --- a/test/unit/auth/action-code-settings-builder.spec.ts +++ b/test/unit/auth/action-code-settings-builder.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/auth/auth-api-request.spec.ts b/test/unit/auth/auth-api-request.spec.ts index 9a6d624b88..5f8e45b458 100644 --- a/test/unit/auth/auth-api-request.spec.ts +++ b/test/unit/auth/auth-api-request.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/auth/auth-config.spec.ts b/test/unit/auth/auth-config.spec.ts index c5bdb7c445..e33c6f7f24 100644 --- a/test/unit/auth/auth-config.spec.ts +++ b/test/unit/auth/auth-config.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/auth/auth.spec.ts b/test/unit/auth/auth.spec.ts index 34d4c3d9a6..b9675e720b 100644 --- a/test/unit/auth/auth.spec.ts +++ b/test/unit/auth/auth.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/auth/index.spec.ts b/test/unit/auth/index.spec.ts index 099bb00cd4..beb0b784d7 100644 --- a/test/unit/auth/index.spec.ts +++ b/test/unit/auth/index.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/auth/project-config-manager.spec.ts b/test/unit/auth/project-config-manager.spec.ts index 3fc0770b36..be7df54d99 100644 --- a/test/unit/auth/project-config-manager.spec.ts +++ b/test/unit/auth/project-config-manager.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2022 Google Inc. + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/auth/project-config.spec.ts b/test/unit/auth/project-config.spec.ts index 7857deb398..a447bf28ed 100644 --- a/test/unit/auth/project-config.spec.ts +++ b/test/unit/auth/project-config.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2022 Google Inc. + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/auth/tenant-manager.spec.ts b/test/unit/auth/tenant-manager.spec.ts index 8a8f0617f2..782e4f96bb 100644 --- a/test/unit/auth/tenant-manager.spec.ts +++ b/test/unit/auth/tenant-manager.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2019 Google Inc. + * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/auth/tenant.spec.ts b/test/unit/auth/tenant.spec.ts index 05c8bbf6aa..62ef31478b 100644 --- a/test/unit/auth/tenant.spec.ts +++ b/test/unit/auth/tenant.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2019 Google Inc. + * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/auth/token-generator.spec.ts b/test/unit/auth/token-generator.spec.ts index acb89b13e8..1cc9bd44ab 100644 --- a/test/unit/auth/token-generator.spec.ts +++ b/test/unit/auth/token-generator.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/auth/token-verifier.spec.ts b/test/unit/auth/token-verifier.spec.ts index 2d01678ef7..3dae6817cd 100644 --- a/test/unit/auth/token-verifier.spec.ts +++ b/test/unit/auth/token-verifier.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/auth/user-import-builder.spec.ts b/test/unit/auth/user-import-builder.spec.ts index 859265a03a..6be38ebe28 100644 --- a/test/unit/auth/user-import-builder.spec.ts +++ b/test/unit/auth/user-import-builder.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/auth/user-record.spec.ts b/test/unit/auth/user-record.spec.ts index dc332c13b9..0a6a7c0357 100644 --- a/test/unit/auth/user-record.spec.ts +++ b/test/unit/auth/user-record.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/data-connect/data-connect-api-client-internal.spec.ts b/test/unit/data-connect/data-connect-api-client-internal.spec.ts index a5798703e5..b23c5e144d 100644 --- a/test/unit/data-connect/data-connect-api-client-internal.spec.ts +++ b/test/unit/data-connect/data-connect-api-client-internal.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2024 Google Inc. + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/data-connect/index.spec.ts b/test/unit/data-connect/index.spec.ts index b71670544b..98dd6dea42 100644 --- a/test/unit/data-connect/index.spec.ts +++ b/test/unit/data-connect/index.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2024 Google Inc. + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/database/database.spec.ts b/test/unit/database/database.spec.ts index 5032deb3c3..941793ebab 100644 --- a/test/unit/database/database.spec.ts +++ b/test/unit/database/database.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/database/index.spec.ts b/test/unit/database/index.spec.ts index 382a1d96d3..82b8250915 100644 --- a/test/unit/database/index.spec.ts +++ b/test/unit/database/index.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/eventarc/eventarc-utils.spec.ts b/test/unit/eventarc/eventarc-utils.spec.ts index d2f18b7d96..b46cc53ca2 100644 --- a/test/unit/eventarc/eventarc-utils.spec.ts +++ b/test/unit/eventarc/eventarc-utils.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2022 Google Inc. + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/eventarc/eventarc.spec.ts b/test/unit/eventarc/eventarc.spec.ts index 94587cde16..93ca71d28a 100644 --- a/test/unit/eventarc/eventarc.spec.ts +++ b/test/unit/eventarc/eventarc.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2022 Google Inc. + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/extensions/extensions-api-client-internal.spec.ts b/test/unit/extensions/extensions-api-client-internal.spec.ts index 66f2e8198b..62cb8eafca 100644 --- a/test/unit/extensions/extensions-api-client-internal.spec.ts +++ b/test/unit/extensions/extensions-api-client-internal.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2022 Google Inc. + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/extensions/extensions.spec.ts b/test/unit/extensions/extensions.spec.ts index 0b3ffb3004..6f2d75a2de 100644 --- a/test/unit/extensions/extensions.spec.ts +++ b/test/unit/extensions/extensions.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2022 Google Inc. + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/firebase.spec.ts b/test/unit/firebase.spec.ts index 9a1ca5820e..815fb1a5db 100644 --- a/test/unit/firebase.spec.ts +++ b/test/unit/firebase.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/firestore/firestore.spec.ts b/test/unit/firestore/firestore.spec.ts index 22f8f4d8df..065b058b99 100644 --- a/test/unit/firestore/firestore.spec.ts +++ b/test/unit/firestore/firestore.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/firestore/index.spec.ts b/test/unit/firestore/index.spec.ts index 11c5c24936..dd78c7fe53 100644 --- a/test/unit/firestore/index.spec.ts +++ b/test/unit/firestore/index.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/functions/functions-api-client-internal.spec.ts b/test/unit/functions/functions-api-client-internal.spec.ts index f1332b2e30..34cc0ffab9 100644 --- a/test/unit/functions/functions-api-client-internal.spec.ts +++ b/test/unit/functions/functions-api-client-internal.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2022 Google Inc. + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/functions/functions.spec.ts b/test/unit/functions/functions.spec.ts index 36e6098d42..48ed21c886 100644 --- a/test/unit/functions/functions.spec.ts +++ b/test/unit/functions/functions.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2022 Google Inc. + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/functions/index.spec.ts b/test/unit/functions/index.spec.ts index 8bfcaa5dfe..8244741040 100644 --- a/test/unit/functions/index.spec.ts +++ b/test/unit/functions/index.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/index.spec.ts b/test/unit/index.spec.ts index f6bfb1b39a..c75bf0dc8b 100644 --- a/test/unit/index.spec.ts +++ b/test/unit/index.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/installations/installations-request-handler.spec.ts b/test/unit/installations/installations-request-handler.spec.ts index 8baeaa3de8..f9cca78d25 100644 --- a/test/unit/installations/installations-request-handler.spec.ts +++ b/test/unit/installations/installations-request-handler.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/installations/installations.spec.ts b/test/unit/installations/installations.spec.ts index 6a38c8413c..71d0eeae9d 100644 --- a/test/unit/installations/installations.spec.ts +++ b/test/unit/installations/installations.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/instance-id/index.spec.ts b/test/unit/instance-id/index.spec.ts index 2f1d690e6a..a33da021c7 100644 --- a/test/unit/instance-id/index.spec.ts +++ b/test/unit/instance-id/index.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/instance-id/instance-id.spec.ts b/test/unit/instance-id/instance-id.spec.ts index a610d7678d..f43d19d9d0 100644 --- a/test/unit/instance-id/instance-id.spec.ts +++ b/test/unit/instance-id/instance-id.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/machine-learning/index.spec.ts b/test/unit/machine-learning/index.spec.ts index 1937f5387d..0e1f7faa18 100644 --- a/test/unit/machine-learning/index.spec.ts +++ b/test/unit/machine-learning/index.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/machine-learning/machine-learning-api-client.spec.ts b/test/unit/machine-learning/machine-learning-api-client.spec.ts index 2f4447c2da..ce99b85177 100644 --- a/test/unit/machine-learning/machine-learning-api-client.spec.ts +++ b/test/unit/machine-learning/machine-learning-api-client.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/machine-learning/machine-learning.spec.ts b/test/unit/machine-learning/machine-learning.spec.ts index 58e424484d..ef2ec15239 100644 --- a/test/unit/machine-learning/machine-learning.spec.ts +++ b/test/unit/machine-learning/machine-learning.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/messaging/index.spec.ts b/test/unit/messaging/index.spec.ts index 56374ff1a6..0063437677 100644 --- a/test/unit/messaging/index.spec.ts +++ b/test/unit/messaging/index.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/messaging/messaging.spec.ts b/test/unit/messaging/messaging.spec.ts index 9e8d1ea814..d51c186c48 100644 --- a/test/unit/messaging/messaging.spec.ts +++ b/test/unit/messaging/messaging.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/project-management/android-app.spec.ts b/test/unit/project-management/android-app.spec.ts index 94ccacbbd3..84807737cb 100644 --- a/test/unit/project-management/android-app.spec.ts +++ b/test/unit/project-management/android-app.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/project-management/index.spec.ts b/test/unit/project-management/index.spec.ts index 6848494c96..9760f9b8fe 100644 --- a/test/unit/project-management/index.spec.ts +++ b/test/unit/project-management/index.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/project-management/ios-app.spec.ts b/test/unit/project-management/ios-app.spec.ts index f250ed5f10..58865dd821 100644 --- a/test/unit/project-management/ios-app.spec.ts +++ b/test/unit/project-management/ios-app.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/project-management/project-management-api-request.spec.ts b/test/unit/project-management/project-management-api-request.spec.ts index 58f15653b7..8040f58f81 100644 --- a/test/unit/project-management/project-management-api-request.spec.ts +++ b/test/unit/project-management/project-management-api-request.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/project-management/project-management.spec.ts b/test/unit/project-management/project-management.spec.ts index b818bdb1c8..c0acea8027 100644 --- a/test/unit/project-management/project-management.spec.ts +++ b/test/unit/project-management/project-management.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2018 Google Inc. + * Copyright 2018 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/remote-config/condition-evaluator.spec.ts b/test/unit/remote-config/condition-evaluator.spec.ts index 7368457fcf..764d1b76ef 100644 --- a/test/unit/remote-config/condition-evaluator.spec.ts +++ b/test/unit/remote-config/condition-evaluator.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2024 Google Inc. + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/remote-config/index.spec.ts b/test/unit/remote-config/index.spec.ts index f2fd51a141..5484bde18e 100644 --- a/test/unit/remote-config/index.spec.ts +++ b/test/unit/remote-config/index.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/remote-config/internal/value-impl.spec.ts b/test/unit/remote-config/internal/value-impl.spec.ts index b344d0c9d1..c8ba45c0b5 100644 --- a/test/unit/remote-config/internal/value-impl.spec.ts +++ b/test/unit/remote-config/internal/value-impl.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2024 Google Inc. + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/remote-config/remote-config-api-client.spec.ts b/test/unit/remote-config/remote-config-api-client.spec.ts index 91783e9088..b5aa644d43 100644 --- a/test/unit/remote-config/remote-config-api-client.spec.ts +++ b/test/unit/remote-config/remote-config-api-client.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/remote-config/remote-config.spec.ts b/test/unit/remote-config/remote-config.spec.ts index 976266e5ab..e7e4f84e57 100644 --- a/test/unit/remote-config/remote-config.spec.ts +++ b/test/unit/remote-config/remote-config.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2020 Google Inc. + * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/security-rules/index.spec.ts b/test/unit/security-rules/index.spec.ts index ad6a0b05de..96f3c37b59 100644 --- a/test/unit/security-rules/index.spec.ts +++ b/test/unit/security-rules/index.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/security-rules/security-rules-api-client.spec.ts b/test/unit/security-rules/security-rules-api-client.spec.ts index c4176540a6..c3599af8d0 100644 --- a/test/unit/security-rules/security-rules-api-client.spec.ts +++ b/test/unit/security-rules/security-rules-api-client.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2019 Google Inc. + * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/security-rules/security-rules.spec.ts b/test/unit/security-rules/security-rules.spec.ts index 380583d4cd..e35b2d07b6 100644 --- a/test/unit/security-rules/security-rules.spec.ts +++ b/test/unit/security-rules/security-rules.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2019 Google Inc. + * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/storage/index.spec.ts b/test/unit/storage/index.spec.ts index 7924f8a61c..7fe4161cf8 100644 --- a/test/unit/storage/index.spec.ts +++ b/test/unit/storage/index.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/storage/storage.spec.ts b/test/unit/storage/storage.spec.ts index ea656a1e92..14653985fb 100644 --- a/test/unit/storage/storage.spec.ts +++ b/test/unit/storage/storage.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/utils.ts b/test/unit/utils.ts index 6ff00d5e26..033015b96b 100644 --- a/test/unit/utils.ts +++ b/test/unit/utils.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/utils/api-request.spec.ts b/test/unit/utils/api-request.spec.ts index 642d011460..4636878f6b 100644 --- a/test/unit/utils/api-request.spec.ts +++ b/test/unit/utils/api-request.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/utils/crypto-signer.spec.ts b/test/unit/utils/crypto-signer.spec.ts index cf38160a2f..a964f00f7b 100644 --- a/test/unit/utils/crypto-signer.spec.ts +++ b/test/unit/utils/crypto-signer.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/utils/error.spec.ts b/test/unit/utils/error.spec.ts index c2321005ee..3bc58c9173 100644 --- a/test/unit/utils/error.spec.ts +++ b/test/unit/utils/error.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/utils/index.spec.ts b/test/unit/utils/index.spec.ts index 5226353107..d6e0ddc06c 100644 --- a/test/unit/utils/index.spec.ts +++ b/test/unit/utils/index.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/utils/jwt.spec.ts b/test/unit/utils/jwt.spec.ts index 8dd49dfaf6..987faf5e47 100644 --- a/test/unit/utils/jwt.spec.ts +++ b/test/unit/utils/jwt.spec.ts @@ -1,5 +1,5 @@ /*! - * Copyright 2021 Google Inc. + * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/test/unit/utils/validator.spec.ts b/test/unit/utils/validator.spec.ts index a2f86c83c2..f059839ca9 100644 --- a/test/unit/utils/validator.spec.ts +++ b/test/unit/utils/validator.spec.ts @@ -1,6 +1,6 @@ /*! * @license - * Copyright 2017 Google Inc. + * Copyright 2017 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 40325df56f44b03de5ae03507fd15072805e8f7c Mon Sep 17 00:00:00 2001 From: Stephen Rosa <84193009+stephenarosaj@users.noreply.github.com> Date: Wed, 17 Sep 2025 11:50:16 -0700 Subject: [PATCH 02/10] FDC: update api version, integration tests, and CONTRIBUTING.md (#2972) * update integration tests and CONTRIBUTING.md * make test data more readable, tweak contributing instructions * update api to v1 * check in dataconnect folder * address comments * remove .dataconnect folder * update .gitignore * make integration tests more robust, explicitly setting and clearing database state --- .gitignore | 3 + CONTRIBUTING.md | 16 + .../data-connect-api-client-internal.ts | 2 +- test/integration/data-connect.spec.ts | 348 +++++++++++------- .../dataconnect/dataconnect/dataconnect.yaml | 13 + .../dataconnect/my-connector/connector.yaml | 1 + .../dataconnect/my-connector/mutations.gql | 95 +++++ .../dataconnect/my-connector/queries.gql | 74 ++++ .../dataconnect/dataconnect/schema/schema.gql | 13 + test/integration/dataconnect/firebase.json | 5 + .../data-connect-api-client-internal.spec.ts | 4 +- 11 files changed, 438 insertions(+), 136 deletions(-) create mode 100644 test/integration/dataconnect/dataconnect/dataconnect.yaml create mode 100644 test/integration/dataconnect/dataconnect/my-connector/connector.yaml create mode 100644 test/integration/dataconnect/dataconnect/my-connector/mutations.gql create mode 100644 test/integration/dataconnect/dataconnect/my-connector/queries.gql create mode 100644 test/integration/dataconnect/dataconnect/schema/schema.gql create mode 100644 test/integration/dataconnect/firebase.json diff --git a/.gitignore b/.gitignore index 9331c650de..141eeb7f06 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,6 @@ test/resources/appid.txt firebase-admin-*.tgz docgen/markdown/ + +# Dataconnect integration test artifacts should not be checked in +test/integration/dataconnect/dataconnect/.dataconnect \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 28d15bf290..4a7df3f715 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -245,6 +245,22 @@ to ensure that exported user records contain the password hashes of the user acc 3. Click **ADD ANOTHER ROLE** and choose **Firebase Authentication Admin**. 4. Click **SAVE**. +9. Setup your project for Firebase Data Connect integration tests: + 1. Set up Data Connect in the Firebase Console: + 1. Go to the Firebase Console, and select **Data Connect** from the **Build** menu. + 2. Click on **Get Started**. You can skip any Gemini generation. + 3. Select **Create a new Cloud SQL instance**. + 4. Set your **Location** to `us-west2` + 5. Set your **Cloud SQL instance ID** to `my-instance` + 6. Set your **Database name** to `my-database` + 7. Set your **Service ID** to `my-service` + 8. Click **Submit**. This operation may take up to 10 minutes to complete - you may + continue setting up while this completes. + 2. Run the following command from the root of the Node Admin SDK repo: + ```bash + $ firebase deploy --only dataconnect:my-service:my-connector --config ./test/integration/dataconnect/firebase.json --project + ``` + 1. If you're asked if you'd like to execute changes, select `Execute all`. Finally, to run the integration test suite: diff --git a/src/data-connect/data-connect-api-client-internal.ts b/src/data-connect/data-connect-api-client-internal.ts index c100abd023..3ef99bacea 100644 --- a/src/data-connect/data-connect-api-client-internal.ts +++ b/src/data-connect/data-connect-api-client-internal.ts @@ -25,7 +25,7 @@ import * as utils from '../utils/index'; import * as validator from '../utils/validator'; import { ConnectorConfig, ExecuteGraphqlResponse, GraphqlOptions } from './data-connect-api'; -const API_VERSION = 'v1alpha'; +const API_VERSION = 'v1'; /** The Firebase Data Connect backend base URL format. */ const FIREBASE_DATA_CONNECT_BASE_URL_FORMAT = diff --git a/test/integration/data-connect.spec.ts b/test/integration/data-connect.spec.ts index 8ab24925df..4cf74549cf 100644 --- a/test/integration/data-connect.spec.ts +++ b/test/integration/data-connect.spec.ts @@ -17,68 +17,79 @@ import * as chai from 'chai'; import * as chaiAsPromised from 'chai-as-promised'; import { getDataConnect, ConnectorConfig, GraphqlOptions } from '../../lib/data-connect/index'; +import firebase from '@firebase/app-compat'; +import { apiKey, projectId } from './setup'; chai.should(); chai.use(chaiAsPromised); const expect = chai.expect; -/* -// Schema -type User @table(key: "uid") { - uid: String! - name: String! - address: String! -} -*/ +/** + * // Schema + * type User @table(key: ["id"]) { + * id: String! + * name: String! + * address: String! + * } + */ type User = { - uid?: string; - name?: string; - address?: string; + id: string; + name: string; + address: string; // Generated emails_on_from?: Email[]; }; -/* -// Schema -type Email @table { - subject: String! - date: Date! - text: String! - from: User! -} +/** + * // Schema + * type Email @table { + * id: String! + * subject: String! + * date: Date! + * text: String! + * from: User! + * } */ type Email = { - subject?: string; - date?: string; - text?: string; - from?: User; - // Generated - id?: string; + subject: string; + date: string; + text: string; + from: User; + id: string; }; -interface UserResponse { +interface GetUserResponse { user: User; } -interface UsersResponse { +interface ListUsersResponse { users: User[]; } interface UserUpsertResponse { - user_upsert: { uid: string; }; + user_upsert: { id: string; }; } interface UserUpdateResponse { - user_update: { uid: string; }; + user_update: { id: string; }; } -interface EmailsResponse { +interface EmailUpsertResponse { + email_upsert: { id: string; }; +} + +interface ListEmailsResponse { emails: Email[]; } -interface UserVariables { - id: { uid: string; }; +interface GetUserVariables { + id: { id: string; }; +} + +interface DeleteResponse { + email_deleteMany: number + user_deleteMany: number } const connectorConfig: ConnectorConfig = { @@ -86,117 +97,195 @@ const connectorConfig: ConnectorConfig = { serviceId: 'my-service', }; -const userId = 'QVBJcy5ndXJ3'; +const fredUser = { id: 'fred_id', address: '32 Elm St.', name: 'Fred' } +const fredrickUser = { id: fredUser.id, address: '64 Elm St. North', name: 'Fredrick' } + +const jeffUser = { id: 'jeff_id', address: '99 Oak St.', name: 'Jeff' } + +const fredEmail = { + id: 'email_id', + subject: 'free bitcoin inside', + date: '1999-12-31', + text: 'get pranked! LOL!', + from: { id: fredUser.id } +} describe('getDataConnect()', () => { - const queryListUsers = 'query ListUsers @auth(level: PUBLIC) { users { uid, name, address } }'; - const queryListEmails = 'query ListEmails @auth(level: NO_ACCESS) { emails { id subject text date from { name } } }'; - const queryGetUserById = 'query GetUser($id: User_Key!) { user(key: $id) { uid name } }'; + before(() => { + firebase.initializeApp({ + apiKey, + authDomain: projectId + '.firebaseapp.com', + }); + }); + + afterEach(async () => { + await cleanupDatabase(); + }) + + beforeEach(async () => { + await initializeDatabase(); + }); + + /** initial state of database after calling initializeDatabase() */ + const initialState = { users: [fredUser, jeffUser], emails: [fredEmail] }; + + /** helper function which sets initial state of the database before each test */ + async function initializeDatabase(): Promise { + await getDataConnect(connectorConfig).executeGraphql( + upsertFredUser + ); + await getDataConnect(connectorConfig).executeGraphql( + upsertJeffUser + ); + await getDataConnect(connectorConfig).executeGraphql( + upsertFredEmail + ); + } + /** helper function which clears state of the database after each test */ + async function cleanupDatabase(): Promise { + await getDataConnect(connectorConfig).executeGraphql(deleteAll); + } + /** @auth(level: PUBLIC) */ + const queryListUsers = 'query ListUsers @auth(level: PUBLIC) { users { id, name, address } }'; + /** @auth(level: NO_ACCESS) */ + const queryListEmails = + 'query ListEmails @auth(level: NO_ACCESS) { emails { id subject text date from { id } } }'; + /** no @auth specified - default permissions */ + const queryGetUserById = 'query GetUser($id: User_Key!) { user(key: $id) { id name address } }'; + + /** @auth(level: USER) */ const queryListUsersImpersonation = ` query ListUsers @auth(level: USER) { - users(where: { uid: { eq_expr: "auth.uid" } }) { uid, name, address } + users(where: { id: { eq_expr: "auth.uid" } }) { id, name, address } }`; - + const multipleQueries = ` - ${queryListUsers} - ${queryListEmails} + ${queryListUsers} + ${queryListEmails} `; - const mutation = `mutation user { user_insert(data: {uid: "${userId}", address: "32 St", name: "Fred Car"}) }`; - - const updateImpersonatedUser = ` - mutation UpdateUser @auth(level: USER) { - user_update(key: { uid_expr: "auth.uid" }, data: { address: "32 Elm St.", name: "Fredrick" }) + /** hardcoded upsert fredUser query, with non-impersonateable id */ + const upsertFredUser = + `mutation user { + user_upsert(data: {id: "${fredUser.id}", address: "${fredUser.address}", name: "${fredUser.name}"}) }`; - const upsertUser = `mutation UpsertUser($id: String) { - user_upsert(data: { uid: $id, address: "32 St.", name: "Fred" }) }`; - - const testUser = { - name: 'Fred', - address: '32 St.', - uid: userId - } + /** hardcoded upsert fredrickUser query, with impersonateable id */ + const updateFredrickUserImpersonated = + `mutation upsertFredrickUserImpersonated @auth(level: USER) { + user_update( + key: { id_expr: "auth.uid" }, + data: { address: "${fredrickUser.address}", name: "${fredrickUser.name}" } + ) + }`; + + /** hardcoded upsert jeffUser query, with non-impersonateable id */ + const upsertJeffUser = + `mutation user { + user_upsert(data: {id: "${jeffUser.id}", address: "${jeffUser.address}", name: "${jeffUser.name}"}) + }`; - const expectedUsers = [ - testUser, - { - name: 'Jeff', - address: '99 Oak St. N', - uid: 'QVBJcy5ndXJ1' - } - ]; + /** hardcoded upsert fredEmail query, with non-impersonateable id */ + const upsertFredEmail = `mutation email { + email_upsert(data: { + id:"${fredEmail.id}", + subject: "${fredEmail.subject}", + date: "${fredEmail.date}", + text: "${fredEmail.text}", + fromId: "${fredEmail.from.id}" + }) + }`; + + /** hardcoded delete all mutation, for cleanup */ + const deleteAll = `mutation delete { + email_deleteMany(all: true) + user_deleteMany(all: true) + }` describe('executeGraphql()', () => { it('executeGraphql() successfully executes a GraphQL mutation', async () => { - const resp = await getDataConnect(connectorConfig).executeGraphql( - upsertUser, { variables: { id: userId } } + const fredResponse = await getDataConnect(connectorConfig).executeGraphql( + upsertFredUser + ); + //{ data: { user_insert: { id: 'fred_id' } } } + expect(fredResponse.data.user_upsert.id).to.be.not.empty; + expect(fredResponse.data.user_upsert.id).equals(fredUser.id); + + const jeffResponse = await getDataConnect(connectorConfig).executeGraphql( + upsertJeffUser + ); + //{ data: { user_insert: { id: 'jeff_id' } } } + expect(jeffResponse.data.user_upsert.id).to.be.not.empty; + expect(jeffResponse.data.user_upsert.id).equals(jeffUser.id); + + const emailResponse = await getDataConnect(connectorConfig).executeGraphql( + upsertFredEmail ); - //{ data: { user_insert: { uid: 'QVBJcy5ndXJ3' } } } - expect(resp.data.user_upsert.uid).to.be.not.empty; - expect(resp.data.user_upsert.uid).equals(userId); + //{ data: { email_upsert: { id: 'email_id' } } } + expect(emailResponse.data.email_upsert.id).to.be.not.empty; + + const deleteResponse = await getDataConnect(connectorConfig).executeGraphql(deleteAll); + expect(deleteResponse.data.email_deleteMany).to.be.greaterThan(0); + expect(deleteResponse.data.user_deleteMany).to.be.greaterThan(0); }); - it('executeGraphql() successfully executes a GraphQL', async () => { - const resp = await getDataConnect(connectorConfig).executeGraphql(queryListUsers); + it('executeGraphql() successfully executes a GraphQL query', async () => { + const resp = await getDataConnect(connectorConfig) + .executeGraphql(queryListUsers); expect(resp.data.users).to.be.not.empty; - expect(resp.data.users.length).to.be.greaterThan(1); - expectedUsers.forEach((expectedUser) => { - expect(resp.data.users).to.deep.include(expectedUser); + expect(resp.data.users.length).to.equal(initialState.users.length); + resp.data.users.forEach((user) => { + expect(initialState.users).to.deep.include(user); }); }); it('executeGraphql() use the operationName when multiple queries are provided', async () => { - const resp = await getDataConnect(connectorConfig).executeGraphql( + const resp = await getDataConnect(connectorConfig).executeGraphql( multipleQueries, { operationName: 'ListEmails' } ); - expect(resp.data.emails).to.be.not.empty; - expect(resp.data.emails.length).equals(1); - expect(resp.data.emails[0].id).to.be.not.undefined; - expect(resp.data.emails[0].from?.name).to.equal('Jeff'); + expect(resp.data.emails).to.not.be.empty; + expect(resp.data.emails).to.deep.equal(initialState.emails); }); - it('executeGraphql() should throw for a query error', async () => { - return getDataConnect(connectorConfig).executeGraphql(mutation) + it('executeGraphql() should throw for a query error when no variables are provided', async () => { + return getDataConnect(connectorConfig).executeGraphql(queryGetUserById) .should.eventually.be.rejected.and.have.property('code', 'data-connect/query-error'); }); it('executeGraphql() successfully executes a GraphQL query with variables', async () => { - const resp = await getDataConnect(connectorConfig).executeGraphql( + const resp = await getDataConnect(connectorConfig).executeGraphql( queryGetUserById, - { variables: { id: { uid: userId } } } + { variables: { id: { id: initialState.users[0].id } } } ); - expect(resp.data.user.uid).to.equal(testUser.uid); - expect(resp.data.user.name).to.equal(testUser.name); - expect(resp.data.user.address).to.be.undefined; + expect(resp.data.user).to.deep.equal(initialState.users[0]); }); }); describe('executeGraphqlRead()', () => { it('executeGraphqlRead() successfully executes a read-only GraphQL', async () => { - const resp = - await getDataConnect(connectorConfig).executeGraphqlRead(queryListUsers); + const resp = await getDataConnect(connectorConfig) + .executeGraphqlRead(queryListUsers); expect(resp.data.users).to.be.not.empty; - expect(resp.data.users.length).to.be.greaterThan(1); - expectedUsers.forEach((expectedUser) => { - expect(resp.data.users).to.deep.include(expectedUser); + expect(resp.data.users.length).to.equal(initialState.users.length); + resp.data.users.forEach((user) => { + expect(initialState.users).to.deep.include(user); }); }); it('executeGraphqlRead() should throw for a GraphQL mutation', async () => { - return getDataConnect(connectorConfig).executeGraphqlRead(mutation) + return getDataConnect(connectorConfig).executeGraphqlRead(upsertFredUser) .should.eventually.be.rejected.and.have.property('code', 'data-connect/permission-denied'); }); }); describe('Impersonation', () => { - const optsAuthorizedClaims: GraphqlOptions = { + const optsAuthorizedFredClaims: GraphqlOptions = { impersonate: { authClaims: { - sub: userId, + sub: fredUser.id, email_verified: true } } @@ -220,25 +309,27 @@ describe('getDataConnect()', () => { describe('USER Auth Policy', () => { it('executeGraphqlRead() successfully executes an impersonated query with authenticated claims', async () => { const resp = - await getDataConnect(connectorConfig).executeGraphqlRead( - queryListUsersImpersonation, optsAuthorizedClaims); + await getDataConnect(connectorConfig).executeGraphqlRead( + queryListUsersImpersonation, optsAuthorizedFredClaims); expect(resp.data.users).to.be.not.empty; expect(resp.data.users.length).equals(1); - expect(resp.data.users[0]).to.deep.equal(testUser); + expect(resp.data.users[0]).to.deep.equal(fredUser); }); it('executeGraphqlRead() should throw for impersonated query with unauthenticated claims', async () => { - return getDataConnect(connectorConfig).executeGraphqlRead(queryListUsersImpersonation, optsUnauthorizedClaims) + return getDataConnect(connectorConfig).executeGraphqlRead( + queryListUsersImpersonation, + optsUnauthorizedClaims + ) .should.eventually.be.rejected.and.have.property('code', 'data-connect/unauthenticated'); }); it('executeGraphql() successfully executes an impersonated query with authenticated claims', async () => { - const resp = - await getDataConnect(connectorConfig).executeGraphqlRead( - queryListUsersImpersonation, optsAuthorizedClaims); + const resp = await getDataConnect(connectorConfig).executeGraphql( + queryListUsersImpersonation, optsAuthorizedFredClaims); expect(resp.data.users).to.be.not.empty; expect(resp.data.users.length).equals(1); - expect(resp.data.users[0]).to.deep.equal(testUser); + expect(resp.data.users[0]).to.deep.equal(fredUser); }); it('executeGraphql() should throw for impersonated query with unauthenticated claims', async () => { @@ -249,7 +340,7 @@ describe('getDataConnect()', () => { it('executeGraphql() should return an empty list for an impersonated query with non-existing authenticated ' + 'claims', async () => { - const resp = await getDataConnect(connectorConfig).executeGraphql( + const resp = await getDataConnect(connectorConfig).executeGraphql( queryListUsersImpersonation, optsNonExistingClaims); // Should find no data expect(resp.data.users).to.be.empty; @@ -257,75 +348,66 @@ describe('getDataConnect()', () => { it('executeGraphql() successfully executes an impersonated mutation with authenticated claims', async () => { - const resp = await getDataConnect(connectorConfig).executeGraphql( - updateImpersonatedUser, optsAuthorizedClaims); + const updateResp = await getDataConnect(connectorConfig).executeGraphql( + updateFredrickUserImpersonated, optsAuthorizedFredClaims); // Fred -> Fredrick - expect(resp.data.user_update.uid).equals(userId); + expect(updateResp.data.user_update.id).equals(fredUser.id); + const queryResp = await getDataConnect(connectorConfig).executeGraphql( + queryGetUserById, { variables: { id: { id: fredUser.id } } }); + expect(queryResp.data.user).to.not.be.empty; + expect(queryResp.data.user).to.deep.equal(fredrickUser); }); it('executeGraphql() should throw for impersonated mutation with unauthenticated claims', async () => { - return getDataConnect(connectorConfig).executeGraphql(updateImpersonatedUser, optsUnauthorizedClaims) + return getDataConnect(connectorConfig).executeGraphql(updateFredrickUserImpersonated, optsUnauthorizedClaims) .should.eventually.be.rejected.and.has.property('code', 'data-connect/unauthenticated'); }); it('executeGraphql() should return null for an impersonated mutation with non-existing authenticated claims', async () => { const resp = await getDataConnect(connectorConfig).executeGraphql( - updateImpersonatedUser, optsNonExistingClaims); + updateFredrickUserImpersonated, optsNonExistingClaims); // Should mutate no data expect(resp.data.user_update).to.be.null; }); }); describe('PUBLIC Auth Policy', () => { - const expectedUsers = [ - { - name: 'Fredrick', - address: '32 Elm St.', - uid: userId - }, - { - name: 'Jeff', - address: '99 Oak St. N', - uid: 'QVBJcy5ndXJ1' - } - ]; - it('executeGraphql() successfully executes an impersonated query with authenticated claims', async () => { - const resp = await getDataConnect(connectorConfig).executeGraphql( - queryListUsers, optsAuthorizedClaims); + const resp = await getDataConnect(connectorConfig).executeGraphql( + queryListUsers, optsAuthorizedFredClaims); expect(resp.data.users).to.be.not.empty; - expect(resp.data.users.length).to.be.greaterThan(1); - expectedUsers.forEach((expectedUser) => { - expect(resp.data.users).to.deep.include(expectedUser); - }) + expect(resp.data.users.length).to.equal(initialState.users.length); + resp.data.users.forEach((user) => { + expect(initialState.users).to.deep.include(user); + }); }); it('executeGraphql() successfully executes an impersonated query with unauthenticated claims', async () => { - const resp = await getDataConnect(connectorConfig).executeGraphql( + const resp = await getDataConnect(connectorConfig).executeGraphql( queryListUsers, optsUnauthorizedClaims); expect(resp.data.users).to.be.not.empty; - expect(resp.data.users.length).to.be.greaterThan(1); - expectedUsers.forEach((expectedUser) => { - expect(resp.data.users).to.deep.include(expectedUser); + expect(resp.data.users.length).to.equal(initialState.users.length); + resp.data.users.forEach((user) => { + expect(initialState.users).to.deep.include(user); }); }); it('executeGraphql() successfully executes an impersonated query with non-existing authenticated claims', async () => { - const resp = await getDataConnect(connectorConfig).executeGraphql( + const resp = await getDataConnect(connectorConfig).executeGraphql( queryListUsers, optsNonExistingClaims); expect(resp.data.users).to.be.not.empty; - expect(resp.data.users.length).to.be.greaterThan(1); - expectedUsers.forEach((expectedUser) => { - expect(resp.data.users).to.deep.include(expectedUser); + expect(resp.data.users.length).to.equal(initialState.users.length); + resp.data.users.forEach((user) => { + expect(initialState.users).to.deep.include(user); }); }); }); describe('NO_ACCESS Auth Policy', () => { it('executeGraphql() should throw for an impersonated query with authenticated claims', async () => { - return await getDataConnect(connectorConfig).executeGraphql(queryListEmails, optsAuthorizedClaims) + return await getDataConnect(connectorConfig).executeGraphql(queryListEmails, optsAuthorizedFredClaims) .should.eventually.be.rejected.and.has.property('code', 'data-connect/permission-denied'); }); diff --git a/test/integration/dataconnect/dataconnect/dataconnect.yaml b/test/integration/dataconnect/dataconnect/dataconnect.yaml new file mode 100644 index 0000000000..2f5b815b88 --- /dev/null +++ b/test/integration/dataconnect/dataconnect/dataconnect.yaml @@ -0,0 +1,13 @@ +specVersion: "v1" +serviceId: "my-service" +location: "us-west2" +schema: + source: "./schema" + datasource: + postgresql: + database: "my-database" + cloudSql: + instanceId: "my-instance" + # schemaValidation: "STRICT" # STRICT mode makes Postgres schema match Data Connect exactly. + # schemaValidation: "COMPATIBLE" # COMPATIBLE mode makes Postgres schema compatible with Data Connect. +connectorDirs: ["./my-connector"] diff --git a/test/integration/dataconnect/dataconnect/my-connector/connector.yaml b/test/integration/dataconnect/dataconnect/my-connector/connector.yaml new file mode 100644 index 0000000000..3b1bcdcc91 --- /dev/null +++ b/test/integration/dataconnect/dataconnect/my-connector/connector.yaml @@ -0,0 +1 @@ +connectorId: "my-connector" diff --git a/test/integration/dataconnect/dataconnect/my-connector/mutations.gql b/test/integration/dataconnect/dataconnect/my-connector/mutations.gql new file mode 100644 index 0000000000..10a870880e --- /dev/null +++ b/test/integration/dataconnect/dataconnect/my-connector/mutations.gql @@ -0,0 +1,95 @@ +mutation upsertFredUser @auth(level: NO_ACCESS) { + user_upsert(data: { id: "fred_id", address: "32 Elm St.", name: "Fred" }) +} +mutation updateFredrickUserImpersonation @auth(level: USER) { + user_update( + key: { id_expr: "auth.uid" } + data: { address: "64 Elm St. North", name: "Fredrick" } + ) +} +mutation upsertJeffUser @auth(level: NO_ACCESS) { + user_upsert(data: { id: "jeff_id", address: "99 Oak St.", name: "Jeff" }) +} + +mutation upsertJeffEmail @auth(level: NO_ACCESS) { + email_upsert( + data: { + id: "jeff_email_id" + subject: "free bitcoin inside" + date: "1999-12-31" + text: "get pranked! LOL!" + fromId: "jeff_id" + } + ) +} + +mutation InsertEmailPublic($id: String!) +@auth(level: PUBLIC, insecureReason: "test") { + email_insert( + data: { + id: $id + subject: "PublicEmail" + date: "1999-12-31" + text: "PublicEmail" + fromId: "jeff_id" + } + ) +} +mutation InsertEmailUserAnon($id: String!) +@auth(level: USER_ANON, insecureReason: "test") { + email_insert( + data: { + id: $id + subject: "UserAnonEmail" + date: "1999-12-31" + text: "UserAnonEmail" + fromId: "jeff_id" + } + ) +} +mutation InsertEmailUser($id: String!) +@auth(level: USER, insecureReason: "test") { + email_insert( + data: { + id: $id + subject: "UserEmail" + date: "1999-12-31" + text: "UserEmail" + fromId: "jeff_id" + } + ) +} +mutation InsertEmailUserEmailVerified($id: String!) +@auth(level: USER_EMAIL_VERIFIED, insecureReason: "test") { + email_insert( + data: { + id: $id + subject: "UserEmailVerifiedEmail" + date: "1999-12-31" + text: "UserEmailVerifiedEmail" + fromId: "jeff_id" + } + ) +} +mutation InsertEmailNoAccess($id: String!) @auth(level: NO_ACCESS) { + email_insert( + data: { + id: $id + subject: "NoAccessEmail" + date: "1999-12-31" + text: "NoAccessEmail" + fromId: "jeff_id" + } + ) +} +mutation InsertEmailImpersonation($id: String!) @auth(level: NO_ACCESS) { + email_insert( + data: { + id: $id + subject: "ImpersonatedEmail" + date: "1999-12-31" + text: "ImpersonatedEmail" + fromId_expr: "auth.uid" + } + ) +} diff --git a/test/integration/dataconnect/dataconnect/my-connector/queries.gql b/test/integration/dataconnect/dataconnect/my-connector/queries.gql new file mode 100644 index 0000000000..b93da41828 --- /dev/null +++ b/test/integration/dataconnect/dataconnect/my-connector/queries.gql @@ -0,0 +1,74 @@ +query ListUsersPublic @auth(level: PUBLIC, insecureReason: "test") { + users { + id + name + address + } +} +query ListUsersUserAnon @auth(level: USER_ANON, insecureReason: "test") { + users { + id + name + address + } +} +query ListUsersUser @auth(level: USER, insecureReason: "test") { + users { + id + name + address + } +} +query ListUsersUserEmailVerified +@auth(level: USER_EMAIL_VERIFIED, insecureReason: "test") { + users { + id + name + address + } +} +query ListUsersNoAccess @auth(level: NO_ACCESS) { + users { + id + name + address + } +} +query ListUsersImpersonationAnon @auth(level: USER_ANON) { + users(where: { id: { eq_expr: "auth.uid" } }) { + id + name + address + } +} +query GetUser($id: User_Key!) @auth(level: NO_ACCESS) { + user(key: $id) { + id + name + } +} + +query ListEmails @auth(level: NO_ACCESS) { + emails { + id + subject + text + date + from { + name + } + } +} +query GetEmail($id: String!) @auth(level: NO_ACCESS) { + email(id: $id) { + id + subject + date + text + from { + id + name + address + } + } +} diff --git a/test/integration/dataconnect/dataconnect/schema/schema.gql b/test/integration/dataconnect/dataconnect/schema/schema.gql new file mode 100644 index 0000000000..1f390b316e --- /dev/null +++ b/test/integration/dataconnect/dataconnect/schema/schema.gql @@ -0,0 +1,13 @@ +type User @table(key: ["id"]) { + id: String! + name: String! + address: String! +} + +type Email @table { + id: String! + subject: String! + date: Date! + text: String! + from: User! +} diff --git a/test/integration/dataconnect/firebase.json b/test/integration/dataconnect/firebase.json new file mode 100644 index 0000000000..73f599717a --- /dev/null +++ b/test/integration/dataconnect/firebase.json @@ -0,0 +1,5 @@ +{ + "dataconnect": { + "source": "dataconnect" + } +} diff --git a/test/unit/data-connect/data-connect-api-client-internal.spec.ts b/test/unit/data-connect/data-connect-api-client-internal.spec.ts index b23c5e144d..8086802861 100644 --- a/test/unit/data-connect/data-connect-api-client-internal.spec.ts +++ b/test/unit/data-connect/data-connect-api-client-internal.spec.ts @@ -207,7 +207,7 @@ describe('DataConnectApiClient', () => { expect(resp.data.users).to.deep.equal(TEST_RESPONSE.data.users); expect(stub).to.have.been.calledOnce.and.calledWith({ method: 'POST', - url: `https://firebasedataconnect.googleapis.com/v1alpha/projects/test-project/locations/${connectorConfig.location}/services/${connectorConfig.serviceId}:executeGraphql`, + url: `https://firebasedataconnect.googleapis.com/v1/projects/test-project/locations/${connectorConfig.location}/services/${connectorConfig.serviceId}:executeGraphql`, headers: EXPECTED_HEADERS, data: { query: 'query' } }); @@ -223,7 +223,7 @@ describe('DataConnectApiClient', () => { .then(() => { expect(stub).to.have.been.calledOnce.and.calledWith({ method: 'POST', - url: `http://localhost:9399/v1alpha/projects/test-project/locations/${connectorConfig.location}/services/${connectorConfig.serviceId}:executeGraphql`, + url: `http://localhost:9399/v1/projects/test-project/locations/${connectorConfig.location}/services/${connectorConfig.serviceId}:executeGraphql`, headers: EMULATOR_EXPECTED_HEADERS, data: { query: 'query' } }); From e72c0cd40cf1c5f1288a0f2c2d44184440b13dbb Mon Sep 17 00:00:00 2001 From: Maneesh Tewani Date: Thu, 2 Oct 2025 10:53:41 -0700 Subject: [PATCH 03/10] chore(dc): Implement gen tracking (#2985) --- package.json | 2 ++ .../data-connect-api-client-internal.ts | 25 ++++++++++--- src/data-connect/data-connect.ts | 8 +++++ src/utils/api-request.ts | 8 +++-- .../data-connect-api-client-internal.spec.ts | 35 +++++++++++++++++++ 5 files changed, 72 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index ffd2a8da9e..bf0126d792 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,9 @@ "test:integration": "mocha test/integration/*.ts --slow 5000 --timeout 20000 --require ts-node/register", "test:coverage": "nyc npm run test:unit", "lint:src": "eslint src/ --ext .ts", + "lint:src:fix": "eslint src/ --ext .ts --fix", "lint:test": "eslint test/ --ext .ts", + "lint:test:fix": "eslint test/ --ext .ts --fix", "apidocs": "run-s api-extractor:local api-documenter", "api-extractor": "node generate-reports.js", "api-extractor:local": "npm run build && node generate-reports.js --local", diff --git a/src/data-connect/data-connect-api-client-internal.ts b/src/data-connect/data-connect-api-client-internal.ts index 3ef99bacea..79a72431ac 100644 --- a/src/data-connect/data-connect-api-client-internal.ts +++ b/src/data-connect/data-connect-api-client-internal.ts @@ -38,9 +38,17 @@ const FIREBASE_DATA_CONNECT_EMULATOR_BASE_URL_FORMAT = const EXECUTE_GRAPH_QL_ENDPOINT = 'executeGraphql'; const EXECUTE_GRAPH_QL_READ_ENDPOINT = 'executeGraphqlRead'; -const DATA_CONNECT_CONFIG_HEADERS = { - 'X-Firebase-Client': `fire-admin-node/${utils.getSdkVersion()}` -}; + +function getHeaders(isUsingGen: boolean): { [key: string]: string } { + const headerValue = { + 'X-Firebase-Client': `fire-admin-node/${utils.getSdkVersion()}`, + 'X-Goog-Api-Client': utils.getMetricsHeader(), + }; + if (isUsingGen) { + headerValue['X-Goog-Api-Client'] += ' admin-js/gen'; + } + return headerValue; +} /** * Class that facilitates sending requests to the Firebase Data Connect backend API. @@ -50,6 +58,7 @@ const DATA_CONNECT_CONFIG_HEADERS = { export class DataConnectApiClient { private readonly httpClient: HttpClient; private projectId?: string; + private isUsingGen = false; constructor(private readonly connectorConfig: ConnectorConfig, private readonly app: App) { if (!validator.isNonNullObject(app) || !('options' in app)) { @@ -59,6 +68,14 @@ export class DataConnectApiClient { } this.httpClient = new DataConnectHttpClient(app as FirebaseApp); } + + /** + * Update whether the SDK is using a generated one or not. + * @param isUsingGen + */ + setIsUsingGen(isUsingGen: boolean): void { + this.isUsingGen = isUsingGen; + } /** * Execute arbitrary GraphQL, including both read and write queries @@ -117,7 +134,7 @@ export class DataConnectApiClient { const request: HttpRequestConfig = { method: 'POST', url, - headers: DATA_CONNECT_CONFIG_HEADERS, + headers: getHeaders(this.isUsingGen), data, }; const resp = await this.httpClient.send(request); diff --git a/src/data-connect/data-connect.ts b/src/data-connect/data-connect.ts index a689423257..366b756327 100644 --- a/src/data-connect/data-connect.ts +++ b/src/data-connect/data-connect.ts @@ -72,6 +72,14 @@ export class DataConnect { this.client = new DataConnectApiClient(connectorConfig, app); } + /** + * @param isUsingGen + * @internal + */ + useGen(isUsingGen: boolean): void { + this.client.setIsUsingGen(isUsingGen); + } + /** * Execute an arbitrary GraphQL query or mutation * diff --git a/src/utils/api-request.ts b/src/utils/api-request.ts index 3e5aa754d8..6e95619e6d 100644 --- a/src/utils/api-request.ts +++ b/src/utils/api-request.ts @@ -1092,7 +1092,9 @@ export class AuthorizedHttpClient extends HttpClient { requestCopy.httpAgent = this.app.options.httpAgent; } - requestCopy.headers['X-Goog-Api-Client'] = getMetricsHeader() + if (!requestCopy.headers['X-Goog-Api-Client']) { + requestCopy.headers['X-Goog-Api-Client'] = getMetricsHeader() + } return super.send(requestCopy); }); @@ -1126,7 +1128,9 @@ export class AuthorizedHttp2Client extends Http2Client { requestCopy.headers['x-goog-user-project'] = quotaProjectId; } - requestCopy.headers['X-Goog-Api-Client'] = getMetricsHeader() + if (!requestCopy.headers['X-Goog-Api-Client']) { + requestCopy.headers['X-Goog-Api-Client'] = getMetricsHeader() + } return super.send(requestCopy); }); diff --git a/test/unit/data-connect/data-connect-api-client-internal.spec.ts b/test/unit/data-connect/data-connect-api-client-internal.spec.ts index 8086802861..67c1541cd6 100644 --- a/test/unit/data-connect/data-connect-api-client-internal.spec.ts +++ b/test/unit/data-connect/data-connect-api-client-internal.spec.ts @@ -46,6 +46,12 @@ describe('DataConnectApiClient', () => { 'X-Goog-Api-Client': getMetricsHeader(), }; + const EXPECTED_HEADERS_WITH_GEN = { + 'Authorization': 'Bearer mock-token', + 'X-Firebase-Client': `fire-admin-node/${getSdkVersion()}`, + 'X-Goog-Api-Client': getMetricsHeader() + ' admin-js/gen', + }; + const EMULATOR_EXPECTED_HEADERS = { 'Authorization': 'Bearer owner', 'X-Firebase-Client': `fire-admin-node/${getSdkVersion()}`, @@ -229,6 +235,35 @@ describe('DataConnectApiClient', () => { }); }); }); + it('should use gen headers if set on success', () => { + interface UsersResponse { + users: [ + user: { + id: string; + name: string; + address: string; + } + ]; + } + apiClient.setIsUsingGen(true); + const stub = sandbox + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom(TEST_RESPONSE, 200)); + return apiClient.executeGraphql('query', {}) + .then((resp) => { + expect(resp.data.users).to.be.not.empty; + expect(resp.data.users[0].name).to.be.not.undefined; + expect(resp.data.users[0].address).to.be.not.undefined; + expect(resp.data.users).to.deep.equal(TEST_RESPONSE.data.users); + expect(stub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: `https://firebasedataconnect.googleapis.com/v1/projects/test-project/locations/${connectorConfig.location}/services/${connectorConfig.serviceId}:executeGraphql`, + headers: EXPECTED_HEADERS_WITH_GEN, + data: { query: 'query' } + }); + apiClient.setIsUsingGen(false); + }); + }); }); }); From 8c9895e87485f5aa686f809b80fd0fb854d25035 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 18:13:52 +0000 Subject: [PATCH 04/10] build(deps): bump axios in /.github/actions/send-email (#2983) --- .github/actions/send-email/package-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/actions/send-email/package-lock.json b/.github/actions/send-email/package-lock.json index 2b2d9b2449..11a686e946 100644 --- a/.github/actions/send-email/package-lock.json +++ b/.github/actions/send-email/package-lock.json @@ -57,13 +57,13 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", - "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", + "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, From 175ca9e073ddd5d4350f7224a7651b6b2abd4b54 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 20:19:20 +0000 Subject: [PATCH 05/10] build(deps-dev): bump @firebase/api-documenter from 0.4.0 to 0.5.0 (#3000) --- package-lock.json | 102 +++++++++++++++++++++++++++++++++++----------- package.json | 2 +- 2 files changed, 79 insertions(+), 25 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5d6cea5157..bea64ae6d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "firebase-admin", - "version": "13.4.0", + "version": "13.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "firebase-admin", - "version": "13.4.0", + "version": "13.5.0", "license": "Apache-2.0", "dependencies": { "@fastify/busboy": "^3.0.0", @@ -22,7 +22,7 @@ "uuid": "^11.0.2" }, "devDependencies": { - "@firebase/api-documenter": "^0.4.0", + "@firebase/api-documenter": "^0.5.0", "@firebase/app-compat": "^0.2.1", "@firebase/auth-compat": "^0.6.0", "@firebase/auth-types": "^0.13.0", @@ -510,15 +510,15 @@ "license": "MIT" }, "node_modules/@firebase/api-documenter": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@firebase/api-documenter/-/api-documenter-0.4.0.tgz", - "integrity": "sha512-UUPxxj1wIAGkXBCF9UL1dhfyzM4Lcd/gMSCoJqy8o75rLqQVGB2GSBaU7I4JmQsQhIIk/nKKLiy4HM/70kEfZA==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/api-documenter/-/api-documenter-0.5.0.tgz", + "integrity": "sha512-7fMC0sVInXsatjWMJG45W6ZQBtRcHh5D4oVpLXzKWBe/0TbRyBSE/ApecH9mn48XZGSs8/8Y0k7mKrLsXPLprA==", "dev": true, "license": "Apache-2.0", "dependencies": { "@microsoft/tsdoc": "0.12.24", - "@rushstack/node-core-library": "3.59.7", - "@rushstack/ts-command-line": "4.15.2", + "@rushstack/node-core-library": "3.66.1", + "@rushstack/ts-command-line": "4.23.3", "api-extractor-model-me": "0.1.1", "colors": "~1.4.0", "js-yaml": "4.1.0", @@ -527,6 +527,9 @@ }, "bin": { "api-documenter-fire": "dist/start.js" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@firebase/app": { @@ -1753,9 +1756,9 @@ "optional": true }, "node_modules/@rushstack/node-core-library": { - "version": "3.59.7", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.59.7.tgz", - "integrity": "sha512-ln1Drq0h+Hwa1JVA65x5mlSgUrBa1uHL+V89FqVWQgXd1vVIMhrtqtWGQrhTnFHxru5ppX+FY39VWELF/FjQCw==", + "version": "3.66.1", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.66.1.tgz", + "integrity": "sha512-ker69cVKAoar7MMtDFZC4CzcDxjwqIhFzqEnYI5NRN/8M3om6saWCVx/A7vL2t/jFCJsnzQplRDqA7c78pytng==", "dev": true, "license": "MIT", "dependencies": { @@ -1897,26 +1900,77 @@ } }, "node_modules/@rushstack/ts-command-line": { - "version": "4.15.2", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.15.2.tgz", - "integrity": "sha512-5+C2uoJY8b+odcZD6coEe2XNC4ZjGB4vCMESbqW/8DHRWC/qIHfANdmN9F1wz/lAgxz72i7xRoVtPY2j7e4gpQ==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.23.3.tgz", + "integrity": "sha512-HazKL8fv4HMQMzrKJCrOrhyBPPdzk7iajUXgsASwjQ8ROo1cmgyqxt/k9+SdmrNLGE1zATgRqMUH3s/6smbRMA==", "dev": true, "license": "MIT", "dependencies": { + "@rushstack/terminal": "0.14.5", "@types/argparse": "1.0.38", "argparse": "~1.0.9", - "colors": "~1.2.1", "string-argv": "~0.3.1" } }, - "node_modules/@rushstack/ts-command-line/node_modules/colors": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz", - "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==", + "node_modules/@rushstack/ts-command-line/node_modules/@rushstack/node-core-library": { + "version": "5.10.2", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.10.2.tgz", + "integrity": "sha512-xOF/2gVJZTfjTxbo4BDj9RtQq/HFnrrKdtem4JkyRLnwsRz2UDTg8gA1/et10fBx5RxmZD9bYVGST69W8ME5OQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=0.1.90" + "dependencies": { + "ajv": "~8.13.0", + "ajv-draft-04": "~1.0.0", + "ajv-formats": "~3.0.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.22.1", + "semver": "~7.5.4" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/ts-command-line/node_modules/@rushstack/terminal": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.14.5.tgz", + "integrity": "sha512-TEOpNwwmsZVrkp0omnuTUTGZRJKTr6n6m4OITiNjkqzLAkcazVpwR1SOtBg6uzpkIBLgrcNHETqI8rbw3uiUfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rushstack/node-core-library": "5.10.2", + "supports-color": "~8.1.1" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/ts-command-line/node_modules/ajv": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, "node_modules/@sinonjs/commons": { @@ -12001,9 +12055,9 @@ } }, "node_modules/validator": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", - "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "version": "13.15.20", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.20.tgz", + "integrity": "sha512-KxPOq3V2LmfQPP4eqf3Mq/zrT0Dqp2Vmx2Bn285LwVahLc+CsxOM0crBHczm8ijlcjZ0Q5Xd6LW3z3odTPnlrw==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index bf0126d792..de6e640c84 100644 --- a/package.json +++ b/package.json @@ -223,7 +223,7 @@ "@google-cloud/storage": "^7.14.0" }, "devDependencies": { - "@firebase/api-documenter": "^0.4.0", + "@firebase/api-documenter": "^0.5.0", "@firebase/app-compat": "^0.2.1", "@firebase/auth-compat": "^0.6.0", "@firebase/auth-types": "^0.13.0", From 27c682a5080ba2080d9b6e73a5b34a6a3ad8a1a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 20:23:40 +0000 Subject: [PATCH 06/10] build(deps): bump @fastify/busboy from 3.1.1 to 3.2.0 (#2998) --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index bea64ae6d6..f2229fb9be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -504,9 +504,9 @@ } }, "node_modules/@fastify/busboy": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.1.1.tgz", - "integrity": "sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz", + "integrity": "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==", "license": "MIT" }, "node_modules/@firebase/api-documenter": { From c9a3ee39e04669a8ed7089a2af6fe331b07136c7 Mon Sep 17 00:00:00 2001 From: Stephen Rosa <84193009+stephenarosaj@users.noreply.github.com> Date: Mon, 3 Nov 2025 11:03:29 -0800 Subject: [PATCH 07/10] feat(dc): Add executeQuery and executeMutation APIs to Data Connect (#2979) # API Changes - Added `executeQuery()` and `executeMutation()` to `src/data-connect/data-connect.ts`. These allow users to call deployed operations with impersonated auth credentials. # Testing - New unit tests added which match the coverage of existing `executeGraphql*` APIs - New integration tests added which more than match the coverage of existing `executeGraphql*` APIs # Commits: * add in changes from stephenarosaj/fdc-impersonate * finish adding in changes from stephenarosaj/fdc-impersonate * update Google Inc. to Google LLC, run npm install; npm run build * run npm apidocs * remove public execute apis * convert executeOperation api to OperationRef(...).execute() api * remove internal client from operation refs * cleanup javadocs to address workflow failures * npm run apidocs * spread GraphqlOptions arguments in OperationRefs and executeOperation functions * convert unit tests to use spread args * convert integration tests to use spread args * add executeQuery test cases which do not provide impersonation options, bypassing auth policies * add executeMutation test cases which do not provide impersonation options, bypassing auth policies * run npm apidocs * address try/catch comment * address await and reject grouping comment * address getUrl comments * address insecureReason comment * convert autopush resources to prod * add RefOptions, [Operation,Query,Mutation]Ref, [Operation,Query,Mutation]Result to exported api * revert OperationRef.execute() API to executeOperation API * revert OperationRef.execute() API to executeOperation API * revert tests to use DataConnect.executeOperation() API instead of OperationRef.execute() API * revert package version * update executeOperation API to return executeOperationResponse * update comments * add invalidateAdminArgs to handle variadic JS executeOperation arguments * npm run apidocs for validateAdminArgs * update validateAdminArgs documentation * address validateAdminArgs and some test comments * update validate-admin-args and add tests, address existing test comments, revert package changes * update tests * update tests * address mutation test comments * address prod url comments * finally fixed unit tests * REALLY fixed unit tests * address comments, add DataConnect.executeQuery() and DataConnect.executeMutation() unit tests * make validateAdminArgs internal * address comments * address documentation comment * undo integration test changes * remove empty checks * remove length checks * remove foreach checks * address test comments * revert package version * revert package version to master * revert package version to master * update comments --- etc/firebase-admin.data-connect.api.md | 15 + .../data-connect-api-client-internal.ts | 294 +++++- src/data-connect/data-connect-api.ts | 34 +- src/data-connect/data-connect.ts | 96 +- src/data-connect/index.ts | 23 +- src/data-connect/validate-admin-args.ts | 91 ++ test/integration/data-connect.spec.ts | 947 ++++++++++++++---- .../dataconnect/my-connector/mutations.gql | 11 +- .../dataconnect/my-connector/queries.gql | 5 +- .../data-connect-api-client-internal.spec.ts | 477 +++++++-- test/unit/data-connect/data-connect.spec.ts | 226 +++++ .../data-connect/validate-admin-args.spec.ts | 271 +++++ test/unit/index.spec.ts | 2 + 13 files changed, 2142 insertions(+), 350 deletions(-) create mode 100644 src/data-connect/validate-admin-args.ts create mode 100644 test/unit/data-connect/data-connect.spec.ts create mode 100644 test/unit/data-connect/validate-admin-args.spec.ts diff --git a/etc/firebase-admin.data-connect.api.md b/etc/firebase-admin.data-connect.api.md index 8a1e7a042c..4ac9e170cd 100644 --- a/etc/firebase-admin.data-connect.api.md +++ b/etc/firebase-admin.data-connect.api.md @@ -13,6 +13,7 @@ export type AuthClaims = Partial; // @public export interface ConnectorConfig { + connector?: string; location: string; serviceId: string; } @@ -27,6 +28,10 @@ export class DataConnect { readonly connectorConfig: ConnectorConfig; executeGraphql(query: string, options?: GraphqlOptions): Promise>; executeGraphqlRead(query: string, options?: GraphqlOptions): Promise>; + executeMutation(name: string, options?: OperationOptions): Promise>; + executeMutation(name: string, variables: Variables, options?: OperationOptions): Promise>; + executeQuery(name: string, options?: OperationOptions): Promise>; + executeQuery(name: string, variables: Variables, options?: OperationOptions): Promise>; insert(tableName: string, variables: Variables): Promise>; insertMany>(tableName: string, variables: Variables): Promise>; upsert(tableName: string, variables: Variables): Promise>; @@ -38,6 +43,11 @@ export interface ExecuteGraphqlResponse { data: GraphqlResponse; } +// @public +export interface ExecuteOperationResponse { + data: GraphqlResponse; +} + // @public export function getDataConnect(connectorConfig: ConnectorConfig, app?: App): DataConnect; @@ -60,4 +70,9 @@ export interface ImpersonateUnauthenticated { unauthenticated: true; } +// @public +export interface OperationOptions { + impersonate?: ImpersonateAuthenticated | ImpersonateUnauthenticated; +} + ``` diff --git a/src/data-connect/data-connect-api-client-internal.ts b/src/data-connect/data-connect-api-client-internal.ts index 79a72431ac..28284db6a4 100644 --- a/src/data-connect/data-connect-api-client-internal.ts +++ b/src/data-connect/data-connect-api-client-internal.ts @@ -23,21 +23,44 @@ import { import { PrefixedFirebaseError } from '../utils/error'; import * as utils from '../utils/index'; import * as validator from '../utils/validator'; -import { ConnectorConfig, ExecuteGraphqlResponse, GraphqlOptions } from './data-connect-api'; +import { ConnectorConfig, ExecuteGraphqlResponse, GraphqlOptions, OperationOptions } from './data-connect-api'; const API_VERSION = 'v1'; - -/** The Firebase Data Connect backend base URL format. */ -const FIREBASE_DATA_CONNECT_BASE_URL_FORMAT = - 'https://firebasedataconnect.googleapis.com/{version}/projects/{projectId}/locations/{locationId}/services/{serviceId}:{endpointId}'; - -/** Firebase Data Connect base URl format when using the Data Connect emultor. */ -const FIREBASE_DATA_CONNECT_EMULATOR_BASE_URL_FORMAT = +const FIREBASE_DATA_CONNECT_PROD_URL = 'https://firebasedataconnect.googleapis.com'; + +/** The Firebase Data Connect backend service URL format. */ +const FIREBASE_DATA_CONNECT_SERVICES_URL_FORMAT = + FIREBASE_DATA_CONNECT_PROD_URL + + '/{version}' + + '/projects/{projectId}' + + '/locations/{locationId}' + + '/services/{serviceId}' + + ':{endpointId}'; + +/** The Firebase Data Connect backend connector URL format. */ +const FIREBASE_DATA_CONNECT_CONNECTORS_URL_FORMAT = + FIREBASE_DATA_CONNECT_PROD_URL + + '/{version}' + + '/projects/{projectId}' + + '/locations/{locationId}' + + '/services/{serviceId}' + + '/connectors/{connectorId}' + + ':{endpointId}'; + +/** Firebase Data Connect service URL format when using the Data Connect emulator. */ +const FIREBASE_DATA_CONNECT_EMULATOR_SERVICES_URL_FORMAT = 'http://{host}/{version}/projects/{projectId}/locations/{locationId}/services/{serviceId}:{endpointId}'; +/** Firebase Data Connect connector URL format when using the Data Connect emulator. */ +const FIREBASE_DATA_CONNECT_EMULATOR_CONNECTORS_URL_FORMAT = + 'http://{host}/{version}/projects/{projectId}/locations/{locationId}/services/{serviceId}/connectors/{connectorId}:{endpointId}'; + const EXECUTE_GRAPH_QL_ENDPOINT = 'executeGraphql'; const EXECUTE_GRAPH_QL_READ_ENDPOINT = 'executeGraphqlRead'; +const IMPERSONATE_QUERY_ENDPOINT = 'impersonateQuery'; +const IMPERSONATE_MUTATION_ENDPOINT = 'impersonateMutation'; + function getHeaders(isUsingGen: boolean): { [key: string]: string } { const headerValue = { @@ -50,6 +73,27 @@ function getHeaders(isUsingGen: boolean): { [key: string]: string } { return headerValue; } +/** + * URL params for requests to an endpoint under services: + * .../services/{serviceId}:endpoint + */ +interface ServicesUrlParams { + version: string; + projectId: string; + locationId: string; + serviceId: string; + endpointId: string; + host?: string; // Present only when using the emulator +} + +/** + * URL params for requests to an endpoint under connectors: + * .../services/{serviceId}/connectors/{connectorId}:endpoint + */ +interface ConnectorsUrlParams extends ServicesUrlParams { + connectorId: string; +} + /** * Class that facilitates sending requests to the Firebase Data Connect backend API. * @@ -106,6 +150,15 @@ export class DataConnectApiClient { return this.executeGraphqlHelper(query, EXECUTE_GRAPH_QL_READ_ENDPOINT, options); } + + /** + * A helper function to execute GraphQL queries. + * + * @param query - The arbitrary GraphQL query to execute. + * @param endpoint - The endpoint to call. + * @param options - The GraphQL options. + * @returns A promise that fulfills with the GraphQL response, or throws an error. + */ private async executeGraphqlHelper( query: string, endpoint: string, @@ -129,52 +182,163 @@ export class DataConnectApiClient { ...(options?.operationName && { operationName: options?.operationName }), ...(options?.impersonate && { extensions: { impersonate: options?.impersonate } }), }; - return this.getUrl(API_VERSION, this.connectorConfig.location, this.connectorConfig.serviceId, endpoint) - .then(async (url) => { - const request: HttpRequestConfig = { - method: 'POST', - url, - headers: getHeaders(this.isUsingGen), - data, - }; - const resp = await this.httpClient.send(request); - if (resp.data.errors && validator.isNonEmptyArray(resp.data.errors)) { - const allMessages = resp.data.errors.map((error: { message: any; }) => error.message).join(' '); - throw new FirebaseDataConnectError( - DATA_CONNECT_ERROR_CODE_MAPPING.QUERY_ERROR, allMessages); - } - return Promise.resolve({ - data: resp.data.data as GraphqlResponse, - }); - }) - .then((resp) => { - return resp; - }) - .catch((err) => { - throw this.toFirebaseError(err); - }); + const url = await this.getServicesUrl( + API_VERSION, + this.connectorConfig.location, + this.connectorConfig.serviceId, + endpoint + ); + try { + const resp = await this.makeGqlRequest(url, data); + return resp; + } catch (err: any) { + throw this.toFirebaseError(err); + } } - private async getUrl(version: string, locationId: string, serviceId: string, endpointId: string): Promise { - return this.getProjectId() - .then((projectId) => { - const urlParams = { - version, - projectId, - locationId, - serviceId, - endpointId - }; - let urlFormat: string; - if (useEmulator()) { - urlFormat = utils.formatString(FIREBASE_DATA_CONNECT_EMULATOR_BASE_URL_FORMAT, { - host: emulatorHost() - }); - } else { - urlFormat = FIREBASE_DATA_CONNECT_BASE_URL_FORMAT; - } - return utils.formatString(urlFormat, urlParams); - }); + /** + * Executes a GraphQL query with impersonation. + * + * @param options - The GraphQL options. Must include impersonation details. + * @returns A promise that fulfills with the GraphQL response. + */ + public async executeQuery( + name: string, + variables: Variables, + options?: OperationOptions + ): Promise> { + return this.executeOperationHelper(IMPERSONATE_QUERY_ENDPOINT, name, variables, options); + } + + /** + * Executes a GraphQL mutation with impersonation. + * + * @param options - The GraphQL options. Must include impersonation details. + * @returns A promise that fulfills with the GraphQL response. + */ + public async executeMutation( + name: string, + variables: Variables, + options?: OperationOptions + ): Promise> { + return this.executeOperationHelper(IMPERSONATE_MUTATION_ENDPOINT, name, variables, options); + } + + /** + * A helper function to execute operations by making requests to FDC's impersonate + * operations endpoints. + * + * @param endpoint - The endpoint to call. + * @param options - The GraphQL options, including impersonation details. + * @returns A promise that fulfills with the GraphQL response. + */ + private async executeOperationHelper( + endpoint: string, + name: string, + variables: Variables, + options?: OperationOptions + ): Promise> { + if ( + typeof name === 'undefined' || + !validator.isNonEmptyString(name) + ) { + throw new FirebaseDataConnectError( + DATA_CONNECT_ERROR_CODE_MAPPING.INVALID_ARGUMENT, + '`name` must be a non-empty string.' + ); + } + + if (this.connectorConfig.connector === undefined || this.connectorConfig.connector === '') { + throw new FirebaseDataConnectError( + DATA_CONNECT_ERROR_CODE_MAPPING.INVALID_ARGUMENT, + `The 'connectorConfig.connector' field used to instantiate your Data Connect + instance must be a non-empty string (the connectorId) when calling executeQuery or executeMutation.`); + } + + const data = { + ...(variables && { variables: variables }), + operationName: name, + extensions: { impersonate: options?.impersonate }, + }; + const url = await this.getConnectorsUrl( + API_VERSION, + this.connectorConfig.location, + this.connectorConfig.serviceId, + this.connectorConfig.connector, + endpoint, + ); + try { + const resp = await this.makeGqlRequest(url, data); + return resp; + } catch (err: any) { + throw this.toFirebaseError(err); + } + } + + /** + * Constructs the URL for a Data Connect request to a service endpoint. + * + * @param version - The API version. + * @param locationId - The location of the Data Connect service. + * @param serviceId - The ID of the Data Connect service. + * @param endpointId - The endpoint to call. + * @returns A promise which resolves to the formatted URL string. + */ + private async getServicesUrl( + version: string, + locationId: string, + serviceId: string, + endpointId: string, + ): Promise { + const projectId = await this.getProjectId(); + const params: ServicesUrlParams = { + version, + projectId, + locationId, + serviceId, + endpointId, + }; + let urlFormat = FIREBASE_DATA_CONNECT_SERVICES_URL_FORMAT; + if (useEmulator()) { + urlFormat = FIREBASE_DATA_CONNECT_EMULATOR_SERVICES_URL_FORMAT; + params.host = emulatorHost(); + } + return utils.formatString(urlFormat, params); + } + + /** + * Constructs the URL for a Data Connect request to a connector endpoint. + * + * @param version - The API version. + * @param locationId - The location of the Data Connect service. + * @param serviceId - The ID of the Data Connect service. + * @param connectorId - The ID of the Connector. + * @param endpointId - The endpoint to call. + * @returns A promise which resolves to the formatted URL string. + + */ + private async getConnectorsUrl( + version: string, + locationId: string, + serviceId: string, + connectorId: string, + endpointId: string, + ): Promise { + const projectId = await this.getProjectId(); + const params: ConnectorsUrlParams = { + version, + projectId, + locationId, + serviceId, + connectorId, + endpointId, + }; + let urlFormat = FIREBASE_DATA_CONNECT_CONNECTORS_URL_FORMAT; + if (useEmulator()) { + urlFormat = FIREBASE_DATA_CONNECT_EMULATOR_CONNECTORS_URL_FORMAT; + params.host = emulatorHost(); + } + return utils.formatString(urlFormat, params); } private getProjectId(): Promise { @@ -195,6 +359,32 @@ export class DataConnectApiClient { }); } + /** + * Makes a GraphQL request to the specified url. + * + * @param url - The URL to send the request to. + * @param data - The GraphQL request payload. + * @returns A promise that fulfills with the GraphQL response, or throws an error. + */ + private async makeGqlRequest(url: string, data: object): + Promise> { + const request: HttpRequestConfig = { + method: 'POST', + url, + headers: getHeaders(this.isUsingGen), + data, + }; + const resp = await this.httpClient.send(request); + if (resp.data.errors && validator.isNonEmptyArray(resp.data.errors)) { + const allMessages = resp.data.errors.map((error: { message: any; }) => error.message).join(' '); + throw new FirebaseDataConnectError( + DATA_CONNECT_ERROR_CODE_MAPPING.QUERY_ERROR, allMessages); + } + return Promise.resolve({ + data: resp.data.data as GraphqlResponse, + }); + } + private toFirebaseError(err: RequestResponseError): PrefixedFirebaseError { if (err instanceof PrefixedFirebaseError) { return err; diff --git a/src/data-connect/data-connect-api.ts b/src/data-connect/data-connect-api.ts index c60ef5a2eb..bd25d493b2 100644 --- a/src/data-connect/data-connect-api.ts +++ b/src/data-connect/data-connect-api.ts @@ -30,10 +30,15 @@ export interface ConnectorConfig { * Service ID of the Data Connect service. */ serviceId: string; + + /** + * Name of the Data Connect connector. + */ + connector?: string; } /** - * Interface representing GraphQL response. + * Interface representing ExecuteGraphQL response. */ export interface ExecuteGraphqlResponse { /** @@ -43,7 +48,17 @@ export interface ExecuteGraphqlResponse { } /** - * Interface representing GraphQL options. + * Interface representing ExecuteOperation response. + */ +export interface ExecuteOperationResponse { + /** + * Data payload of the GraphQL response. + */ + data: GraphqlResponse; +} + +/** + * Interface representing GraphQL options for executing arbitrary GraphQL operations. */ export interface GraphqlOptions { /** @@ -52,7 +67,9 @@ export interface GraphqlOptions { variables?: Variables; /** - * The name of the GraphQL operation. Required only if `query` contains multiple operations. + * The name of the GraphQL operation. + * Required for operations that interact with services, such as executeGraphql, if + * `query` contains multiple operations. */ operationName?: string; @@ -63,6 +80,17 @@ export interface GraphqlOptions { impersonate?: ImpersonateAuthenticated | ImpersonateUnauthenticated; } +/** + * Interface representing options for executing defined operations. + */ +export interface OperationOptions { + /** + * If set, impersonate a request with given Firebase Auth context and evaluate the auth + * policies on the operation. If omitted, bypass any defined auth policies. + */ + impersonate?: ImpersonateAuthenticated | ImpersonateUnauthenticated; +} + /** * Type representing the partial claims of a Firebase Auth token used to evaluate the * Data Connect auth policy. diff --git a/src/data-connect/data-connect.ts b/src/data-connect/data-connect.ts index 366b756327..c48e3d1c78 100644 --- a/src/data-connect/data-connect.ts +++ b/src/data-connect/data-connect.ts @@ -21,7 +21,9 @@ import { DataConnectApiClient } from './data-connect-api-client-internal'; import { ConnectorConfig, ExecuteGraphqlResponse, + ExecuteOperationResponse, GraphqlOptions, + OperationOptions, } from './data-connect-api'; export class DataConnectService { @@ -96,13 +98,13 @@ export class DataConnect { } /** - * Execute an arbitrary read-only GraphQL query - * - * @param query - The GraphQL read-only query. - * @param options - Optional {@link GraphqlOptions} when executing a read-only GraphQL query. - * - * @returns A promise that fulfills with a `ExecuteGraphqlResponse`. - */ + * Execute an arbitrary read-only GraphQL query + * + * @param query - The GraphQL read-only query. + * @param options - Optional {@link GraphqlOptions} when executing a read-only GraphQL query. + * + * @returns A promise that fulfills with a `ExecuteGraphqlResponse`. + */ public executeGraphqlRead( query: string, options?: GraphqlOptions, @@ -165,4 +167,84 @@ export class DataConnect { ): Promise> { return this.client.upsertMany(tableName, variables); } + + /** + * Executes a GraphQL query. The query must be defined in your Data Connect GraphQL files. + * Optionally, you can provide auth impersonation details. If you don't + * specify a value for this option, the query will run with admin privileges + * and will ignore all auth directives. + * + * @param name - The name of the defined query to execute. + * @param options - The GraphQL options, must include operationName and impersonation details. + * @returns A promise that fulfills with the GraphQL response. + */ + public executeQuery( + name: string, + options?: OperationOptions + ): Promise>; + + /** + * Executes a GraphQL query. The query must be defined in your Data Connect GraphQL files. + * Optionally, you can provide auth impersonation details. If you don't + * specify a value for this option, the query will run with admin privileges + * and will ignore all auth directives. + * + * @param name - The name of the defined query to execute. + * @param variables - The variables for the query. May be optional if the query's variables are optional. + * @param options - The GraphQL options, must include operationName and impersonation details. + * @returns A promise that fulfills with the GraphQL response. + */ + public executeQuery( + name: string, + variables: Variables, + options?: OperationOptions + ): Promise>; + + public executeQuery( + name: string, + variables: Variables, + options?: OperationOptions + ): Promise> { + return this.client.executeQuery(name, variables, options); + } + + /** + * Executes a GraphQL mutation. The mutation must be defined in your Data Connect GraphQL files. + * Optionally, you can provide auth impersonation details. If you don't + * specify a value for this option, the query will run with admin privileges + * and will ignore all auth directives. + * + * @param name - The name of the defined mutation to execute. + * @param options - The GraphQL options, must include operationName and impersonation details. + * @returns A promise that fulfills with the GraphQL response. + */ + public executeMutation( + name: string, + options?: OperationOptions + ): Promise>; + + /** + * Executes a GraphQL mutation. The mutation must be defined in your Data Connect GraphQL files. + * Optionally, you can provide auth impersonation details. If you don't + * specify a value for this option, the query will run with admin privileges + * and will ignore all auth directives. + * + * @param name - The name of the defined mutation to execute. + * @param variables - The variables for the mutation. May be optional if the mutation's variables are optional. + * @param options - The GraphQL options, must include operationName and impersonation details. + * @returns A promise that fulfills with the GraphQL response. + */ + public executeMutation( + name: string, + variables: Variables, + options?: OperationOptions + ): Promise>; + + public executeMutation( + name: string, + variables: Variables, + options?: OperationOptions + ): Promise> { + return this.client.executeMutation(name, variables, options); + } } diff --git a/src/data-connect/index.ts b/src/data-connect/index.ts index 43ca313c90..95d9f26da2 100644 --- a/src/data-connect/index.ts +++ b/src/data-connect/index.ts @@ -1,3 +1,9 @@ +/** + * Firebase Data Connect service. + * + * @packageDocumentation + */ + /*! * @license * Copyright 2024 Google LLC @@ -15,12 +21,7 @@ * limitations under the License. */ -/** - * Firebase Data Connect service. - * - * @packageDocumentation - */ - +import { _validateAdminArgs } from './validate-admin-args'; import { App, getApp } from '../app'; import { FirebaseApp } from '../app/firebase-app'; import { DataConnect, DataConnectService } from './data-connect'; @@ -29,10 +30,12 @@ import { ConnectorConfig } from './data-connect-api'; export { GraphqlOptions, ExecuteGraphqlResponse, + ExecuteOperationResponse, ConnectorConfig, ImpersonateAuthenticated, ImpersonateUnauthenticated, - AuthClaims + AuthClaims, + OperationOptions, } from './data-connect-api' export { DataConnect, @@ -51,6 +54,7 @@ export { * const connectorConfig: ConnectorConfig = { * location: 'us-west2', * serviceId: 'my-service', + * connectorName: 'my-connector', * }; * * // Get the `DataConnect` service for the default app @@ -80,3 +84,8 @@ export function getDataConnect(connectorConfig: ConnectorConfig, app?: App): Dat const dataConnectService = firebaseApp.getOrInitService('dataConnect', (app) => new DataConnectService(app)); return dataConnectService.getDataConnect(connectorConfig); } + +/** + * @internal + */ +export const validateAdminArgs = _validateAdminArgs; diff --git a/src/data-connect/validate-admin-args.ts b/src/data-connect/validate-admin-args.ts new file mode 100644 index 0000000000..f4332b5ddd --- /dev/null +++ b/src/data-connect/validate-admin-args.ts @@ -0,0 +1,91 @@ +/*! + * @license + * Copyright 2025 Google LLC + * + * 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. + */ + +import { getDataConnect } from './index'; +import { DataConnect } from './data-connect'; +import { ConnectorConfig, OperationOptions } from './data-connect-api'; +import { + DATA_CONNECT_ERROR_CODE_MAPPING, + FirebaseDataConnectError, +} from './data-connect-api-client-internal'; + +/** + * @internal + * + * The generated Admin SDK will allow the user to pass in variables, a Data Connect + * instance, or operation options. The only required argument is the variables, + * which are only required when the operation has at least one required variable. + * Otherwise, all arguments are optional. + * + * This function validates the variables and returns back the DataConnect instance, + * variables, and options based on the arguments passed in. It always returns a + * DataConnect instance, using the connectorConfig to grab one if not provided. + * + * For this function to work properly, if the operation has variables (optional + * are required), you must pass hasVars: true (if there are no variables, it is + * not required, since undefined is false-y). + * + * Usage examples can be found in test files. + * + * @param connectorConfig - DataConnect connector config + * @param dcOrVarsOrOptions - the first argument provided to a generated admin function + * @param varsOrOptions - the second argument provided to a generated admin function + * @param options - the third argument provided to a generated admin function + * @param hasVars - boolean parameter indicating whether the operation has variables + * @param validateVars - boolean parameter indicating whether we should expect to find a value for realVars + * @returns parsed DataConnect, Variables, and Options for the operation + */ +export function _validateAdminArgs( + connectorConfig: ConnectorConfig, + dcOrVarsOrOptions?: DataConnect | Variables | OperationOptions, + varsOrOptions?: Variables | OperationOptions, + options?: OperationOptions, + hasVars?: boolean, + validateVars?: boolean +): { dc: DataConnect; vars: Variables; options: OperationOptions; } { + let dcInstance: DataConnect; + let realVars: Variables; + let realOptions: OperationOptions; + + if (dcOrVarsOrOptions && 'connectorConfig' in dcOrVarsOrOptions) { + dcInstance = dcOrVarsOrOptions as DataConnect; + if (hasVars) { + realVars = varsOrOptions as Variables; + realOptions = options as OperationOptions; + } else { + realVars = undefined as unknown as Variables; + realOptions = varsOrOptions as OperationOptions; + } + } else { + dcInstance = getDataConnect(connectorConfig); + if (hasVars) { + realVars = dcOrVarsOrOptions as Variables; + realOptions = varsOrOptions as OperationOptions; + } else { + realVars = undefined as unknown as Variables; + realOptions = dcOrVarsOrOptions as OperationOptions; + } + } + + if (!dcInstance || (!realVars && validateVars)) { + throw new FirebaseDataConnectError( + DATA_CONNECT_ERROR_CODE_MAPPING.INVALID_ARGUMENT, + 'Variables required.' + ); + } + return { dc: dcInstance, vars: realVars, options: realOptions }; +} diff --git a/test/integration/data-connect.spec.ts b/test/integration/data-connect.spec.ts index 4cf74549cf..78da027fb4 100644 --- a/test/integration/data-connect.spec.ts +++ b/test/integration/data-connect.spec.ts @@ -16,9 +16,10 @@ import * as chai from 'chai'; import * as chaiAsPromised from 'chai-as-promised'; -import { getDataConnect, ConnectorConfig, GraphqlOptions } from '../../lib/data-connect/index'; +import { getDataConnect, ConnectorConfig } from '../../lib/data-connect/index'; import firebase from '@firebase/app-compat'; import { apiKey, projectId } from './setup'; +import { OperationOptions } from '../../lib/data-connect/data-connect-api'; chai.should(); chai.use(chaiAsPromised); @@ -44,11 +45,11 @@ type User = { /** * // Schema * type Email @table { - * id: String! - * subject: String! - * date: Date! - * text: String! - * from: User! + * id: String! + * subject: String! + * date: Date! + * text: String! + * from: User! * } */ type Email = { @@ -59,10 +60,24 @@ type Email = { id: string; }; +interface GetUserVariables { + id: { id: string; }; +} + interface GetUserResponse { user: User; } +interface InsertUserVariables { + id: string; + name: string; + address: string; +} + +interface InsertUserResponse { + user_insert: { id: string; }; +} + interface ListUsersResponse { users: User[]; } @@ -75,16 +90,32 @@ interface UserUpdateResponse { user_update: { id: string; }; } +interface GetEmailVariables { + id: string +} + +interface GetEmailResponse { + email: Email; +} + interface EmailUpsertResponse { email_upsert: { id: string; }; } -interface ListEmailsResponse { - emails: Email[]; +interface InsertEmailVariables { + id: string; } -interface GetUserVariables { - id: { id: string; }; +interface InsertEmailResponse { + email_insert: { id: string; }; +} + +interface InsertEmailResponse { + email_insert: { id: string; }; +} + +interface ListEmailsResponse { + emails: Email[]; } interface DeleteResponse { @@ -95,6 +126,7 @@ interface DeleteResponse { const connectorConfig: ConnectorConfig = { location: 'us-west2', serviceId: 'my-service', + connector: 'my-connector' }; const fredUser = { id: 'fred_id', address: '32 Elm St.', name: 'Fred' } @@ -152,6 +184,9 @@ describe('getDataConnect()', () => { /** @auth(level: NO_ACCESS) */ const queryListEmails = 'query ListEmails @auth(level: NO_ACCESS) { emails { id subject text date from { id } } }'; + /** @auth(level: NO_ACCESS) */ + const queryGetEmail = + 'query GetEmail($id: String!) @auth(level: NO_ACCESS) { email(id: $id) { id subject text date from { id } } }'; /** no @auth specified - default permissions */ const queryGetUserById = 'query GetUser($id: User_Key!) { user(key: $id) { id name address } }'; @@ -204,223 +239,767 @@ describe('getDataConnect()', () => { user_deleteMany(all: true) }` - describe('executeGraphql()', () => { - it('executeGraphql() successfully executes a GraphQL mutation', async () => { - const fredResponse = await getDataConnect(connectorConfig).executeGraphql( - upsertFredUser - ); - //{ data: { user_insert: { id: 'fred_id' } } } - expect(fredResponse.data.user_upsert.id).to.be.not.empty; - expect(fredResponse.data.user_upsert.id).equals(fredUser.id); - - const jeffResponse = await getDataConnect(connectorConfig).executeGraphql( - upsertJeffUser - ); - //{ data: { user_insert: { id: 'jeff_id' } } } - expect(jeffResponse.data.user_upsert.id).to.be.not.empty; - expect(jeffResponse.data.user_upsert.id).equals(jeffUser.id); - - const emailResponse = await getDataConnect(connectorConfig).executeGraphql( - upsertFredEmail - ); - //{ data: { email_upsert: { id: 'email_id' } } } - expect(emailResponse.data.email_upsert.id).to.be.not.empty; - - const deleteResponse = await getDataConnect(connectorConfig).executeGraphql(deleteAll); - expect(deleteResponse.data.email_deleteMany).to.be.greaterThan(0); - expect(deleteResponse.data.user_deleteMany).to.be.greaterThan(0); - }); + const optsUnauthorizedClaims: OperationOptions = { + impersonate: { + unauthenticated: true + } + }; + + const optsAuthorizedFredAnonClaims: OperationOptions = { + impersonate: { + authClaims: { + sub: fredUser.id, + firebase: { + identities: { who: 'me' }, + sign_in_provider: 'anonymous' + } + } + } + }; - it('executeGraphql() successfully executes a GraphQL query', async () => { - const resp = await getDataConnect(connectorConfig) - .executeGraphql(queryListUsers); - expect(resp.data.users).to.be.not.empty; - expect(resp.data.users.length).to.equal(initialState.users.length); - resp.data.users.forEach((user) => { - expect(initialState.users).to.deep.include(user); + const optsAuthorizedFredClaims: OperationOptions = { + impersonate: { + authClaims: { + sub: fredUser.id, + } + } + }; + + const optsAuthorizedFredEmailVerifiedClaims: OperationOptions = { + impersonate: { + authClaims: { + sub: fredUser.id, + email_verified: true + } + } + }; + + const optsNonExistingClaims: OperationOptions = { + impersonate: { + authClaims: { + sub: 'non-exisiting-id', + email_verified: true + } + } + }; + + + describe('executeGraphql* API', () => { + describe('executeGraphql()', () => { + it('executeGraphql() successfully executes a GraphQL mutation', async () => { + const fredResponse = await getDataConnect(connectorConfig).executeGraphql( + upsertFredUser + ); + //{ data: { user_insert: { id: 'fred_id' } } } + expect(fredResponse.data.user_upsert.id).equals(fredUser.id); + + const jeffResponse = await getDataConnect(connectorConfig).executeGraphql( + upsertJeffUser + ); + //{ data: { user_insert: { id: 'jeff_id' } } } + expect(jeffResponse.data.user_upsert.id).equals(jeffUser.id); + + const upsertEmailResponse = await getDataConnect(connectorConfig).executeGraphql( + upsertFredEmail + ); + //{ data: { email_upsert: { id: 'email_id' } } } + expect(upsertEmailResponse.data.email_upsert.id).to.not.be.empty; + const queryGetEmailResponse = + await getDataConnect(connectorConfig).executeGraphql( + queryGetEmail, { variables: { id: upsertEmailResponse.data.email_upsert.id } } + ); + expect(queryGetEmailResponse.data.email).to.deep.equal(fredEmail); + + const deleteResponse = await getDataConnect(connectorConfig).executeGraphql(deleteAll); + expect(deleteResponse.data.email_deleteMany).to.equal(1); + expect(deleteResponse.data.user_deleteMany).to.equal(2); }); - }); - it('executeGraphql() use the operationName when multiple queries are provided', async () => { - const resp = await getDataConnect(connectorConfig).executeGraphql( - multipleQueries, - { operationName: 'ListEmails' } - ); - expect(resp.data.emails).to.not.be.empty; - expect(resp.data.emails).to.deep.equal(initialState.emails); - }); + it('executeGraphql() successfully executes a GraphQL query', async () => { + const resp = await getDataConnect(connectorConfig) + .executeGraphql(queryListUsers); + expect(resp.data.users).to.deep.equal(initialState.users); + }); - it('executeGraphql() should throw for a query error when no variables are provided', async () => { - return getDataConnect(connectorConfig).executeGraphql(queryGetUserById) - .should.eventually.be.rejected.and.have.property('code', 'data-connect/query-error'); - }); + it('executeGraphql() use the operationName when multiple queries are provided', async () => { + const resp = await getDataConnect(connectorConfig).executeGraphql( + multipleQueries, + { operationName: 'ListEmails' } + ); + expect(resp.data.emails).to.deep.equal(initialState.emails); + }); - it('executeGraphql() successfully executes a GraphQL query with variables', async () => { - const resp = await getDataConnect(connectorConfig).executeGraphql( - queryGetUserById, - { variables: { id: { id: initialState.users[0].id } } } - ); - expect(resp.data.user).to.deep.equal(initialState.users[0]); - }); - }); + it('executeGraphql() should throw for a query error when no variables are provided', async () => { + return getDataConnect(connectorConfig).executeGraphql(queryGetUserById) + .should.eventually.be.rejected.and.have.property('code', 'data-connect/query-error'); + }); - describe('executeGraphqlRead()', () => { - it('executeGraphqlRead() successfully executes a read-only GraphQL', async () => { - const resp = await getDataConnect(connectorConfig) - .executeGraphqlRead(queryListUsers); - expect(resp.data.users).to.be.not.empty; - expect(resp.data.users.length).to.equal(initialState.users.length); - resp.data.users.forEach((user) => { - expect(initialState.users).to.deep.include(user); + it('executeGraphql() successfully executes a GraphQL query with variables', async () => { + const resp = await getDataConnect(connectorConfig).executeGraphql( + queryGetUserById, + { variables: { id: { id: initialState.users[0].id } } } + ); + expect(resp.data.user).to.deep.equal(initialState.users[0]); }); }); - it('executeGraphqlRead() should throw for a GraphQL mutation', async () => { - return getDataConnect(connectorConfig).executeGraphqlRead(upsertFredUser) - .should.eventually.be.rejected.and.have.property('code', 'data-connect/permission-denied'); - }); - }); + describe('executeGraphqlRead()', () => { + it('executeGraphqlRead() successfully executes a read-only GraphQL', async () => { + const resp = await getDataConnect(connectorConfig) + .executeGraphqlRead(queryListUsers); + expect(resp.data.users).to.deep.equal(initialState.users); + }); - describe('Impersonation', () => { - const optsAuthorizedFredClaims: GraphqlOptions = { - impersonate: { - authClaims: { - sub: fredUser.id, - email_verified: true - } - } - }; + it('executeGraphqlRead() should throw for a GraphQL mutation', async () => { + return getDataConnect(connectorConfig).executeGraphqlRead(upsertFredUser) + .should.eventually.be.rejected.and.have.property('code', 'data-connect/permission-denied'); + }); + }); - const optsNonExistingClaims: GraphqlOptions = { - impersonate: { - authClaims: { - sub: 'non-exisiting-id', - email_verified: true - } - } - }; + describe('executeGraphql* impersonation', () => { + describe('USER Auth Policy', () => { + it('executeGraphqlRead() successfully executes an impersonated query with authenticated claims', async () => { + const resp = + await getDataConnect(connectorConfig).executeGraphqlRead( + queryListUsersImpersonation, optsAuthorizedFredClaims); + expect(resp.data.users.length).equals(1); + expect(resp.data.users[0]).to.deep.equal(fredUser); + }); - const optsUnauthorizedClaims: GraphqlOptions = { - impersonate: { - unauthenticated: true - } - }; + it('executeGraphqlRead() should throw for impersonated query with unauthenticated claims', async () => { + return getDataConnect(connectorConfig).executeGraphqlRead( + queryListUsersImpersonation, + optsUnauthorizedClaims + ) + .should.eventually.be.rejected.and.have.property('code', 'data-connect/unauthenticated'); + }); - describe('USER Auth Policy', () => { - it('executeGraphqlRead() successfully executes an impersonated query with authenticated claims', async () => { - const resp = - await getDataConnect(connectorConfig).executeGraphqlRead( + it('executeGraphql() successfully executes an impersonated query with authenticated claims', async () => { + const resp = await getDataConnect(connectorConfig).executeGraphql( queryListUsersImpersonation, optsAuthorizedFredClaims); - expect(resp.data.users).to.be.not.empty; - expect(resp.data.users.length).equals(1); - expect(resp.data.users[0]).to.deep.equal(fredUser); + expect(resp.data.users.length).equals(1); + expect(resp.data.users[0]).to.deep.equal(fredUser); + }); + + it('executeGraphql() should throw for impersonated query with unauthenticated claims', async () => { + return getDataConnect(connectorConfig).executeGraphql(queryListUsersImpersonation, optsUnauthorizedClaims) + .should.eventually.be.rejected.and.have.property('code', 'data-connect/unauthenticated'); + }); + + it('executeGraphql() should return an empty list for an impersonated query with non-existing authenticated ' + + 'claims', + async () => { + const resp = await getDataConnect(connectorConfig).executeGraphql( + queryListUsersImpersonation, optsNonExistingClaims); + // Should find no data + expect(resp.data.users).to.be.empty; + }); + + it('executeGraphql() successfully executes an impersonated mutation with authenticated claims', + async () => { + const updateResp = await getDataConnect(connectorConfig).executeGraphql( + updateFredrickUserImpersonated, optsAuthorizedFredClaims); + // Fred -> Fredrick + expect(updateResp.data.user_update.id).equals(fredUser.id); + const queryResp = await getDataConnect(connectorConfig).executeGraphql( + queryGetUserById, { variables: { id: { id: fredUser.id } } }); + expect(queryResp.data.user).to.deep.equal(fredrickUser); + }); + + it('executeGraphql() should throw for impersonated mutation with unauthenticated claims', async () => { + return getDataConnect(connectorConfig).executeGraphql(updateFredrickUserImpersonated, optsUnauthorizedClaims) + .should.eventually.be.rejected.and.have.property('code', 'data-connect/unauthenticated'); + }); + + it('executeGraphql() should return null for an impersonated mutation with non-existing authenticated claims', + async () => { + const resp = await getDataConnect(connectorConfig).executeGraphql( + updateFredrickUserImpersonated, optsNonExistingClaims); + // Should mutate no data + expect(resp.data.user_update).to.be.null; + }); + }); + + describe('PUBLIC Auth Policy', () => { + it('executeGraphql() successfully executes an impersonated query with authenticated claims', async () => { + const resp = await getDataConnect(connectorConfig).executeGraphql( + queryListUsers, optsAuthorizedFredClaims); + expect(resp.data.users).to.deep.equal(initialState.users); + }); + + it('executeGraphql() successfully executes an impersonated query with unauthenticated claims', async () => { + const resp = await getDataConnect(connectorConfig).executeGraphql( + queryListUsers, optsUnauthorizedClaims); + expect(resp.data.users).to.deep.equal(initialState.users); + }); + + it('executeGraphql() successfully executes an impersonated query with non-existing authenticated claims', + async () => { + const resp = await getDataConnect(connectorConfig).executeGraphql( + queryListUsers, optsNonExistingClaims); + expect(resp.data.users).to.deep.equal(initialState.users); + }); + }); + + describe('NO_ACCESS Auth Policy', () => { + it('executeGraphql() should throw for an impersonated query with authenticated claims', async () => { + return await getDataConnect(connectorConfig).executeGraphql(queryListEmails, optsAuthorizedFredClaims) + .should.eventually.be.rejected.and.have.property('code', 'data-connect/permission-denied'); + }); + + it('executeGraphql() should throw for an impersonated query with unauthenticated claims', async () => { + return await getDataConnect(connectorConfig).executeGraphql(queryListEmails, optsUnauthorizedClaims) + .should.eventually.be.rejected.and.have.property('code', 'data-connect/permission-denied'); + }); + + it('executeGraphql() should throw for an impersonated query with non-existing authenticated claims', + async () => { + return await getDataConnect(connectorConfig).executeGraphql(queryListEmails, optsNonExistingClaims) + .should.eventually.be.rejected.and.have.property('code', 'data-connect/permission-denied'); + }); }); + }); + }); - it('executeGraphqlRead() should throw for impersonated query with unauthenticated claims', async () => { - return getDataConnect(connectorConfig).executeGraphqlRead( - queryListUsersImpersonation, + describe('executeQuery|Mutation API', () => { + describe('executeQuery()', () => { + it("should fail when executing a query which doesn't exist", async () => { + return getDataConnect(connectorConfig).executeQuery( + 'DOES_NOT_EXIST!!!', + undefined, optsUnauthorizedClaims - ) - .should.eventually.be.rejected.and.have.property('code', 'data-connect/unauthenticated'); + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/not-found'); + }); + + it('should execute a query with variables', async () => { + const resp = await getDataConnect(connectorConfig).executeQuery( + 'GetUser', + { id: { id: fredUser.id } }, + ); + expect(resp.data.user).to.deep.equal(fredUser); }); - it('executeGraphql() successfully executes an impersonated query with authenticated claims', async () => { - const resp = await getDataConnect(connectorConfig).executeGraphql( - queryListUsersImpersonation, optsAuthorizedFredClaims); - expect(resp.data.users).to.be.not.empty; - expect(resp.data.users.length).equals(1); - expect(resp.data.users[0]).to.deep.equal(fredUser); + describe('with unauthenticated impersonation', () => { + it('should successfully execute a query with @auth(level: PUBLIC)', async () => { + const resp = await getDataConnect(connectorConfig).executeQuery( + 'ListUsersPublic', + undefined, + optsUnauthorizedClaims + ); + expect(resp.data.users).to.deep.equal(initialState.users); + }); + + it('should fail to execute a query with @auth(level: USER_ANON)', () => { + return getDataConnect(connectorConfig).executeQuery( + 'ListUsersUserAnon', undefined, optsUnauthorizedClaims + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/unauthenticated'); + }); + + it('should fail to execute a query with @auth(level: USER)', async () => { + return getDataConnect(connectorConfig).executeQuery( + 'ListUsersUser', undefined, optsUnauthorizedClaims + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/unauthenticated'); + }); + + it('should fail to execute a query with @auth(level: USER_EMAIL_VERIFIED)', () => { + return getDataConnect(connectorConfig).executeQuery( + 'ListUsersUserEmailVerified', undefined, optsUnauthorizedClaims + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/unauthenticated'); + }); + + it('should fail to execute a query with @auth(level: NO_ACCESS)', async () => { + return getDataConnect(connectorConfig).executeQuery( + 'ListUsersNoAccess', undefined, optsUnauthorizedClaims + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/permission-denied'); + }); }); - it('executeGraphql() should throw for impersonated query with unauthenticated claims', async () => { - return getDataConnect(connectorConfig).executeGraphql(queryListUsersImpersonation, optsUnauthorizedClaims) - .should.eventually.be.rejected.and.has.property('code', 'data-connect/unauthenticated'); + describe('with authenticated anonymous impersonation', () => { + it('should successfully execute a query with @auth(level: PUBLIC)', async () => { + const resp = await getDataConnect(connectorConfig).executeQuery( + 'ListUsersPublic', undefined, optsAuthorizedFredAnonClaims + ); + expect(resp.data.users).to.deep.equal(initialState.users); + }); + + it('should successfully execute a query with @auth(level: USER_ANON)', async () => { + const resp = await getDataConnect(connectorConfig).executeQuery( + 'ListUsersUserAnon', undefined, optsAuthorizedFredAnonClaims + ); + expect(resp.data.users).to.deep.equal(initialState.users); + }); + + it('should fail to execute a query with @auth(level: USER)', async () => { + return getDataConnect(connectorConfig).executeQuery( + 'ListUsersUser', undefined, optsAuthorizedFredAnonClaims + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/unauthenticated'); + }); + + it('should fail to execute a query with @auth(level: USER_EMAIL_VERIFIED)', async () => { + return getDataConnect(connectorConfig).executeQuery( + 'ListUsersUserEmailVerified', undefined, optsAuthorizedFredAnonClaims + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/unauthenticated'); + }); + + it('should fail to execute a query with @auth(level: NO_ACCESS)', async () => { + return getDataConnect(connectorConfig).executeQuery( + 'ListUsersNoAccess', undefined, optsAuthorizedFredAnonClaims + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/permission-denied'); + }); + + it("should use the impersonated user's auth.uid", async () => { + const resp = await getDataConnect(connectorConfig).executeQuery( + 'ListUsersImpersonationAnon', undefined, optsAuthorizedFredAnonClaims + ); + expect(resp.data.users.length).equals(1); + expect(resp.data.users[0]).to.deep.equal(fredUser); + }); }); - it('executeGraphql() should return an empty list for an impersonated query with non-existing authenticated ' + - 'claims', - async () => { - const resp = await getDataConnect(connectorConfig).executeGraphql( - queryListUsersImpersonation, optsNonExistingClaims); - // Should find no data - expect(resp.data.users).to.be.empty; + describe('with authenticated user impersonation', () => { + it('should successfully execute a query with @auth(level: PUBLIC)', async () => { + const resp = await getDataConnect(connectorConfig).executeQuery( + 'ListUsersPublic', undefined, optsAuthorizedFredClaims + ); + expect(resp.data.users).to.deep.equal(initialState.users); + }); + + it('should successfully execute a query with @auth(level: USER_ANON)', async () => { + const resp = await getDataConnect(connectorConfig).executeQuery( + 'ListUsersUserAnon', undefined, optsAuthorizedFredClaims + ); + expect(resp.data.users).to.deep.equal(initialState.users); + }); + + it('should successfully execute a query with @auth(level: USER)', async () => { + const resp = await getDataConnect(connectorConfig).executeQuery( + 'ListUsersUser', undefined, optsAuthorizedFredClaims + ); + expect(resp.data.users).to.deep.equal(initialState.users); + }); + + it('should fail to execute a query with @auth(level: USER_EMAIL_VERIFIED)', async () => { + return getDataConnect(connectorConfig).executeQuery( + 'ListUsersUserEmailVerified', undefined, optsAuthorizedFredClaims + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/unauthenticated'); + }); + + it('should fail to execute a query with @auth(level: NO_ACCESS)', async () => { + return getDataConnect(connectorConfig).executeQuery( + 'ListUsersNoAccess', undefined, optsAuthorizedFredClaims + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/permission-denied'); + }); + + it("should use the impersonated user's auth.uid", async () => { + const resp = await getDataConnect(connectorConfig).executeQuery( + 'ListUsersImpersonationAnon', undefined, optsAuthorizedFredClaims + ); + expect(resp.data.users.length).equals(1); + expect(resp.data.users[0]).to.deep.equal(fredUser); + }); }); - it('executeGraphql() successfully executes an impersonated mutation with authenticated claims', - async () => { - const updateResp = await getDataConnect(connectorConfig).executeGraphql( - updateFredrickUserImpersonated, optsAuthorizedFredClaims); - // Fred -> Fredrick - expect(updateResp.data.user_update.id).equals(fredUser.id); - const queryResp = await getDataConnect(connectorConfig).executeGraphql( - queryGetUserById, { variables: { id: { id: fredUser.id } } }); - expect(queryResp.data.user).to.not.be.empty; - expect(queryResp.data.user).to.deep.equal(fredrickUser); - }); - - it('executeGraphql() should throw for impersonated mutation with unauthenticated claims', async () => { - return getDataConnect(connectorConfig).executeGraphql(updateFredrickUserImpersonated, optsUnauthorizedClaims) - .should.eventually.be.rejected.and.has.property('code', 'data-connect/unauthenticated'); + describe('with authenticated email verified user impersonation', () => { + it('should successfully execute a query with @auth(level: PUBLIC)', async () => { + const resp = await getDataConnect(connectorConfig).executeQuery( + 'ListUsersPublic', undefined, optsAuthorizedFredEmailVerifiedClaims + ); + expect(resp.data.users).to.deep.equal(initialState.users); + }); + + it('should successfully execute a query with @auth(level: USER_ANON)', async () => { + const resp = await getDataConnect(connectorConfig).executeQuery( + 'ListUsersUserAnon', undefined, optsAuthorizedFredEmailVerifiedClaims + ); + expect(resp.data.users).to.deep.equal(initialState.users); + }); + + it('should successfully execute a query with @auth(level: USER)', async () => { + const resp = await getDataConnect(connectorConfig).executeQuery( + 'ListUsersUser', undefined, optsAuthorizedFredEmailVerifiedClaims + ); + expect(resp.data.users).to.deep.equal(initialState.users); + }); + + it('should successfully execute a query with @auth(level: USER_EMAIL_VERIFIED)', async () => { + const resp = await getDataConnect(connectorConfig).executeQuery( + 'ListUsersUserEmailVerified', undefined, optsAuthorizedFredEmailVerifiedClaims + ); + expect(resp.data.users).to.deep.equal(initialState.users); + }); + + it('should fail to execute a query with @auth(level: NO_ACCESS)', async () => { + return getDataConnect(connectorConfig).executeQuery( + 'ListUsersNoAccess', undefined, optsAuthorizedFredEmailVerifiedClaims + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/permission-denied'); + }); + + it("should use the impersonated user's auth.uid", async () => { + const resp = await getDataConnect(connectorConfig).executeQuery( + 'ListUsersImpersonationAnon', undefined, optsAuthorizedFredEmailVerifiedClaims + ); + expect(resp.data.users.length).equals(1); + expect(resp.data.users[0]).to.deep.equal(fredUser); + }); }); - it('executeGraphql() should return null for an impersonated mutation with non-existing authenticated claims', - async () => { - const resp = await getDataConnect(connectorConfig).executeGraphql( - updateFredrickUserImpersonated, optsNonExistingClaims); - // Should mutate no data - expect(resp.data.user_update).to.be.null; + describe('with no impersonation, bypassing auth policies', () => { + it('should successfully execute a query with @auth(level: PUBLIC)', async () => { + const resp = await getDataConnect(connectorConfig).executeQuery( + 'ListUsersPublic' + ); + expect(resp.data.users).to.deep.equal(initialState.users); + }); + + it('should successfully execute a query with @auth(level: USER_ANON)', async () => { + const resp = await getDataConnect(connectorConfig).executeQuery( + 'ListUsersUserAnon' + ); + expect(resp.data.users).to.deep.equal(initialState.users); + }); + + it('should successfully execute a query with @auth(level: USER)', async () => { + const resp = await getDataConnect(connectorConfig).executeQuery( + 'ListUsersUser' + ); + expect(resp.data.users).to.deep.equal(initialState.users); + }); + + it('should successfully execute a query with @auth(level: USER_EMAIL_VERIFIED)', async () => { + const resp = await getDataConnect(connectorConfig).executeQuery( + 'ListUsersUserEmailVerified' + ); + expect(resp.data.users).to.deep.equal(initialState.users); + }); + + it('should successfully execute a query with @auth(level: NO_ACCESS)', async () => { + const resp = await getDataConnect(connectorConfig).executeQuery( + 'ListUsersNoAccess' + ); + expect(resp.data.users).to.deep.equal(initialState.users); }); + + it("should fail to execute a query using the impersonated user's auth.uid", async () => { + return getDataConnect(connectorConfig).executeQuery( + 'ListUsersImpersonationAnon' + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/query-error'); + }); + }); }); - describe('PUBLIC Auth Policy', () => { - it('executeGraphql() successfully executes an impersonated query with authenticated claims', async () => { - const resp = await getDataConnect(connectorConfig).executeGraphql( - queryListUsers, optsAuthorizedFredClaims); - expect(resp.data.users).to.be.not.empty; - expect(resp.data.users.length).to.equal(initialState.users.length); - resp.data.users.forEach((user) => { - expect(initialState.users).to.deep.include(user); + describe('executeMutation()', () => { + function generateEmailId(): string { + return `email_id_${Math.random() * 1000}`; + } + + it("should fail when executing a mutation which doesn't exist", async () => { + return getDataConnect(connectorConfig).executeMutation( + 'DOES_NOT_EXIST!!!', optsUnauthorizedClaims + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/not-found'); + }); + + it('should execute a mutation with variables', async () => { + const user = { id: 'USER_ID', name: 'USER_NAME', address: 'USER_ADDRESS' }; + const insertResp = await getDataConnect(connectorConfig) + .executeMutation('InsertUser', user); + expect(insertResp.data.user_insert).to.deep.equal({ id: user.id }); + + const getResp = await getDataConnect(connectorConfig).executeQuery( + 'GetUser', + { id: { id: user.id } }, + ); + expect(getResp.data.user).to.deep.equal(user); + }) + + describe('with unauthenticated impersonation', () => { + it('should successfully execute a mutation with @auth(level: PUBLIC)', async () => { + const resp = await getDataConnect(connectorConfig) + .executeMutation( + 'InsertEmailPublic', + { id: generateEmailId() }, + optsUnauthorizedClaims + ); + expect(resp.data.email_insert.id).to.not.be.undefined; + }); + + it('should fail to execute a mutation with @auth(level: USER_ANON)', () => { + return getDataConnect(connectorConfig).executeMutation( + 'InsertEmailUserAnon', + { id: generateEmailId() }, + optsUnauthorizedClaims + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/unauthenticated'); + }); + + it('should fail to execute a mutation with @auth(level: USER)', async () => { + return getDataConnect(connectorConfig).executeMutation( + 'InsertEmailUser', + { id: generateEmailId() }, + optsUnauthorizedClaims + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/unauthenticated'); + }); + + it('should fail to execute a mutation with @auth(level: USER_EMAIL_VERIFIED)', () => { + return getDataConnect(connectorConfig).executeMutation( + 'InsertEmailUserEmailVerified', + { id: generateEmailId() }, + optsUnauthorizedClaims + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/unauthenticated'); + }); + + it('should fail to execute a mutation with @auth(level: NO_ACCESS)', async () => { + return getDataConnect(connectorConfig).executeMutation( + 'InsertEmailNoAccess', + { id: generateEmailId() }, + optsUnauthorizedClaims + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/permission-denied'); }); }); - it('executeGraphql() successfully executes an impersonated query with unauthenticated claims', async () => { - const resp = await getDataConnect(connectorConfig).executeGraphql( - queryListUsers, optsUnauthorizedClaims); - expect(resp.data.users).to.be.not.empty; - expect(resp.data.users.length).to.equal(initialState.users.length); - resp.data.users.forEach((user) => { - expect(initialState.users).to.deep.include(user); + describe('with authenticated anonymous impersonation', () => { + it('should successfully execute a mutation with @auth(level: PUBLIC)', async () => { + const resp = await getDataConnect(connectorConfig) + .executeMutation( + 'InsertEmailPublic', + { id: generateEmailId() }, + optsAuthorizedFredAnonClaims + ); + expect(resp.data.email_insert.id).to.not.be.undefined; + }); + + it('should successfully execute a mutation with @auth(level: USER_ANON)', async () => { + const resp = await getDataConnect(connectorConfig) + .executeMutation( + 'InsertEmailUserAnon', + { id: generateEmailId() }, + optsAuthorizedFredAnonClaims + ); + expect(resp.data.email_insert.id).to.not.be.undefined; + }); + + it('should fail to execute a mutation with @auth(level: USER)', async () => { + return getDataConnect(connectorConfig).executeMutation( + 'InsertEmailUser', + { id: generateEmailId() }, + optsAuthorizedFredAnonClaims + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/unauthenticated'); + }); + + it('should fail to execute a mutation with @auth(level: USER_EMAIL_VERIFIED)', () => { + return getDataConnect(connectorConfig).executeMutation( + 'InsertEmailUserEmailVerified', + { id: generateEmailId() }, + optsAuthorizedFredAnonClaims + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/unauthenticated'); + }); + + it('should fail to execute a mutation with @auth(level: NO_ACCESS)', async () => { + return getDataConnect(connectorConfig).executeMutation( + 'InsertEmailNoAccess', + { id: generateEmailId() }, + optsAuthorizedFredAnonClaims + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/permission-denied'); + }); + + it("should use the impersonated user's auth.uid", async () => { + const insertResp = await getDataConnect(connectorConfig) + .executeMutation( + 'InsertEmailImpersonation', + { id: generateEmailId() }, + optsAuthorizedFredAnonClaims + ); + expect(insertResp.data.email_insert.id).to.not.be.undefined; + const queryResp = await getDataConnect(connectorConfig).executeQuery( + 'GetEmail', + { id: insertResp.data.email_insert.id }, + optsAuthorizedFredAnonClaims + ); + expect(queryResp.data.email.from.id).to.equal(fredUser.id); }); }); - it('executeGraphql() successfully executes an impersonated query with non-existing authenticated claims', - async () => { - const resp = await getDataConnect(connectorConfig).executeGraphql( - queryListUsers, optsNonExistingClaims); - expect(resp.data.users).to.be.not.empty; - expect(resp.data.users.length).to.equal(initialState.users.length); - resp.data.users.forEach((user) => { - expect(initialState.users).to.deep.include(user); - }); + describe('with authenticated user impersonation', () => { + it('should successfully execute a mutation with @auth(level: PUBLIC)', async () => { + const resp = await getDataConnect(connectorConfig) + .executeMutation( + 'InsertEmailPublic', + { id: generateEmailId() }, + optsAuthorizedFredClaims + ); + expect(resp.data.email_insert.id).to.not.be.undefined; + }); + + it('should fail to execute a mutation with @auth(level: USER_ANON)', async () => { + const resp = await getDataConnect(connectorConfig) + .executeMutation( + 'InsertEmailUserAnon', + { id: generateEmailId() }, + optsAuthorizedFredClaims + ); + expect(resp.data.email_insert.id).to.not.be.undefined; + }); + + it('should successfully execute a mutation with @auth(level: USER)', async () => { + const resp = await getDataConnect(connectorConfig) + .executeMutation( + 'InsertEmailUser', + { id: generateEmailId() }, + optsAuthorizedFredClaims + ); + expect(resp.data.email_insert.id).to.not.be.undefined; + }); + + it('should fail to execute a mutation with @auth(level: USER_EMAIL_VERIFIED)', () => { + return getDataConnect(connectorConfig).executeMutation( + 'InsertEmailUserEmailVerified', + { id: generateEmailId() }, + optsAuthorizedFredClaims + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/unauthenticated'); + }); + + it('should fail to execute a mutation with @auth(level: NO_ACCESS)', async () => { + return getDataConnect(connectorConfig).executeMutation( + 'InsertEmailNoAccess', + { id: generateEmailId() }, + optsAuthorizedFredClaims + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/permission-denied'); }); - }); - describe('NO_ACCESS Auth Policy', () => { - it('executeGraphql() should throw for an impersonated query with authenticated claims', async () => { - return await getDataConnect(connectorConfig).executeGraphql(queryListEmails, optsAuthorizedFredClaims) - .should.eventually.be.rejected.and.has.property('code', 'data-connect/permission-denied'); + it("should use the impersonated user's auth.uid", async () => { + const insertResp = await getDataConnect(connectorConfig) + .executeMutation( + 'InsertEmailImpersonation', + { id: generateEmailId() }, + optsAuthorizedFredClaims + ); + expect(insertResp.data.email_insert.id).to.not.be.undefined; + const queryResp = await getDataConnect(connectorConfig).executeQuery( + 'GetEmail', + { id: insertResp.data.email_insert.id }, + optsAuthorizedFredClaims + ); + expect(queryResp.data.email.from.id).to.equal(fredUser.id); + }); }); - it('executeGraphql() should throw for an impersonated query with unauthenticated claims', async () => { - return await getDataConnect(connectorConfig).executeGraphql(queryListEmails, optsUnauthorizedClaims) - .should.eventually.be.rejected.and.has.property('code', 'data-connect/permission-denied'); + describe('with authenticated email verified user impersonation', () => { + it('should successfully execute a mutation with @auth(level: PUBLIC)', async () => { + const resp = await getDataConnect(connectorConfig) + .executeMutation( + 'InsertEmailPublic', + { id: generateEmailId() }, + optsAuthorizedFredEmailVerifiedClaims + ); + expect(resp.data.email_insert.id).to.not.be.undefined; + }); + + it('should successfully execute a mutation with @auth(level: USER_ANON)', async () => { + const resp = await getDataConnect(connectorConfig) + .executeMutation( + 'InsertEmailUserAnon', + { id: generateEmailId() }, + optsAuthorizedFredEmailVerifiedClaims + ); + expect(resp.data.email_insert.id).to.not.be.undefined; + }); + + it('should successfully execute a mutation with @auth(level: USER)', async () => { + const resp = await getDataConnect(connectorConfig) + .executeMutation( + 'InsertEmailUser', + { id: generateEmailId() }, + optsAuthorizedFredEmailVerifiedClaims + ); + expect(resp.data.email_insert.id).to.not.be.undefined; + }); + + it('should successfully execute a mutation with @auth(level: USER_EMAIL_VERIFIED)', async () => { + const resp = await getDataConnect(connectorConfig) + .executeMutation( + 'InsertEmailUserEmailVerified', + { id: generateEmailId() }, + optsAuthorizedFredEmailVerifiedClaims + ); + expect(resp.data.email_insert.id).to.not.be.undefined; + }); + + it('should fail to execute a mutation with @auth(level: NO_ACCESS)', async () => { + return getDataConnect(connectorConfig).executeMutation( + 'InsertEmailNoAccess', + { id: generateEmailId() }, + optsAuthorizedFredEmailVerifiedClaims + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/permission-denied'); + }); + + it("should use the impersonated user's auth.uid", async () => { + const insertResp = await getDataConnect(connectorConfig) + .executeMutation( + 'InsertEmailImpersonation', + { id: generateEmailId() }, + optsAuthorizedFredEmailVerifiedClaims + ); + expect(insertResp.data.email_insert.id).to.not.be.undefined; + const queryResp = await getDataConnect(connectorConfig).executeQuery( + 'GetEmail', + { id: insertResp.data.email_insert.id }, + optsAuthorizedFredEmailVerifiedClaims + ); + expect(queryResp.data.email.from.id).to.equal(fredUser.id); + }); }); - it('executeGraphql() should throw for an impersonated query with non-existing authenticated claims', - async () => { - return await getDataConnect(connectorConfig).executeGraphql(queryListEmails, optsNonExistingClaims) - .should.eventually.be.rejected.and.has.property('code', 'data-connect/permission-denied'); + describe('with no impersonation, bypassing auth policies', () => { + it('should successfully execute a mutation with @auth(level: PUBLIC)', async () => { + const resp = await getDataConnect(connectorConfig) + .executeMutation( + 'InsertEmailPublic', + { id: generateEmailId() } + ); + expect(resp.data.email_insert.id).to.not.be.undefined; + }); + + it('should successfully execute a mutation with @auth(level: USER_ANON)', async () => { + const resp = await getDataConnect(connectorConfig) + .executeMutation( + 'InsertEmailUserAnon', + { id: generateEmailId() } + ); + expect(resp.data.email_insert.id).to.not.be.undefined; + }); + + it('should successfully execute a mutation with @auth(level: USER)', async () => { + const resp = await getDataConnect(connectorConfig) + .executeMutation( + 'InsertEmailUser', + { id: generateEmailId() } + ); + expect(resp.data.email_insert.id).to.not.be.undefined; + }); + + it('should successfully execute a mutation with @auth(level: USER_EMAIL_VERIFIED)', async () => { + const resp = await getDataConnect(connectorConfig) + .executeMutation( + 'InsertEmailUserEmailVerified', + { id: generateEmailId() } + ); + expect(resp.data.email_insert.id).to.not.be.undefined; + }); + + it('should successfully execute a mutation with @auth(level: NO_ACCESS)', async () => { + const resp = await getDataConnect(connectorConfig) + .executeMutation( + 'InsertEmailNoAccess', + { id: generateEmailId() } + ); + expect(resp.data.email_insert.id).to.not.be.undefined; }); + + it("should fail to execute a mutation using the impersonated user's auth.uid", async () => { + return getDataConnect(connectorConfig).executeMutation( + 'InsertEmailImpersonation', + { id: generateEmailId() }, + ).should.eventually.be.rejected.and.have.property('code', 'data-connect/query-error'); + }); + }); }); }); }); diff --git a/test/integration/dataconnect/dataconnect/my-connector/mutations.gql b/test/integration/dataconnect/dataconnect/my-connector/mutations.gql index 10a870880e..f3bfbeeb89 100644 --- a/test/integration/dataconnect/dataconnect/my-connector/mutations.gql +++ b/test/integration/dataconnect/dataconnect/my-connector/mutations.gql @@ -1,7 +1,8 @@ mutation upsertFredUser @auth(level: NO_ACCESS) { user_upsert(data: { id: "fred_id", address: "32 Elm St.", name: "Fred" }) } -mutation updateFredrickUserImpersonation @auth(level: USER) { +mutation updateFredrickUserImpersonation +@auth(level: USER, insecureReason: "test") { user_update( key: { id_expr: "auth.uid" } data: { address: "64 Elm St. North", name: "Fredrick" } @@ -23,6 +24,11 @@ mutation upsertJeffEmail @auth(level: NO_ACCESS) { ) } +mutation InsertUser($id: String!, $name: String!, $address: String!) +@auth(level: PUBLIC, insecureReason: "test") { + user_insert(data: { id: $id, name: $name, address: $address }) +} + mutation InsertEmailPublic($id: String!) @auth(level: PUBLIC, insecureReason: "test") { email_insert( @@ -82,7 +88,8 @@ mutation InsertEmailNoAccess($id: String!) @auth(level: NO_ACCESS) { } ) } -mutation InsertEmailImpersonation($id: String!) @auth(level: NO_ACCESS) { +mutation InsertEmailImpersonation($id: String!) +@auth(level: USER_ANON, insecureReason: "test") { email_insert( data: { id: $id diff --git a/test/integration/dataconnect/dataconnect/my-connector/queries.gql b/test/integration/dataconnect/dataconnect/my-connector/queries.gql index b93da41828..b4c3c92213 100644 --- a/test/integration/dataconnect/dataconnect/my-connector/queries.gql +++ b/test/integration/dataconnect/dataconnect/my-connector/queries.gql @@ -41,10 +41,11 @@ query ListUsersImpersonationAnon @auth(level: USER_ANON) { address } } -query GetUser($id: User_Key!) @auth(level: NO_ACCESS) { +query GetUser($id: User_Key!) @auth(level: PUBLIC, insecureReason: "test") { user(key: $id) { id name + address } } @@ -59,7 +60,7 @@ query ListEmails @auth(level: NO_ACCESS) { } } } -query GetEmail($id: String!) @auth(level: NO_ACCESS) { +query GetEmail($id: String!) @auth(level: USER_ANON, insecureReason: "test") { email(id: $id) { id subject diff --git a/test/unit/data-connect/data-connect-api-client-internal.spec.ts b/test/unit/data-connect/data-connect-api-client-internal.spec.ts index 67c1541cd6..628608a9e9 100644 --- a/test/unit/data-connect/data-connect-api-client-internal.spec.ts +++ b/test/unit/data-connect/data-connect-api-client-internal.spec.ts @@ -29,6 +29,7 @@ import { DATA_CONNECT_ERROR_CODE_MAPPING, DataConnectApiClient, FirebaseDataConn import { FirebaseApp } from '../../../src/app/firebase-app'; import { ConnectorConfig } from '../../../src/data-connect'; import { getMetricsHeader, getSdkVersion } from '../../../src/utils'; +import { OperationOptions } from '../../../src/data-connect/data-connect-api'; describe('DataConnectApiClient', () => { @@ -75,6 +76,7 @@ describe('DataConnectApiClient', () => { const connectorConfig: ConnectorConfig = { location: 'us-west2', serviceId: 'my-service', + connector: 'my-connector', }; const clientWithoutProjectId = new DataConnectApiClient( @@ -119,48 +121,307 @@ describe('DataConnectApiClient', () => { }); describe('executeGraphql', () => { - it('should reject when project id is not available', () => { - return clientWithoutProjectId.executeGraphql('query', {}) - .should.eventually.be.rejectedWith(noProjectId); + describe('should reject with an appropriate error response on failure', () => { + it('should reject when project id is not available', () => { + return clientWithoutProjectId.executeGraphql('query', {}) + .should.eventually.be.rejectedWith(noProjectId); + }); + + it('should throw an error if query is not a non-empty string', async () => { + await expect(apiClient.executeGraphql('')).to.be.rejectedWith( + FirebaseDataConnectError, + '`query` must be a non-empty string.' + ); + await expect(apiClient.executeGraphql(undefined as any)).to.be.rejectedWith( + FirebaseDataConnectError, + '`query` must be a non-empty string.' + ); + }); + + const invalidQueries = [null, NaN, 0, 1, true, false, [], {}, { a: 1 }, _.noop]; + invalidQueries.forEach((invalidQuery) => { + it('should throw given a non-string query: ' + JSON.stringify(invalidQuery), async () => { + await expect(apiClient.executeGraphql(invalidQuery as any)).to.be.rejectedWith( + FirebaseDataConnectError, + '`query` must be a non-empty string.' + ); + }); + }); + + const invalidOptions = [null, NaN, 0, 1, true, false, [], _.noop]; + invalidOptions.forEach((invalidOption) => { + it('should throw given an invalid options object: ' + JSON.stringify(invalidOption), async () => { + await expect(apiClient.executeGraphql('query', invalidOption as any)).to.be.rejectedWith( + FirebaseDataConnectError, + 'GraphqlOptions must be a non-null object' + ); + }); + }); + + it('should reject when a full platform error response is received', () => { + sandbox + .stub(HttpClient.prototype, 'send') + .rejects(utils.errorFrom(ERROR_RESPONSE, 404)); + const expected = new FirebaseDataConnectError('not-found', 'Requested entity not found'); + return apiClient.executeGraphql('query', {}) + .should.eventually.be.rejected.and.deep.include(expected); + }); + + it('should reject with unknown-error when error code is not present', () => { + sandbox + .stub(HttpClient.prototype, 'send') + .rejects(utils.errorFrom({}, 404)); + const expected = new FirebaseDataConnectError('unknown-error', 'Unknown server error: {}'); + return apiClient.executeGraphql('query', {}) + .should.eventually.be.rejected.and.deep.include(expected); + }); + + it('should reject with unknown-error for non-json response', () => { + sandbox + .stub(HttpClient.prototype, 'send') + .rejects(utils.errorFrom('not json', 404)); + const expected = new FirebaseDataConnectError( + 'unknown-error', 'Unexpected response with status: 404 and body: not json'); + return apiClient.executeGraphql('query', {}) + .should.eventually.be.rejected.and.deep.include(expected); + }); + + it('should reject when rejected with a FirebaseDataConnectError', () => { + const expected = new FirebaseDataConnectError('internal-error', 'socket hang up'); + sandbox + .stub(HttpClient.prototype, 'send') + .rejects(expected); + return apiClient.executeGraphql('query', {}) + .should.eventually.be.rejected.and.deep.include(expected); + }); }); - it('should throw an error if query is not a non-empty string', async () => { - await expect(apiClient.executeGraphql('')).to.be.rejectedWith( - FirebaseDataConnectError, - '`query` must be a non-empty string.' - ); - await expect(apiClient.executeGraphql(undefined as any)).to.be.rejectedWith( - FirebaseDataConnectError, - '`query` must be a non-empty string.' - ); + it('should resolve with the GraphQL response on success', async () => { + interface UsersResponse { + users: [ + user: { + id: string; + name: string; + address: string; + } + ]; + } + const stub = sandbox + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom(TEST_RESPONSE, 200)); + const resp = await apiClient.executeGraphql('query', {}); + expect(resp.data.users).to.be.not.empty; + expect(resp.data.users[0].name).to.be.not.undefined; + expect(resp.data.users[0].address).to.be.not.undefined; + expect(resp.data.users).to.deep.equal(TEST_RESPONSE.data.users); + expect(stub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: `https://firebasedataconnect.googleapis.com/v1/projects/test-project/locations/${connectorConfig.location}/services/${connectorConfig.serviceId}:executeGraphql`, + headers: EXPECTED_HEADERS, + data: { query: 'query' } + }); }); - const invalidQueries = [null, NaN, 0, 1, true, false, [], {}, { a: 1 }, _.noop]; - invalidQueries.forEach((invalidQuery) => { - it('should throw given a non-string query: ' + JSON.stringify(invalidQuery), async () => { - await expect(apiClient.executeGraphql(invalidQuery as any)).to.be.rejectedWith( - FirebaseDataConnectError, - '`query` must be a non-empty string.' + it('should use DATA_CONNECT_EMULATOR_HOST if set', async () => { + process.env.DATA_CONNECT_EMULATOR_HOST = 'localhost:9399'; + const stub = sandbox + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom(TEST_RESPONSE, 200)); + await apiClient.executeGraphql('query', {}); + expect(stub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: `http://localhost:9399/v1/projects/test-project/locations/${connectorConfig.location}/services/${connectorConfig.serviceId}:executeGraphql`, + headers: EMULATOR_EXPECTED_HEADERS, + data: { query: 'query' } + }); + }); + }); + + describe('executeQuery', () => { + const unauthenticatedOptions: OperationOptions = { impersonate: { unauthenticated: true } }; + const authenticatedOptions: OperationOptions = { impersonate: { authClaims: { sub: 'authenticated-UUID' } } }; + + describe('should reject with an appropriate error response on failure', () => { + it('should reject when no operationName is provided', () => { + apiClient.executeQuery( '', undefined, unauthenticatedOptions) + .should.eventually.be.rejectedWith('`name` must be a non-empty string.'); + apiClient.executeQuery(undefined as unknown as string, undefined, unauthenticatedOptions) + .should.eventually.be.rejectedWith('`name` must be a non-empty string.'); + }); + + it('should reject when project id is not available', () => { + clientWithoutProjectId.executeQuery( + 'unauthenticated query', + undefined, + unauthenticatedOptions + ).should.eventually.be.rejectedWith(noProjectId); + }); + + it('should reject when no connectorId is provided', () => { + apiClient = new DataConnectApiClient( + { location: connectorConfig.location, serviceId: connectorConfig.serviceId }, + app ); + apiClient.executeQuery('unauthenticated query', undefined, unauthenticatedOptions) + .should.eventually.be.rejectedWith( + `The 'connectorConfig.connector' field used to instantiate your Data Connect + instance must be a non-empty string (the connectorId) when calling executeQuery or executeMutation.`); + }); + + it('should reject when a full platform error response is received', () => { + sandbox + .stub(HttpClient.prototype, 'send') + .rejects(utils.errorFrom(ERROR_RESPONSE, 404)); + const expected = new FirebaseDataConnectError('not-found', 'Requested entity not found'); + return apiClient.executeQuery('unauthenticated query', undefined, unauthenticatedOptions) + .should.eventually.be.rejected.and.deep.include(expected); + }); + + it('should reject with unknown-error when error code is not present', () => { + sandbox + .stub(HttpClient.prototype, 'send') + .rejects(utils.errorFrom({}, 404)); + const expected = new FirebaseDataConnectError('unknown-error', 'Unknown server error: {}'); + return apiClient.executeQuery('unauthenticated query', undefined, unauthenticatedOptions) + .should.eventually.be.rejected.and.deep.include(expected); + }); + + it('should reject with unknown-error for non-json response', () => { + sandbox + .stub(HttpClient.prototype, 'send') + .rejects(utils.errorFrom('not json', 404)); + const expected = new FirebaseDataConnectError( + 'unknown-error', 'Unexpected response with status: 404 and body: not json'); + return apiClient.executeQuery('unauthenticated query', undefined, unauthenticatedOptions) + .should.eventually.be.rejected.and.deep.include(expected); + }); + + it('should reject when rejected with a FirebaseDataConnectError', () => { + const expected = new FirebaseDataConnectError('internal-error', 'socket hang up'); + sandbox + .stub(HttpClient.prototype, 'send') + .rejects(expected); + return apiClient.executeQuery('unauthenticated query', undefined, unauthenticatedOptions) + .should.eventually.be.rejected.and.deep.include(expected); }); }); - const invalidOptions = [null, NaN, 0, 1, true, false, [], _.noop]; - invalidOptions.forEach((invalidOption) => { - it('should throw given an invalid options object: ' + JSON.stringify(invalidOption), async () => { - await expect(apiClient.executeGraphql('query', invalidOption as any)).to.be.rejectedWith( - FirebaseDataConnectError, - 'GraphqlOptions must be a non-null object' + describe('should resolve with the GraphQL response on success', () => { + interface UsersResponse { + users: [ + user: { + id: string; + name: string; + address: string; + } + ]; + } + it('for an unauthenticated request', async () => { + const stub = sandbox + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom(TEST_RESPONSE, 200)); + const resp = await apiClient.executeQuery( + 'unauthenticated query', + undefined, + unauthenticatedOptions + ); + expect(resp.data.users).to.be.not.empty; + expect(resp.data.users[0].name).to.be.not.undefined; + expect(resp.data.users[0].address).to.be.not.undefined; + expect(resp.data.users).to.deep.equal(TEST_RESPONSE.data.users); + expect(stub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: `https://firebasedataconnect.googleapis.com/v1/projects/test-project/locations/${connectorConfig.location}/services/${connectorConfig.serviceId}/connectors/${connectorConfig.connector}:impersonateQuery`, + headers: EXPECTED_HEADERS, + data: { + operationName: 'unauthenticated query', + extensions: unauthenticatedOptions + } + }); + }); + + it('for an authenticated request', async () => { + const stub = sandbox + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom(TEST_RESPONSE, 200)); + const resp = await apiClient.executeQuery( + 'authenticated query', + undefined, + authenticatedOptions ); + expect(resp.data.users).to.be.not.empty; + expect(resp.data.users[0].name).to.be.not.undefined; + expect(resp.data.users[0].address).to.be.not.undefined; + expect(resp.data.users).to.deep.equal(TEST_RESPONSE.data.users); + expect(stub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: `https://firebasedataconnect.googleapis.com/v1/projects/test-project/locations/${connectorConfig.location}/services/${connectorConfig.serviceId}/connectors/${connectorConfig.connector}:impersonateQuery`, + headers: EXPECTED_HEADERS, + data: { + operationName: 'authenticated query', + extensions: { impersonate: authenticatedOptions.impersonate } + } + }); + }); + }); + + it('should use DATA_CONNECT_EMULATOR_HOST if set', async () => { + process.env.DATA_CONNECT_EMULATOR_HOST = 'localhost:9399'; + const stub = sandbox + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom(TEST_RESPONSE, 200)); + await apiClient.executeQuery( + 'unauthenticated query', + undefined, + unauthenticatedOptions + ); + expect(stub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: `http://localhost:9399/v1/projects/test-project/locations/${connectorConfig.location}/services/${connectorConfig.serviceId}/connectors/${connectorConfig.connector}:impersonateQuery`, + headers: EMULATOR_EXPECTED_HEADERS, + data: { + operationName: 'unauthenticated query', + extensions: unauthenticatedOptions + } }); }); + }); + + const unauthenticatedOptions: OperationOptions = + { impersonate: { unauthenticated: true } }; + const authenticatedOptions: OperationOptions = + { impersonate: { authClaims: { sub: 'authenticated-UUID' } } }; + + describe('executeMutation', () => { + it('should reject when no operationName is provided', () => { + apiClient.executeMutation('', undefined, unauthenticatedOptions) + .should.eventually.be.rejectedWith('`name` must be a non-empty string.'); + apiClient.executeMutation(undefined as unknown as string, undefined, unauthenticatedOptions) + .should.eventually.be.rejectedWith('`name` must be a non-empty string.'); + }); + + it('should reject when project id is not available', () => { + clientWithoutProjectId.executeMutation('unauthenticated mutation', undefined, unauthenticatedOptions) + .should.eventually.be.rejectedWith(noProjectId); + }); + + it('should reject when no connectorId is provided', () => { + apiClient = new DataConnectApiClient( + { location: connectorConfig.location, serviceId: connectorConfig.serviceId }, + app + ); + apiClient.executeMutation('unauthenticated mutation', undefined, unauthenticatedOptions) + .should.eventually.be.rejectedWith( + `The 'connectorConfig.connector' field used to instantiate your Data Connect + instance must be a non-empty string (the connectorId) when calling executeQuery or executeMutation.`); + }); it('should reject when a full platform error response is received', () => { sandbox .stub(HttpClient.prototype, 'send') .rejects(utils.errorFrom(ERROR_RESPONSE, 404)); const expected = new FirebaseDataConnectError('not-found', 'Requested entity not found'); - return apiClient.executeGraphql('query', {}) + return apiClient.executeMutation('unauthenticated mutation', undefined, unauthenticatedOptions) .should.eventually.be.rejected.and.deep.include(expected); }); @@ -169,7 +430,7 @@ describe('DataConnectApiClient', () => { .stub(HttpClient.prototype, 'send') .rejects(utils.errorFrom({}, 404)); const expected = new FirebaseDataConnectError('unknown-error', 'Unknown server error: {}'); - return apiClient.executeGraphql('query', {}) + return apiClient.executeMutation('unauthenticated mutation', undefined, unauthenticatedOptions) .should.eventually.be.rejected.and.deep.include(expected); }); @@ -179,7 +440,7 @@ describe('DataConnectApiClient', () => { .rejects(utils.errorFrom('not json', 404)); const expected = new FirebaseDataConnectError( 'unknown-error', 'Unexpected response with status: 404 and body: not json'); - return apiClient.executeGraphql('query', {}) + return apiClient.executeMutation('unauthenticated mutation', undefined, unauthenticatedOptions) .should.eventually.be.rejected.and.deep.include(expected); }); @@ -188,11 +449,11 @@ describe('DataConnectApiClient', () => { sandbox .stub(HttpClient.prototype, 'send') .rejects(expected); - return apiClient.executeGraphql('query', {}) + return apiClient.executeMutation('unauthenticated mutation', undefined, unauthenticatedOptions) .should.eventually.be.rejected.and.deep.include(expected); }); - it('should resolve with the GraphQL response on success', () => { + describe('should resolve with the GraphQL response on success', () => { interface UsersResponse { users: [ user: { @@ -202,38 +463,67 @@ describe('DataConnectApiClient', () => { } ]; } - const stub = sandbox - .stub(HttpClient.prototype, 'send') - .resolves(utils.responseFrom(TEST_RESPONSE, 200)); - return apiClient.executeGraphql('query', {}) - .then((resp) => { - expect(resp.data.users).to.be.not.empty; - expect(resp.data.users[0].name).to.be.not.undefined; - expect(resp.data.users[0].address).to.be.not.undefined; - expect(resp.data.users).to.deep.equal(TEST_RESPONSE.data.users); - expect(stub).to.have.been.calledOnce.and.calledWith({ - method: 'POST', - url: `https://firebasedataconnect.googleapis.com/v1/projects/test-project/locations/${connectorConfig.location}/services/${connectorConfig.serviceId}:executeGraphql`, - headers: EXPECTED_HEADERS, - data: { query: 'query' } - }); + + it('for an unauthenticated request', async () => { + const stub = sandbox + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom(TEST_RESPONSE, 200)); + const resp = await apiClient.executeMutation( + 'unauthenticated mutation', undefined, unauthenticatedOptions + ) + expect(resp.data.users).to.be.not.empty; + expect(resp.data.users[0].name).to.be.not.undefined; + expect(resp.data.users[0].address).to.be.not.undefined; + expect(resp.data.users).to.deep.equal(TEST_RESPONSE.data.users); + expect(stub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: `https://firebasedataconnect.googleapis.com/v1/projects/test-project/locations/${connectorConfig.location}/services/${connectorConfig.serviceId}/connectors/${connectorConfig.connector}:impersonateMutation`, + headers: EXPECTED_HEADERS, + data: { + operationName: 'unauthenticated mutation', + extensions: unauthenticatedOptions + } + }); + }); + + it('for an authenticated request', async () => { + const stub = sandbox + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom(TEST_RESPONSE, 200)); + const resp = await apiClient.executeMutation( + 'authenticated mutation', undefined, authenticatedOptions + ); + expect(resp.data.users).to.be.not.empty; + expect(resp.data.users[0].name).to.be.not.undefined; + expect(resp.data.users[0].address).to.be.not.undefined; + expect(resp.data.users).to.deep.equal(TEST_RESPONSE.data.users); + expect(stub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: `https://firebasedataconnect.googleapis.com/v1/projects/test-project/locations/${connectorConfig.location}/services/${connectorConfig.serviceId}/connectors/${connectorConfig.connector}:impersonateMutation`, + headers: EXPECTED_HEADERS, + data: { + operationName: 'authenticated mutation', + extensions: authenticatedOptions + } }); + }); }); - it('should use DATA_CONNECT_EMULATOR_HOST if set', () => { + it('should use DATA_CONNECT_EMULATOR_HOST if set', async () => { process.env.DATA_CONNECT_EMULATOR_HOST = 'localhost:9399'; const stub = sandbox .stub(HttpClient.prototype, 'send') .resolves(utils.responseFrom(TEST_RESPONSE, 200)); - return apiClient.executeGraphql('query', {}) - .then(() => { - expect(stub).to.have.been.calledOnce.and.calledWith({ - method: 'POST', - url: `http://localhost:9399/v1/projects/test-project/locations/${connectorConfig.location}/services/${connectorConfig.serviceId}:executeGraphql`, - headers: EMULATOR_EXPECTED_HEADERS, - data: { query: 'query' } - }); - }); + await apiClient.executeMutation('unauthenticated mutation', undefined, unauthenticatedOptions); + expect(stub).to.have.been.calledOnce.and.calledWith({ + method: 'POST', + url: `http://localhost:9399/v1/projects/test-project/locations/${connectorConfig.location}/services/${connectorConfig.serviceId}/connectors/${connectorConfig.connector}:impersonateMutation`, + headers: EMULATOR_EXPECTED_HEADERS, + data: { + operationName: 'unauthenticated mutation', + extensions: unauthenticatedOptions + } + }); }); it('should use gen headers if set on success', () => { interface UsersResponse { @@ -276,6 +566,7 @@ describe('DataConnectApiClient CRUD helpers', () => { const connectorConfig: ConnectorConfig = { location: 'us-west1', serviceId: 'my-crud-service', + connector: 'my-crud-connector', }; const mockOptions = { @@ -349,9 +640,9 @@ describe('DataConnectApiClient CRUD helpers', () => { const expectedMutation = ` mutation { ${formatedTableName}_insert(data: { - name: "test", - value: 123 - }) + name: "test", + value: 123 + }) }`; await apiClient.insert(tableName, simpleData); expect(executeGraphqlStub).to.have.been.calledOnceWithExactly(normalizeGraphQLString(expectedMutation)); @@ -362,9 +653,9 @@ describe('DataConnectApiClient CRUD helpers', () => { const expectedMutation = ` mutation { ${formatedTableName}_insert(data: { - id: "abc", active: true, scores: [10, 20], - info: { nested: "yes/no \\"quote\\" \\\\slash\\\\" } - }) + id: "abc", active: true, scores: [10, 20], + info: { nested: "yes/no \\"quote\\" \\\\slash\\\\" } + }) }`; await apiClient.insert(tableName, complexData); expect(executeGraphqlStub).to.have.been.calledOnceWithExactly(normalizeGraphQLString(expectedMutation)); @@ -374,12 +665,12 @@ describe('DataConnectApiClient CRUD helpers', () => { const expectedMutation = ` mutation { ${formatedTableName}_insert(data: { - genre: "Action", - title: "Die Hard", - ratings: null, - director: {}, - extras: [1, null, "hello", null, { a: 1 }] - }) + genre: "Action", + title: "Die Hard", + ratings: null, + director: {}, + extras: [1, null, "hello", null, { a: 1 }] + }) }`; await apiClient.insert(tableName, dataWithUndefined); expect(executeGraphqlStub).to.have.been.calledOnceWithExactly(normalizeGraphQLString(expectedMutation)); @@ -435,7 +726,7 @@ describe('DataConnectApiClient CRUD helpers', () => { mutation { ${formatedTableName}_insertMany(data: [{ id: "a", active: true, info: { nested: "n1 \\"quote\\"" } }, { id: "b", scores: [1, 2], - info: { nested: "n2/\\\\" } }]) }`; + info: { nested: "n2/\\\\" } }]) }`; await apiClient.insertMany(tableName, complexDataArray); expect(executeGraphqlStub).to.have.been.calledOnceWithExactly(normalizeGraphQLString(expectedMutation)); }); @@ -448,18 +739,18 @@ describe('DataConnectApiClient CRUD helpers', () => { const expectedMutation = ` mutation { ${formatedTableName}_insertMany(data: [{ - genre: "Action", - title: "Die Hard", - ratings: null, - director: {}, - extras: [1, null, "hello", null, { a: 1 }] + genre: "Action", + title: "Die Hard", + ratings: null, + director: {}, + extras: [1, null, "hello", null, { a: 1 }] }, { - genre: "Action", - title: "Die Hard", - ratings: null, - director: {}, - extras: [1, null, "hello", null, { a: 1 }] + genre: "Action", + title: "Die Hard", + ratings: null, + director: {}, + extras: [1, null, "hello", null, { a: 1 }] }]) }`; await apiClient.insertMany(tableName, dataArray); @@ -523,12 +814,12 @@ describe('DataConnectApiClient CRUD helpers', () => { const expectedMutation = ` mutation { ${formatedTableName}_upsert(data: { - genre: "Action", - title: "Die Hard", - ratings: null, - director: {}, - extras: [1, null, "hello", null, { a: 1 }] - }) + genre: "Action", + title: "Die Hard", + ratings: null, + director: {}, + extras: [1, null, "hello", null, { a: 1 }] + }) }`; await apiClient.upsert(tableName, dataWithUndefined); expect(executeGraphqlStub).to.have.been.calledOnceWithExactly(normalizeGraphQLString(expectedMutation)); @@ -594,18 +885,18 @@ describe('DataConnectApiClient CRUD helpers', () => { const expectedMutation = ` mutation { ${formatedTableName}_upsertMany(data: [{ - genre: "Action", - title: "Die Hard", - ratings: null, - director: {}, - extras: [1, null, "hello", null, { a: 1 }] + genre: "Action", + title: "Die Hard", + ratings: null, + director: {}, + extras: [1, null, "hello", null, { a: 1 }] }, { - genre: "Action", - title: "Die Hard", - ratings: null, - director: {}, - extras: [1, null, "hello", null, { a: 1 }] + genre: "Action", + title: "Die Hard", + ratings: null, + director: {}, + extras: [1, null, "hello", null, { a: 1 }] }]) }`; await apiClient.upsertMany(tableName, dataArray); diff --git a/test/unit/data-connect/data-connect.spec.ts b/test/unit/data-connect/data-connect.spec.ts new file mode 100644 index 0000000000..992d1c3861 --- /dev/null +++ b/test/unit/data-connect/data-connect.spec.ts @@ -0,0 +1,226 @@ +import * as sinon from 'sinon'; +import * as mocks from '../../resources/mocks'; +import { ConnectorConfig, DataConnect, getDataConnect, OperationOptions } from '../../../src/data-connect'; +import { expect } from 'chai'; +import { DataConnectApiClient } from '../../../src/data-connect/data-connect-api-client-internal'; +import { FirebaseApp } from '../../../src/app/firebase-app'; +import * as appIndex from '../../../src/app/lifecycle'; + +describe('DataConnect', () => { + const mockOptions = { + credential: new mocks.MockCredential(), + projectId: 'test-project', + }; + const connectorConfig: ConnectorConfig = { + location: 'us-west2', + serviceId: 'my-service', + connector: 'my-connector', + }; + let mockApp: FirebaseApp; + let dc: DataConnect; + + interface RequiredVariables { + limit: number; + } + + type OptionalVariables = Partial | undefined; + + interface Data { + data: number; + } + + const operationName = 'operation'; + const requiredVariables: RequiredVariables = { limit: 1 }; + const optionalVariables: OptionalVariables = { limit: 2 }; + const options: OperationOptions = { impersonate: { unauthenticated: true } }; + const data: Data = { data: 99 }; + + let executeQueryStub: sinon.SinonStub; + let executeMutationStub: sinon.SinonStub; + let getAppStub: sinon.SinonStub; + + beforeEach(() => { + mockApp = mocks.appWithOptions(mockOptions); + getAppStub = sinon.stub(appIndex, 'getApp').returns(mockApp); + executeQueryStub = sinon + .stub(DataConnectApiClient.prototype, 'executeQuery') + .resolves(data); + executeMutationStub = sinon + .stub(DataConnectApiClient.prototype, 'executeMutation') + .resolves(data); + dc = getDataConnect(connectorConfig); + }); + + afterEach(() => { + executeQueryStub.restore(); + executeMutationStub.restore(); + getAppStub.restore(); + return mockApp.delete(); + }); + + describe('executeQuery()', () => { + describe('should handle method and argument overload correctly', async () => { + describe('with required variables', () => { + describe('with a Variable type parameter provided', () => { + describe('with variables provided', () => { + it('with options provided', async () => { + await dc.executeQuery(operationName, requiredVariables, options); + expect(executeQueryStub.calledOnceWithExactly(operationName, requiredVariables, options)).to.be.true; + }); + + it('WITHOUT options provided', async () => { + await dc.executeQuery(operationName, requiredVariables); + expect(executeQueryStub.calledOnceWithExactly(operationName, requiredVariables, undefined)).to.be.true; + }); + }); + }); + + describe('WITHOUT a Variable type parameter provided', () => { + describe('with variables provided', () => { + it('with options provided', async () => { + await dc.executeQuery(operationName, requiredVariables, options); + expect(executeQueryStub.calledOnceWithExactly(operationName, requiredVariables, options)).to.be.true; + }); + + it('WITHOUT options provided', async () => { + await dc.executeQuery(operationName, requiredVariables); + expect(executeQueryStub.calledOnceWithExactly(operationName, requiredVariables, undefined)).to.be.true; + }); + }); + }); + }); + + describe('with optional variables', () => { + describe('with a Variable type parameter provided', () => { + describe('with variables provided', () => { + it('with options provided', async () => { + await dc.executeQuery(operationName, optionalVariables, options); + expect(executeQueryStub.calledOnceWithExactly(operationName, optionalVariables, options)).to.be.true; + }); + + it('WITHOUT options provided', async () => { + await dc.executeQuery(operationName, optionalVariables); + expect(executeQueryStub.calledOnceWithExactly(operationName, optionalVariables, undefined)).to.be.true; + }); + }); + + describe('WITHOUT variables provided', () => { + it('with options provided', async () => { + await dc.executeQuery(operationName, undefined, options); + expect(executeQueryStub.calledOnceWithExactly(operationName, undefined, options)).to.be.true; + }); + + it('WITHOUT options provided', async () => { + await dc.executeQuery(operationName, undefined); + expect(executeQueryStub.calledOnceWithExactly(operationName, undefined, undefined)).to.be.true; + }); + }); + }); + + describe('WITHOUT a Variable type parameter provided', () => { + describe('with variables provided', async () => { + it('with options provided', async () => { + await dc.executeQuery(operationName, optionalVariables, options); + expect(executeQueryStub.calledOnceWithExactly(operationName, optionalVariables, options)).to.be.true; + }); + + it('WITHOUT options provided', async () => { + await dc.executeQuery(operationName, optionalVariables); + expect(executeQueryStub.calledOnceWithExactly(operationName, optionalVariables, undefined)).to.be.true; + }); + }); + + describe('WITHOUT variables provided', async () => { + it('WITHOUT options provided', async () => { + await dc.executeQuery(operationName); + expect(executeQueryStub.calledOnceWithExactly(operationName, undefined, undefined)).to.be.true; + }); + }); + }); + }); + }); + }); + + describe('executeMutation()', () => { + describe('should handle method and argument overload correctly', async () => { + describe('with required variables', () => { + describe('with a Variable type parameter provided', () => { + describe('with variables provided', () => { + it('with options provided', async () => { + await dc.executeMutation(operationName, requiredVariables, options); + expect(executeMutationStub.calledOnceWithExactly(operationName, requiredVariables, options)).to.be.true; + }); + + it('WITHOUT options provided', async () => { + await dc.executeMutation(operationName, requiredVariables); + expect(executeMutationStub.calledOnceWithExactly(operationName, requiredVariables, undefined)).to.be.true; + }); + }); + }); + + describe('WITHOUT a Variable type parameter provided', () => { + describe('with variables provided', () => { + it('with options provided', async () => { + await dc.executeMutation(operationName, requiredVariables, options); + expect(executeMutationStub.calledOnceWithExactly(operationName, requiredVariables, options)).to.be.true; + }); + + it('WITHOUT options provided', async () => { + await dc.executeMutation(operationName, requiredVariables); + expect(executeMutationStub.calledOnceWithExactly(operationName, requiredVariables, undefined)).to.be.true; + }); + }); + }); + }); + + describe('with optional variables', () => { + describe('with a Variable type parameter provided', () => { + describe('with variables provided', () => { + it('with options provided', async () => { + await dc.executeMutation(operationName, optionalVariables, options); + expect(executeMutationStub.calledOnceWithExactly(operationName, optionalVariables, options)).to.be.true; + }); + + it('WITHOUT options provided', async () => { + await dc.executeMutation(operationName, optionalVariables); + expect(executeMutationStub.calledOnceWithExactly(operationName, optionalVariables, undefined)).to.be.true; + }); + }); + + describe('WITHOUT variables provided', () => { + it('with options provided', async () => { + await dc.executeMutation(operationName, undefined, options); + expect(executeMutationStub.calledOnceWithExactly(operationName, undefined, options)).to.be.true; + }); + + it('WITHOUT options provided', async () => { + await dc.executeMutation(operationName, undefined); + expect(executeMutationStub.calledOnceWithExactly(operationName, undefined, undefined)).to.be.true; + }); + }); + }); + + describe('WITHOUT a Variable type parameter provided', () => { + describe('with variables provided', async () => { + it('with options provided', async () => { + await dc.executeMutation(operationName, optionalVariables, options); + expect(executeMutationStub.calledOnceWithExactly(operationName, optionalVariables, options)).to.be.true; + }); + + it('WITHOUT options provided', async () => { + await dc.executeMutation(operationName, optionalVariables); + expect(executeMutationStub.calledOnceWithExactly(operationName, optionalVariables, undefined)).to.be.true; + }); + }); + + describe('WITHOUT variables provided', async () => { + it('WITHOUT options provided', async () => { + await dc.executeMutation(operationName); + expect(executeMutationStub.calledOnceWithExactly(operationName, undefined, undefined)).to.be.true; + }); + }); + }); + }); + }); + }); +}); diff --git a/test/unit/data-connect/validate-admin-args.spec.ts b/test/unit/data-connect/validate-admin-args.spec.ts new file mode 100644 index 0000000000..0d940d5144 --- /dev/null +++ b/test/unit/data-connect/validate-admin-args.spec.ts @@ -0,0 +1,271 @@ +/*! + * @license + * Copyright 2025 Google LLC + * + * 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. + */ +import { expect } from 'chai'; +import * as sinon from 'sinon'; +import { ConnectorConfig, validateAdminArgs } from '../../../src/data-connect'; +import { DataConnect } from '../../../src/data-connect/data-connect'; +import { OperationOptions } from '../../../src/data-connect'; +import { + DATA_CONNECT_ERROR_CODE_MAPPING, + FirebaseDataConnectError +} from '../../../src/data-connect/data-connect-api-client-internal'; +import * as dataConnectIndex from '../../../src/data-connect/index'; + +interface IdVars { + id: string +} + +describe('validateAdminArgs()', () => { + let getDataConnectStub: sinon.SinonStub; + + const connectorConfig: ConnectorConfig = { + location: 'us-west2', + serviceId: 'my-service', + connector: 'my-connector', + }; + + const providedDcInstance = { connectorConfig: connectorConfig, source: 'PROVIDED' } as unknown as DataConnect; + const variables: IdVars = { id: 'stephenarosaj' }; + const options: OperationOptions = { impersonate: { unauthenticated: true } }; + + const invalidVariablesError = new FirebaseDataConnectError( + DATA_CONNECT_ERROR_CODE_MAPPING.INVALID_ARGUMENT, + 'Variables required.' + ); + + const stubDcInstance = { connectorConfig: connectorConfig, source: 'STUB' } as unknown as DataConnect; + beforeEach(() => { + getDataConnectStub = sinon.stub(dataConnectIndex, 'getDataConnect').returns(stubDcInstance); + }); + + afterEach(() => { + getDataConnectStub.restore(); + }); + + describe('with no variadic args', () => { + it('should call getDataConnect to generate a DataConnect instance', () => { + const { dc: dcInstance, vars: inputVars, options: inputOpts } = validateAdminArgs(connectorConfig); + expect(getDataConnectStub.calledOnce).to.be.true; + expect(dcInstance).to.deep.equal(stubDcInstance); + expect(inputVars).to.be.undefined; + expect(inputOpts).to.be.undefined; + }); + }); + + describe('with one variadic arg', () => { + it('should successfully parse the provided DataConnect', () => { + const { dc: dcInstance, vars: inputVars, options: inputOpts } = validateAdminArgs( + connectorConfig, providedDcInstance + ); + expect(dcInstance).to.deep.equal(providedDcInstance); + expect(inputVars).to.be.undefined; + expect(inputOpts).to.be.undefined; + }); + + it('should successfully parse the provided options', () => { + const { dc: dcInstance, vars: inputVars, options: inputOpts } = validateAdminArgs( + connectorConfig, options + ); + expect(dcInstance).to.deep.equal(stubDcInstance); + expect(inputVars).to.be.undefined; + expect(inputOpts).to.deep.equal(options); + }); + }); + + describe('with two variadic args', () => { + it('should successfully parse the provided DataConnect instance and options', () => { + const { dc: dcInstance, vars: inputVars, options: inputOpts } = validateAdminArgs( + connectorConfig, providedDcInstance, options + ); + expect(dcInstance).to.deep.equal(providedDcInstance); + expect(inputVars).to.be.undefined; + expect(inputOpts).to.deep.equal(options); + }); + }); + + describe('with four variadic args and hasVars = true', () => { + describe('and the first argument is a DataConnect instance', () => { + it('and variables and options are undefined', () => { + const { dc: dcInstance, vars: inputVars, options: inputOpts } = validateAdminArgs( + connectorConfig, providedDcInstance, undefined, undefined, true + ); + expect(dcInstance).to.deep.equal(providedDcInstance); + expect(inputVars).to.be.undefined; + expect(inputOpts).to.be.undefined; + }); + + it('and variables are provided, options undefined', () => { + const { dc: dcInstance, vars: inputVars, options: inputOpts } = validateAdminArgs( + connectorConfig, providedDcInstance, variables, undefined, true + ); + expect(dcInstance).to.deep.equal(providedDcInstance); + expect(inputVars).to.deep.equal(variables); + expect(inputOpts).to.be.undefined; + }); + + it('and options are provided, variables undefined', () => { + const { dc: dcInstance, vars: inputVars, options: inputOpts } = validateAdminArgs( + connectorConfig, providedDcInstance, undefined, options, true + ); + expect(dcInstance).to.deep.equal(providedDcInstance); + expect(inputVars).to.deep.be.undefined; + expect(inputOpts).to.deep.equal(options); + }); + + it('and variables and options are provided', () => { + const { dc: dcInstance, vars: inputVars, options: inputOpts } = validateAdminArgs( + connectorConfig, providedDcInstance, variables, options, true + ); + expect(dcInstance).to.deep.equal(providedDcInstance); + expect(inputVars).to.deep.equal(variables); + expect(inputOpts).to.deep.equal(options); + }); + }); + + describe('and the first argument is variables', () => { + it('and variables and options are undefined', () => { + const { dc: dcInstance, vars: inputVars, options: inputOpts } = validateAdminArgs( + connectorConfig, undefined, undefined, undefined, true + ); + expect(dcInstance).to.deep.equal(stubDcInstance); + expect(inputVars).to.be.undefined; + expect(inputOpts).to.be.undefined; + }); + + it('and variables are provided, options undefined', () => { + const { dc: dcInstance, vars: inputVars, options: inputOpts } = validateAdminArgs( + connectorConfig, variables, undefined, undefined, true + ); + expect(dcInstance).to.deep.equal(stubDcInstance); + expect(inputVars).to.deep.equal(variables); + expect(inputOpts).to.be.undefined; + }); + + it('and options are provided, variables undefined', () => { + const { dc: dcInstance, vars: inputVars, options: inputOpts } = validateAdminArgs( + connectorConfig, undefined, options, undefined, true + ); + expect(dcInstance).to.deep.equal(stubDcInstance); + expect(inputVars).to.deep.be.undefined; + expect(inputOpts).to.deep.equal(options); + }); + + it('and variables and options are provided', () => { + const { dc: dcInstance, vars: inputVars, options: inputOpts } = validateAdminArgs( + connectorConfig, variables, options, undefined, true + ); + expect(dcInstance).to.deep.equal(stubDcInstance); + expect(inputVars).to.deep.equal(variables); + expect(inputOpts).to.deep.equal(options); + }); + }); + }); + + describe('with five variadic args', () => { + describe('and validateVars = true', () => { + describe('should succeed if vars WERE provided', () => { + it('and the first argument is a DataConnect instance', () => { + expect(() => { + const { dc: dcInstance, vars: inputVars, options: inputOpts } = validateAdminArgs( + connectorConfig, providedDcInstance, variables, undefined, true, true + ); + expect(dcInstance).to.deep.equal(providedDcInstance); + expect(inputVars).to.deep.equal(variables); + expect(inputOpts).to.be.undefined; + }).to.not.throw(invalidVariablesError); + }); + + it('and the first argument is variables instance', () => { + expect(() => { + const { dc: dcInstance, vars: inputVars, options: inputOpts } = validateAdminArgs( + connectorConfig, variables, undefined, undefined, true, true + ); + expect(dcInstance).to.deep.equal(stubDcInstance); + expect(inputVars).to.deep.equal(variables); + expect(inputOpts).to.be.undefined; + }).to.not.throw(invalidVariablesError); + }); + }); + + describe('should throw if vars were NOT provided', () => { + it('and the first argument is a DataConnect instance', () => { + expect(() => { + validateAdminArgs(connectorConfig, providedDcInstance, undefined, undefined, true, true); + }).to.throw().and.have.property('code', invalidVariablesError.code); + }); + + it('and the first argument is undefined variables', () => { + expect(() => { + validateAdminArgs(connectorConfig, undefined, undefined, undefined, true, true); + }).to.throw().and.have.property('code', invalidVariablesError.code); + }); + }); + }); + + describe('and validateVars = false', () => { + describe('should succeed if vars WERE provided', () => { + it('and the first argument is a DataConnect instance', () => { + expect(() => { + const { dc: dcInstance, vars: inputVars, options: inputOpts } = validateAdminArgs( + connectorConfig, providedDcInstance, variables, undefined, true, false + ); + expect(dcInstance).to.deep.equal(providedDcInstance); + expect(inputVars).to.be.undefined; + expect(inputOpts).to.be.undefined; + }).to.not.throw(invalidVariablesError); + }); + + it('and the first argument is variables instance', () => { + expect(() => { + validateAdminArgs(connectorConfig, variables, undefined, undefined, true, false); + }).to.not.throw(invalidVariablesError); + + const { dc: dcInstance, vars: inputVars, options: inputOpts } = validateAdminArgs( + connectorConfig, variables, undefined, undefined, true, false + ); + expect(dcInstance).to.deep.equal(stubDcInstance); + expect(inputVars).to.deep.equal(variables); + expect(inputOpts).to.be.undefined; + }); + }); + + describe('should succeed if vars were NOT provided', () => { + it('and the first argument is a DataConnect instance', () => { + expect(() => { + const { dc: dcInstance, vars: inputVars, options: inputOpts } = validateAdminArgs( + connectorConfig, providedDcInstance, undefined, undefined, true, false + ); + expect(dcInstance).to.deep.equal(providedDcInstance); + expect(inputVars).to.deep.equal(variables); + expect(inputOpts).to.be.undefined; + }).to.not.throw(invalidVariablesError); + }); + + it('and the first argument is undefined variables', () => { + expect(() => { + const { dc: dcInstance, vars: inputVars, options: inputOpts } = validateAdminArgs( + connectorConfig, undefined, undefined, undefined, true, false + ); + expect(dcInstance).to.deep.equal(stubDcInstance); + expect(inputVars).to.be.undefined; + expect(inputOpts).to.be.undefined; + }).to.not.throw(invalidVariablesError); + }); + }); + }); + }); +}); diff --git a/test/unit/index.spec.ts b/test/unit/index.spec.ts index c75bf0dc8b..39ae51fd9f 100644 --- a/test/unit/index.spec.ts +++ b/test/unit/index.spec.ts @@ -121,3 +121,5 @@ import './extensions/extensions-api-client-internal.spec'; // Data Connect import './data-connect/index.spec'; import './data-connect/data-connect-api-client-internal.spec'; +import './data-connect/data-connect.spec'; +import './data-connect/validate-admin-args.spec'; From 48c8ca6552efeb636a9eef9ca91e8ab3c50229e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Nov 2025 09:57:43 -0500 Subject: [PATCH 08/10] build(deps): bump @firebase/database-compat from 2.0.6 to 2.1.0 (#3002) Bumps [@firebase/database-compat](https://github.com/firebase/firebase-js-sdk/tree/HEAD/packages/database-compat) from 2.0.6 to 2.1.0. - [Release notes](https://github.com/firebase/firebase-js-sdk/releases) - [Changelog](https://github.com/firebase/firebase-js-sdk/blob/main/packages/database-compat/CHANGELOG.md) - [Commits](https://github.com/firebase/firebase-js-sdk/commits/@firebase/database-compat@2.1.0/packages/database-compat) --- updated-dependencies: - dependency-name: "@firebase/database-compat" dependency-version: 2.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 104 ++++++++++++++-------------------------------- 1 file changed, 31 insertions(+), 73 deletions(-) diff --git a/package-lock.json b/package-lock.json index f2229fb9be..dd5838d0ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -708,7 +708,6 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.7.0.tgz", "integrity": "sha512-wR9En2A+WESUHexjmRHkqtaVH94WLNKt6rmeqZhSLBybg4Wyf0Umk04SZsS6sBq4102ZsDBFwoqMqJYj2IoDSg==", - "dev": true, "dependencies": { "@firebase/util": "1.13.0", "tslib": "^2.1.0" @@ -718,119 +717,79 @@ } }, "node_modules/@firebase/database": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.15.tgz", - "integrity": "sha512-xmeTqKoIB2u1AXvLc1jq3Val0QAHUr49YycAr6feoDD7zM9dCjSk8rq9s1ESTv+tbbqS2BRoTpjIvxwXRTKhQQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.1.0.tgz", + "integrity": "sha512-gM6MJFae3pTyNLoc9VcJNuaUDej0ctdjn3cVtILo3D5lpp0dmUHHLFN/pUKe7ImyeB1KAvRlEYxvIHNF04Filg==", "license": "Apache-2.0", "dependencies": { "@firebase/app-check-interop-types": "0.3.3", "@firebase/auth-interop-types": "0.2.4", - "@firebase/component": "0.6.14", - "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.1", + "@firebase/component": "0.7.0", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.13.0", "faye-websocket": "0.11.4", "tslib": "^2.1.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@firebase/database-compat": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.0.6.tgz", - "integrity": "sha512-vv15b1E59sLwoVdjGKvJ75ok9Qu1VRJC/7KXAQGnXvURAL199Ndy1YEw6/GA9twoFlLCYnd2ltxxB2pPiL1Vqw==", - "license": "Apache-2.0", - "dependencies": { - "@firebase/component": "0.6.14", - "@firebase/database": "1.0.15", - "@firebase/database-types": "1.0.11", - "@firebase/logger": "0.4.4", - "@firebase/util": "1.11.1", - "tslib": "^2.1.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@firebase/database-compat/node_modules/@firebase/component": { - "version": "0.6.14", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.14.tgz", - "integrity": "sha512-kf/zAT8GQJ9nYoHuj0mv7twp1QzifKYrO+GsmsVHHM+Hi9KkmI7E3B3J0CtihHpb34vinl4gbJrYJ2p2wfvc9A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-2.1.0.tgz", + "integrity": "sha512-8nYc43RqxScsePVd1qe1xxvWNf0OBnbwHxmXJ7MHSuuTVYFO3eLyLW3PiCKJ9fHnmIz4p4LbieXwz+qtr9PZDg==", "license": "Apache-2.0", "dependencies": { - "@firebase/util": "1.11.1", + "@firebase/component": "0.7.0", + "@firebase/database": "1.1.0", + "@firebase/database-types": "1.0.16", + "@firebase/logger": "0.5.0", + "@firebase/util": "1.13.0", "tslib": "^2.1.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, - "node_modules/@firebase/database-compat/node_modules/@firebase/util": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.11.1.tgz", - "integrity": "sha512-RXg4WE8C2LUrvoV/TMGRTu223zZf9Dq9MR8yHZio9nF9TpLnpCPURw9VWWB2WATDl6HfIdWfl2x2SJYtHkN4hw==", - "hasInstallScript": true, + "node_modules/@firebase/database-compat/node_modules/@firebase/logger": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.5.0.tgz", + "integrity": "sha512-cGskaAvkrnh42b3BA3doDWeBmuHFO/Mx5A83rbRDYakPjO9bJtRL3dX7javzc2Rr/JHZf4HlterTW2lUkfeN4g==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@firebase/database-types": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.11.tgz", - "integrity": "sha512-LBZG/nT6GbntbIdGxBNvu9PBtj4xUEE9wX8AFF6njFK/MufYBESiKqT+jhDwmbcM4zAha9U0Pcca8FvJ1z1bYw==", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.16.tgz", + "integrity": "sha512-xkQLQfU5De7+SPhEGAXFBnDryUWhhlFXelEg2YeZOQMCdoe7dL64DDAd77SQsR+6uoXIZY5MB4y/inCs4GTfcw==", "license": "Apache-2.0", "dependencies": { "@firebase/app-types": "0.9.3", - "@firebase/util": "1.11.1" - } - }, - "node_modules/@firebase/database-types/node_modules/@firebase/util": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.11.1.tgz", - "integrity": "sha512-RXg4WE8C2LUrvoV/TMGRTu223zZf9Dq9MR8yHZio9nF9TpLnpCPURw9VWWB2WATDl6HfIdWfl2x2SJYtHkN4hw==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@firebase/database/node_modules/@firebase/component": { - "version": "0.6.14", - "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.14.tgz", - "integrity": "sha512-kf/zAT8GQJ9nYoHuj0mv7twp1QzifKYrO+GsmsVHHM+Hi9KkmI7E3B3J0CtihHpb34vinl4gbJrYJ2p2wfvc9A==", - "license": "Apache-2.0", - "dependencies": { - "@firebase/util": "1.11.1", - "tslib": "^2.1.0" - }, - "engines": { - "node": ">=18.0.0" + "@firebase/util": "1.13.0" } }, - "node_modules/@firebase/database/node_modules/@firebase/util": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.11.1.tgz", - "integrity": "sha512-RXg4WE8C2LUrvoV/TMGRTu223zZf9Dq9MR8yHZio9nF9TpLnpCPURw9VWWB2WATDl6HfIdWfl2x2SJYtHkN4hw==", - "hasInstallScript": true, + "node_modules/@firebase/database/node_modules/@firebase/logger": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.5.0.tgz", + "integrity": "sha512-cGskaAvkrnh42b3BA3doDWeBmuHFO/Mx5A83rbRDYakPjO9bJtRL3dX7javzc2Rr/JHZf4HlterTW2lUkfeN4g==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@firebase/logger": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.4.tgz", "integrity": "sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g==", + "dev": true, "dependencies": { "tslib": "^2.1.0" }, @@ -842,7 +801,6 @@ "version": "1.13.0", "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.13.0.tgz", "integrity": "sha512-0AZUyYUfpMNcztR5l09izHwXkZpghLgCUaAGjtMwXnCg3bj4ml5VgiwqOMOxJ+Nw4qN/zJAaOQBcJ7KGkWStqQ==", - "dev": true, "hasInstallScript": true, "dependencies": { "tslib": "^2.1.0" From ad7a4f3efced00de5ee1f81556467b35324e8c1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Nov 2025 15:02:28 +0000 Subject: [PATCH 09/10] build(deps-dev): bump gulp from 5.0.0 to 5.0.1 (#3001) --- package-lock.json | 130 +++++++++++++++++++++++++++------------------- 1 file changed, 76 insertions(+), 54 deletions(-) diff --git a/package-lock.json b/package-lock.json index dd5838d0ba..3826e45eb1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3209,6 +3209,21 @@ "dev": true, "license": "MIT" }, + "node_modules/b4a": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", + "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, "node_modules/bach": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/bach/-/bach-2.0.1.tgz", @@ -3232,12 +3247,19 @@ "license": "MIT" }, "node_modules/bare-events": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.0.tgz", - "integrity": "sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.1.tgz", + "integrity": "sha512-oxSAxTS1hRfnyit2CL5QpAOS5ixfBjj6ex3yTNvXyY/kE719jQ/IjuESJBK2w5v4wwQRAHGseVJXx9QBYOtFGQ==", "dev": true, "license": "Apache-2.0", - "optional": true + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } }, "node_modules/base64-js": { "version": "1.5.1", @@ -4767,6 +4789,16 @@ "node": ">=6" } }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, "node_modules/expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", @@ -5651,9 +5683,9 @@ } }, "node_modules/glob-stream": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-8.0.2.tgz", - "integrity": "sha512-R8z6eTB55t3QeZMmU1C+Gv+t5UnNRkA55c5yo67fAVfxODxieTwsjNG7utxS/73NdP1NbDgCrhVEg2h00y4fFw==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-8.0.3.tgz", + "integrity": "sha512-fqZVj22LtFJkHODT+M4N1RJQ3TjnnQhfE9GwZI8qXscYarnhpip70poMldRnP8ipQ/w0B621kOhfc53/J9bd/A==", "dev": true, "license": "MIT", "dependencies": { @@ -5912,16 +5944,16 @@ } }, "node_modules/gulp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/gulp/-/gulp-5.0.0.tgz", - "integrity": "sha512-S8Z8066SSileaYw1S2N1I64IUc/myI2bqe2ihOBzO6+nKpvNSg7ZcWJt/AwF8LC/NVN+/QZ560Cb/5OPsyhkhg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-5.0.1.tgz", + "integrity": "sha512-PErok3DZSA5WGMd6XXV3IRNO0mlB+wW3OzhFJLEec1jSERg2j1bxJ6e5Fh6N6fn3FH2T9AP4UYNb/pYlADB9sA==", "dev": true, "license": "MIT", "dependencies": { "glob-watcher": "^6.0.0", - "gulp-cli": "^3.0.0", + "gulp-cli": "^3.1.0", "undertaker": "^2.0.0", - "vinyl-fs": "^4.0.0" + "vinyl-fs": "^4.0.2" }, "bin": { "gulp": "bin/gulp.js" @@ -5931,9 +5963,9 @@ } }, "node_modules/gulp-cli": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-3.0.0.tgz", - "integrity": "sha512-RtMIitkT8DEMZZygHK2vEuLPqLPAFB4sntSxg4NoDta7ciwGZ18l7JuhCTiS5deOJi2IoK0btE+hs6R4sfj7AA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-3.1.0.tgz", + "integrity": "sha512-zZzwlmEsTfXcxRKiCHsdyjZZnFvXWM4v1NqBJSYbuApkvVKivjcmOS2qruAJ+PkEHLFavcDKH40DPc1+t12a9Q==", "dev": true, "license": "MIT", "dependencies": { @@ -5942,7 +5974,7 @@ "copy-props": "^4.0.0", "gulplog": "^2.2.0", "interpret": "^3.1.1", - "liftoff": "^5.0.0", + "liftoff": "^5.0.1", "mute-stdout": "^2.0.0", "replace-homedir": "^2.0.0", "semver-greatest-satisfied-range": "^2.0.0", @@ -7893,9 +7925,9 @@ } }, "node_modules/liftoff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-5.0.0.tgz", - "integrity": "sha512-a5BQjbCHnB+cy+gsro8lXJ4kZluzOijzJ1UVVfyJYZC+IP2pLv1h4+aysQeKuTmyO8NAqfyQAk4HWaP/HjcKTg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-5.0.1.tgz", + "integrity": "sha512-wwLXMbuxSF8gMvubFcFRp56lkFV69twvbU5vDPbaw+Q+/rF8j0HKjGbIdlSi+LuJm9jf7k9PB+nTxnsLMPcv2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -9896,13 +9928,6 @@ ], "license": "MIT" }, - "node_modules/queue-tick": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", - "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", - "dev": true, - "license": "MIT" - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -11065,18 +11090,15 @@ } }, "node_modules/streamx": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.1.tgz", - "integrity": "sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==", + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", + "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", "dev": true, "license": "MIT", "dependencies": { + "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", - "queue-tick": "^1.0.1", "text-decoder": "^1.1.0" - }, - "optionalDependencies": { - "bare-events": "^2.2.0" } }, "node_modules/string_decoder": { @@ -11430,11 +11452,14 @@ } }, "node_modules/text-decoder": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.1.tgz", - "integrity": "sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", "dev": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } }, "node_modules/text-table": { "version": "0.2.0", @@ -12090,14 +12115,13 @@ } }, "node_modules/vinyl-contents/node_modules/vinyl": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.0.tgz", - "integrity": "sha512-rC2VRfAVVCGEgjnxHUnpIVh3AGuk62rP3tqVrn+yab0YH7UULisC085+NYH+mnqf3Wx4SpSi1RQMwudL89N03g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.1.tgz", + "integrity": "sha512-0QwqXteBNXgnLCdWdvPQBX6FXRHtIH3VhJPTd5Lwn28tJXc34YqSCWUmkOvtJHBmB3gGoPtrOKk3Ts8/kEZ9aA==", "dev": true, "license": "MIT", "dependencies": { "clone": "^2.1.2", - "clone-stats": "^1.0.0", "remove-trailing-separator": "^1.1.0", "replace-ext": "^2.0.0", "teex": "^1.0.1" @@ -12107,14 +12131,14 @@ } }, "node_modules/vinyl-fs": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-4.0.0.tgz", - "integrity": "sha512-7GbgBnYfaquMk3Qu9g22x000vbYkOex32930rBnc3qByw6HfMEAoELjCjoJv4HuEQxHAurT+nvMHm6MnJllFLw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-4.0.2.tgz", + "integrity": "sha512-XRFwBLLTl8lRAOYiBqxY279wY46tVxLaRhSwo3GzKEuLz1giffsOquWWboD/haGf5lx+JyTigCFfe7DWHoARIA==", "dev": true, "license": "MIT", "dependencies": { "fs-mkdirp-stream": "^2.0.1", - "glob-stream": "^8.0.0", + "glob-stream": "^8.0.3", "graceful-fs": "^4.2.11", "iconv-lite": "^0.6.3", "is-valid-glob": "^1.0.0", @@ -12125,7 +12149,7 @@ "streamx": "^2.14.0", "to-through": "^3.0.0", "value-or-function": "^4.0.0", - "vinyl": "^3.0.0", + "vinyl": "^3.0.1", "vinyl-sourcemap": "^2.0.0" }, "engines": { @@ -12143,14 +12167,13 @@ } }, "node_modules/vinyl-fs/node_modules/vinyl": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.0.tgz", - "integrity": "sha512-rC2VRfAVVCGEgjnxHUnpIVh3AGuk62rP3tqVrn+yab0YH7UULisC085+NYH+mnqf3Wx4SpSi1RQMwudL89N03g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.1.tgz", + "integrity": "sha512-0QwqXteBNXgnLCdWdvPQBX6FXRHtIH3VhJPTd5Lwn28tJXc34YqSCWUmkOvtJHBmB3gGoPtrOKk3Ts8/kEZ9aA==", "dev": true, "license": "MIT", "dependencies": { "clone": "^2.1.2", - "clone-stats": "^1.0.0", "remove-trailing-separator": "^1.1.0", "replace-ext": "^2.0.0", "teex": "^1.0.1" @@ -12195,14 +12218,13 @@ } }, "node_modules/vinyl-sourcemap/node_modules/vinyl": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.0.tgz", - "integrity": "sha512-rC2VRfAVVCGEgjnxHUnpIVh3AGuk62rP3tqVrn+yab0YH7UULisC085+NYH+mnqf3Wx4SpSi1RQMwudL89N03g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.1.tgz", + "integrity": "sha512-0QwqXteBNXgnLCdWdvPQBX6FXRHtIH3VhJPTd5Lwn28tJXc34YqSCWUmkOvtJHBmB3gGoPtrOKk3Ts8/kEZ9aA==", "dev": true, "license": "MIT", "dependencies": { "clone": "^2.1.2", - "clone-stats": "^1.0.0", "remove-trailing-separator": "^1.1.0", "replace-ext": "^2.0.0", "teex": "^1.0.1" From 2e2b36a84ba28679bcb7aecdacabfec0bded2d48 Mon Sep 17 00:00:00 2001 From: Jonathan Edey <145066863+jonathanedey@users.noreply.github.com> Date: Wed, 5 Nov 2025 09:59:05 -0500 Subject: [PATCH 10/10] [chore] Release 13.6.0 (#3006) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index de6e640c84..721ff00c9f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-admin", - "version": "13.5.0", + "version": "13.6.0", "description": "Firebase admin SDK for Node.js", "author": "Firebase (https://firebase.google.com/)", "license": "Apache-2.0",