diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 9b77ea71..1e94b364 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -7,9 +7,11 @@ assignees: ""
---
**Describe the bug**
+
A clear and concise description of what the bug is.
**To Reproduce**
+
Steps to reproduce the behavior:
1. Go to '...'
@@ -18,9 +20,11 @@ Steps to reproduce the behavior:
4. See error
**Expected behavior**
+
A clear and concise description of what you expected to happen.
**Screenshots**
+
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
@@ -37,4 +41,5 @@ If applicable, add screenshots to help explain your problem.
- Version [e.g. 22]
**Additional context**
+
Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index 2bc5d5f7..ff331d9c 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -7,13 +7,17 @@ assignees: ""
---
**Is your feature request related to a problem? Please describe.**
+
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
+
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
+
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
+
Add any other context or screenshots about the feature request here.
diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml
new file mode 100644
index 00000000..56482246
--- /dev/null
+++ b/.markdownlint-cli2.yaml
@@ -0,0 +1,35 @@
+# 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.
+# 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.
+
+config:
+ line-length:
+ line_length: 150
+ #stern: true
+ code_blocks: false
+ tables: false
+
+ # these are apparently in conflict with prettier's markdown formatting
+ list-marker-space: false
+ list-indent: false
+ ul-indent: false
+
+ headings: false
+
+ # we use inline html tags for docusaurus layout
+ no-inline-html: false
+
+ # we use ordered list for demo step by step but with additional information between steps
+ ol-prefix: false
+
+fix: true
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 00000000..3e482884
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,42 @@
+# Copyright 2023 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.
+
+# See https://pre-commit.com for more information
+# See https://pre-commit.com/hooks.html for more hooks
+
+fail_fast: true
+repos:
+ - repo: https://github.com/pre-commit/mirrors-prettier
+ rev: v3.1.0
+ hooks:
+ - id: prettier
+ types_or:
+ - markdown
+ - json
+ - javascript
+ - ts
+ - html
+ - css
+ - json
+ - yaml
+
+ - repo: https://github.com/DavidAnson/markdownlint-cli2
+ rev: v0.10.0
+ hooks:
+ - id: markdownlint-cli2
+ name: lint markdown
+ci:
+ autofix_prs: false
+ autoupdate_branch: dev
+ autoupdate_schedule: monthly
diff --git a/.prettierrc.json b/.prettierrc.json
index bf0c4316..27412213 100644
--- a/.prettierrc.json
+++ b/.prettierrc.json
@@ -1,28 +1,46 @@
{
- "printWidth": 150,
+ "printWidth": 80,
"tabWidth": 2,
- "trailingComma": "none",
- "semi": false,
+ "useTabs": false,
+ "semi": true,
+ "singleQuote": true,
+ "quoteProps": "preserve",
+ "bracketSpacing": false,
+ "trailingComma": "all",
"arrowParens": "always",
+ "embeddedLanguageFormatting": "off",
+ "proseWrap": "always",
"overrides": [
{
"files": [
"**/*.js",
"**/*.ts",
- "**/*.html",
- "**/*.css",
- "**/*.md"
+ "**/*.css"
]
},
{
"files": [
- "**/*.json",
"**/*.yaml",
+ "**/*.yml"
+ ],
+ "options": {
+ "singleQuote": false
+ }
+ },
+ {
+ "files": [
+ "**/*.json",
"**/tailwind.config.js"
],
"options": {
"printWidth": 40
}
+ },
+ {
+ "files": ["**/*.md"],
+ "options": {
+ "printWidth": 150
+ }
}
]
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cd7e1169..630d7d86 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,25 +1,40 @@
# Changelog
-## v1.2 (2023/11/16)
+## v1.2 (2023/12/6)
-#### Enhancements
+### Enhancements
-- Home Site : upgrade CMS to [Docusaurus v3](https://docusaurus.io/blog/releases/3.0)
-- DSP Site : serve attestation file version2 on privacy-sandbox-demos-dsp.dev
-- SSP Site : serve attestation file version2 on privacy-sandbox-demos-ssp.dev
-- CI/CD : Cloud Build copies the attestation files from cicd/attestations to dsp/ssp services for the target environment
-- Update deployment documentation with instructions related to enrollment and attestation
-- Use Case : Single-touch conversion Attribution. Move attribution code from SSP to DSP and update documentation
-- Tools : Add [Aggregatable Report Converter](https://github.com/privacysandbox/privacy-sandbox-demos/tree/main/tools/aggregatable_report_converter) to the tooling codebase. This tool helps developers to create debug aggregatable reports that can be used for Local Testing and AWS Aggregation Service testing.
- GitHub documentation : Add a changelog
-
-#### Bug Fixes
+- Home Site : upgrade CMS to [Docusaurus v3](https://docusaurus.io/blog/releases/3.0)
+- Enrollment and Attestation :
+ - DSP Site : serve attestation file version2 on privacy-sandbox-demos-dsp.dev
+ - SSP Site : serve attestation file version2 on privacy-sandbox-demos-ssp.dev
+ - CI/CD : Cloud Build copies the attestation files from cicd/attestations to dsp/ssp services for the target environment
+ - Update deployment documentation with instructions related to enrollment and attestation
+- Demos
+ - Use Case : Single-touch conversion Attribution. Move attribution code from SSP to DSP and update documentation
+- Tools :
+
+ - Add [Aggregatable Report Converter](https://github.com/privacysandbox/privacy-sandbox-demos/tree/main/tools/aggregatable_report_converter) to the
+ tooling codebase. This tool helps developers to create debug aggregatable reports that can be used for Local Testing and AWS Aggregation Service
+ testing.
+
+- CI/CD Linting and Reformating code base on commit/PR
+ - pre-commit.ci won't fix automatically but only raise a warning when submitting PR
+ - prettier will now wrap markdown file where line length exceeds 150 chars
+ - markdownlint will ignore line length on tables, ordered list validation, and html tags included for docusaurus layout.
+
+### Bug Fixes
+
+- #76
+- #77
+- #178
---
## v1.1 (2023/10/13)
-#### Enhancements
+### Enhancements
- Shop site : refactor service using expressJS (from nextJS)
- Shop site : fix issue with Firebase filtering session cookies
@@ -27,9 +42,10 @@
- Use Case : remarketing. Move `renderURL` from the SSP codebase to the DSP codebase.
- Use Case : VAST Video Protected Audience. Release v1.
-#### Bug Fixes
+### Bug Fixes
-- Use Case : Single-touch conversion Attribution. Fix registering attribution sources ("Attribution-Reporting-Eligible" is a Dictionary Structured Header and thus need to be decoded accordingly)
+- Use Case : Single-touch conversion Attribution. Fix registering attribution sources ("Attribution-Reporting-Eligible" is a Dictionary Structured
+ Header and thus need to be decoded accordingly)
- Home site : fix Docusaurus and nginx configuration that prevented direct links to use cases
- Home site : fix broken links in documentation
- GitHub documentation : update deployment guides
@@ -39,5 +55,5 @@
## v1.0.0 (2023/06/30)
- Launch Privacy Sandbox Demos [Github Repository](https://github.com/privacysandbox/privacy-sandbox-demos)
-- Launch hosted demos [https://privacy-sandbox-demos.dev/ ](https://privacy-sandbox-demos.dev/)
-- Publication of a [blog post ](https://developer.chrome.com/blog/privacy-sandbox-demos/)on [developers.chrome.com](http://developers.chrome.com/)
+- Launch hosted demos [https://privacy-sandbox-demos.dev/](https://privacy-sandbox-demos.dev/)
+- Publication of a [blog post](https://developer.chrome.com/blog/privacy-sandbox-demos/)on [developers.chrome.com](http://developers.chrome.com/)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ae319c70..eeaa30b6 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,23 +1,42 @@
# How to Contribute
-We'd love to accept your patches and contributions to this project. There are
-just a few small guidelines you need to follow.
+We'd love to accept your patches and contributions to this project. There are just a few small guidelines you need to follow.
## Contributor License Agreement
-Contributions to this project must be accompanied by a Contributor License
-Agreement. You (or your employer) retain the copyright to your contribution,
-this simply gives us permission to use and redistribute your contributions as
-part of the project. Head over to to see
-your current agreements on file or to sign a new one.
+Contributions to this project must be accompanied by a Contributor License Agreement. You (or your employer) retain the copyright to your
+contribution, this simply gives us permission to use and redistribute your contributions as part of the project. Head over to
+ to see your current agreements on file or to sign a new one.
-You generally only need to submit a CLA once, so if you've already submitted one
-(even if it was for a different project), you probably don't need to do it
-again.
+You generally only need to submit a CLA once, so if you've already submitted one (even if it was for a different project), you probably don't need to
+do it again.
## Code reviews
-All submissions, including submissions by project members, require review. We
-use GitHub pull requests for this purpose. Consult
-[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
-information on using pull requests.
+All submissions, including submissions by project members, require review. We use GitHub pull requests for this purpose. Consult
+[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more information on using pull requests.
+
+## Code linting
+
+We use a pre-commit hook to clean/format code before submitting a change. This allows for a cleaner code base. pre-commit will fetch and trigger the
+_prettier_ and _markdown_ link tools and run it through your staged files.
+
+Install pre-commit on your cloud top
+
+```shell
+sudo apt install pre-commit
+```
+
+Then install the git hook :
+
+```shell
+pre-commit install
+```
+
+you can also run pre-commit on all the files manually before committing your changes :
+
+```shell
+pre-commit run --all-files
+```
+
+more information about pre-commit :
diff --git a/README.md b/README.md
index 0036b253..356a3edc 100644
--- a/README.md
+++ b/README.md
@@ -1,19 +1,23 @@
# Privacy Sandbox Demos
-A use case demo library for
-[Privacy Sandbox APIs](https://developer.chrome.com/en/docs/privacy-sandbox/) on the web.
+A use case demo library for [Privacy Sandbox APIs](https://developer.chrome.com/en/docs/privacy-sandbox/) on the web.
## Motivation
-The Privacy Sandbox initiative offers [20+ APIs](https://developer.chrome.com/en/docs/privacy-sandbox/) to protect people’s privacy online while giving companies and developers tools to build thriving digital businesses.
+The Privacy Sandbox initiative offers [20+ APIs](https://developer.chrome.com/en/docs/privacy-sandbox/) to protect people’s privacy online while
+giving companies and developers tools to build thriving digital businesses.
-Web ecosystem developers expect new approaches to typical use cases supported today by third-party cookies. These use cases often require incorporating a combination of new Privacy Sandbox APIs into their products, which requires advanced planning and a clear understanding how these APIs work.
+Web ecosystem developers expect new approaches to typical use cases supported today by third-party cookies. These use cases often require
+incorporating a combination of new Privacy Sandbox APIs into their products, which requires advanced planning and a clear understanding how these APIs
+work.
## Our proposition
-The Privacy Sandbox Demos repository offers cookbook recipes, sample code, and demo applications, based on Privacy Sandbox APIs. These are intended to aid businesses and developers in adapting their applications and the businesses they support to a web ecosystem without third-party cookies.
+The Privacy Sandbox Demos repository offers cookbook recipes, sample code, and demo applications, based on Privacy Sandbox APIs. These are intended to
+aid businesses and developers in adapting their applications and the businesses they support to a web ecosystem without third-party cookies.
-All the demo code and assets are released under the Apache open-source [license](https://github.com/privacysandbox/privacy-sandbox-demos) to allow you to extend and modify the code for your own needs.
+All the demo code and assets are released under the Apache open-source [license](https://github.com/privacysandbox/privacy-sandbox-demos) to allow you
+to extend and modify the code for your own needs.
This repository contains :
@@ -23,7 +27,8 @@ This repository contains :
- [Instructions for deploying and running the demos on Google Cloud Platform](docs/deploy-to-gcp.md)
- Development framework to contribute to the project
-If you are a developer we recommend you follow the [deployment instructions](docs/deploy-to-linux-docker.md). If you are simply curious, we recommend you have a look at our [Google-hosted instances](https://privacy-sandbox-demos.dev) to quickly start learning and experimenting.
+If you are a developer we recommend you follow the [deployment instructions](docs/deploy-to-linux-docker.md). If you are simply curious, we recommend
+you have a look at our [Google-hosted instances](https://privacy-sandbox-demos.dev) to quickly start learning and experimenting.
## Use Cases
@@ -44,7 +49,8 @@ These use cases are based on a set of demo apps and services that we have develo
| SSP service (Supply Side Platform) | The demo SSP service will Choose ads to show based on Topics, Protected Audience auctions and other contextual or first-party signals. Show ads in iframe or fenced frame. Register view/click impressions with Attribution Reporting API. Detect ad fraud via Private State Tokens |
| DSP service (Demand Side Platform) | The demo DSP service will Add a user into an Interest Group with Protected Audience API. Register conversions with Attribution Reporting API |
-We’re continuing our effort to deliver comprehensive documentation and demos, and we welcome your feedback on which use cases you would like to see in future releases. Share your ideas and feedback on our issue tracker.
+We’re continuing our effort to deliver comprehensive documentation and demos, and we welcome your feedback on which use cases you would like to see in
+future releases. Share your ideas and feedback on our issue tracker.
---
diff --git a/docs/deploy-to-gcp.md b/docs/deploy-to-gcp.md
index 1d0e7ef0..7391c74f 100644
--- a/docs/deploy-to-gcp.md
+++ b/docs/deploy-to-gcp.md
@@ -6,25 +6,28 @@ The same instructions can be repeated to deploy a dev, staging, prod etc. enviro
Project names and variables in _italic_ must be carefully chosen and updated to suit your project naming convention.
-Resources : https://firebase.google.com/docs/hosting/cloud-run
+Resources :
## Prepare your Google Cloud Platform Billing Account
-If you don’t have yet a billing account, follow the documentation to Create a Google Cloud Platform Billing Account : https://cloud.google.com/billing/docs/how-to/manage-billing-account
+If you don’t have yet a billing account, follow the documentation to Create a Google Cloud Platform Billing Account :
+
## Prepare your Google Cloud Platform Project
-**Note** : the instructions below assume you are creating a completely new GCP Project. However if the project already exists, instead you’ll need to get permission from the project owner to be able to deploy resources (Firebase hosting & Cloud Run).
+**Note** : the instructions below assume you are creating a completely new GCP Project. However if the project already exists, instead you’ll need to
+get permission from the project owner to be able to deploy resources (Firebase hosting & Cloud Run).
-1. Create a Google Cloud Platform Project : https://cloud.google.com/resource-manager/docs/creating-managing-projects
+1. Create a Google Cloud Platform Project :
1. Note the name of the project/id. E.g.: _privacy-sandbox-demos_
2. Assign the billing account created in step above
-2. Add a Firebase Project linked to your GCP Project : https://console.firebase.google.com/
+2. Add a Firebase Project linked to your GCP Project :
1. Click "Add Project"
2. Select the GCP project you previously created. E.g. : _privacy-sandbox-demos_
3. Since you enabled Billing Account on this project, it will automatically select the Firebase pay-as-you-go plan
4. Enable Google Analytics for the project : Select "Default Account for Firebase" unless you have specific analytics requirements
-3. If you don’t have the project owner role , you will need to obtain at least the following IAM role to your account on the target project before you proceed with the next steps.
+3. If you don’t have the project owner role , you will need to obtain at least the following IAM role to your account on the target project before you
+ proceed with the next steps.
1. Artifact Registry Administrator
2. Cloud Build Editor
3. Cloud Run Admin
@@ -38,15 +41,16 @@ If you don’t have yet a billing account, follow the documentation to Create a
## Prepare your Development Environment for Firebase Hosting
-In this section you will configure your development environment to get ready to build and deploy resources to Firebase. The Instructions below are based on the Linux environment.
+In this section you will configure your development environment to get ready to build and deploy resources to Firebase. The Instructions below are
+based on the Linux environment.
-1. Clone Privacy Sandbox Demos Git Repository : https://github.com/privacysandbox/privacy-sandbox-demos.git
-2. Install the Firebase CLI : https://firebase.google.com/docs/cli#linux
+1. Clone Privacy Sandbox Demos Git Repository :
+2. Install the Firebase CLI :
3. Open a terminal at the root of the project. Login and test the Firebase CLI :
```shell-session
-$ firebase login
-$ firebase projects:list
+firebase login
+firebase projects:list
```
4. Configure firebase to use your project (e.g. )
@@ -66,19 +70,21 @@ FIREBASE_HOSTING_DOMAIN=privacy-sandbox-demos
Resources :
-- https://firebase.google.com/docs/hosting
-- https://firebase.google.com/docs/hosting/multisites?authuser=0&hl=en#set_up_deploy_targets
+-
+-
## Setup Firebase Hosting Multiple Sites
-Your firebase project will host 5 different sites to demonstrate the capabilities of Privacy Sandbox across the different actors of the adtech ecosystem :
+Your firebase project will host 5 different sites to demonstrate the capabilities of Privacy Sandbox across the different actors of the adtech
+ecosystem :
- Home : Home page with the links to the different use-cases and scenario
- DSP : Demand Side Platform
- Shop & Travel : The advertiser shopping or travel site = Buy side. They are buying ad space from the publisher. Site embeds the DSP tags.
- SSP : Supply Side Platform
- News : Publisher site where ads will be displayed = Sell side. They are selling ad space to advertisers. Site embeds SSP tags
-- Collector : Collector service collects, transforms and batches Aggregatable Reports produced by the Attribution Reporting API and Private Aggregation API, then sends them to the Aggregation Service running on TEE.
+- Collector : Collector service collects, transforms and batches Aggregatable Reports produced by the Attribution Reporting API and Private
+ Aggregation API, then sends them to the Aggregation Service running on TEE.
Each site will have a different domain name to simulate a real life adtech scenario
@@ -114,7 +120,8 @@ firebase hosting:sites:create privacy-sandbox-demos-news
firebase hosting:sites:create privacy-sandbox-demos-collector
```
-Set up deploy targets for your sites (When you have multiple sites and you run Firebase CLI deploy commands, the CLI needs a way to communicate which settings should be deployed to each site).
+Set up deploy targets for your sites (When you have multiple sites and you run Firebase CLI deploy commands, the CLI needs a way to communicate which
+settings should be deployed to each site).
use the following command to setup deploy target for each hosting site :
@@ -134,7 +141,8 @@ firebase target:apply hosting travel privacy-sandbox-demos-travel
firebase target:apply hosting travel privacy-sandbox-demos-collector
```
-Adding hosting sites and deploy targets can be done using the provided script below (make sure your `.env.deploy` file contains the right domain value for the key `FIREBASE_HOSTING_DOMAIN`)
+Adding hosting sites and deploy targets can be done using the provided script below (make sure your `.env.deploy` file contains the right domain value
+for the key `FIREBASE_HOSTING_DOMAIN`)
```shell
# From the root of the project, execute
@@ -149,7 +157,7 @@ By using Cloud Logging with your Firebase Hosting sites, you allow web request l
Access the following URL (replace _privacy-sandbox-demos_ with your project name)
-https://firebase.corp.google.com/project/_privacy-sandbox-demos_/settings/integrations/cloudlogging
+
Select all the sites you want to export logs from, click Save and Finish.
@@ -157,11 +165,13 @@ Select all the sites you want to export logs from, click Save and Finish.
Next you will deploy containers to Cloud Run to run the content of the demo sites.
-For our architecture, we chose to deploy everything container based for portability and flexibility and to use Firebase hosting as a frontend solution for HTTPS request handling, domain name and ssl certificates.
+For our architecture, we chose to deploy everything container based for portability and flexibility and to use Firebase hosting as a frontend solution
+for HTTPS request handling, domain name and ssl certificates.
-Install Google Cloud SDK : If Google Cloud SDK is not installed on the machine, follow instructions here : https://cloud.google.com/sdk/docs/install#linux
+Install Google Cloud SDK : If Google Cloud SDK is not installed on the machine, follow instructions here :
+
-Initialize Google Cloud SDK : https://cloud.google.com/sdk/docs/initializing
+Initialize Google Cloud SDK :
```shell
# Run `gcloud init` to setup authentication and project
@@ -181,12 +191,11 @@ gcloud services enable run.googleapis.com cloudbuild.googleapis.com artifactregi
gcloud config set run/region us-central1
```
-Resources : https://firebase.google.com/docs/hosting/cloud-run
+Resources :
## Setup Artifact Registry
-https://cloud.google.com/artifact-registry/docs/enable-service
-https://cloud.google.com/artifact-registry/docs/transition/setup-repo
+
```sh
# create docker repository in Cloud Artifact Registry
@@ -213,22 +222,19 @@ gcloud artifacts repositories list
gcloud auth configure-docker us-central1-docker.pkg.dev
```
-Enable Vulnerability Scanning : navigate to settings and Turn On.
-https://console.cloud.google.com/artifacts/settings
+Enable Vulnerability Scanning : navigate to settings and Turn On.
## Setup Cloud Build
-https://cloud.google.com/build/docs/deploying-builds/deploy-cloud-run
+
-Enable Cloud Build Service Account permissions :
-Cloud Run Admin
-Service Account User
+Enable Cloud Build Service Account permissions : Cloud Run Admin Service Account User
-From Cloud Build Settings page : https://console.cloud.google.com/cloud-build/settings/service-account
+From Cloud Build Settings page :
or from IAM page :
-https://console.cloud.google.com/iam-admin/iam
+
## Deploy all Cloud Run services and Firebase Sites
@@ -259,14 +265,16 @@ NEWS_DETAIL="Publisher: News media site"
...
```
-Copy the `.env.deploy.template` to `.env.deploy` file then edit .env.deploy to update the GCP Project Name and the Firebase domain prefix you will use to deploy your services :
+Copy the `.env.deploy.template` to `.env.deploy` file then edit .env.deploy to update the GCP Project Name and the Firebase domain prefix you will use
+to deploy your services :
```sh
GCP_PROJECT_NAME=xxx
FIREBASE_HOSTING_DOMAIN=**_privacy-sandbox-demos_**
```
-**[optional]** If you have enrolled your site with Privacy Sandbox, copy your attestation files for dsp/ssp services under the folder : `cicd/attestations`
+**[optional]** If you have enrolled your site with Privacy Sandbox, copy your attestation files for dsp/ssp services under the folder :
+`cicd/attestations`
| Environment | Service | Attestation file name |
| :---------: | :-----: | :----------------------------------------: |
diff --git a/docs/deploy-to-linux-docker.md b/docs/deploy-to-linux-docker.md
index 0a9bc066..be52edec 100644
--- a/docs/deploy-to-linux-docker.md
+++ b/docs/deploy-to-linux-docker.md
@@ -2,7 +2,9 @@
## Prerequisites
-These instructions are given for Linux environments and assume you have the following packages installed on your system and your user is in the sudoers list (can execute commands with root access using sudo). If you plan to contribute to the code, a GitHub account is also required to commit your change and/or make pull requests.
+These instructions are given for Linux environments and assume you have the following packages installed on your system and your user is in the
+sudoers list (can execute commands with root access using sudo). If you plan to contribute to the code, a GitHub account is also required to commit
+your change and/or make pull requests.
The following packages must be installed
@@ -17,12 +19,14 @@ The following packages must be installed
### Domain Name / URL
-Privacy Sandbox APIs use the domain name in the URL (site origin) to allow/block cross-site data sharing, observe topics, etc. As a result developers cannot rely only on the “localhost” domain for development.
+Privacy Sandbox APIs use the domain name in the URL (site origin) to allow/block cross-site data sharing, observe topics, etc. As a result developers
+cannot rely only on the “localhost” domain for development.
-You will re-map individual domain names to loopback address (127.0.0.1), so that each service running on your local environment can be accessed via a URL, such as:
+You will re-map individual domain names to loopback address (127.0.0.1), so that each service running on your local environment can be accessed via a
+URL, such as:
-- https://privacy-sandbox-demos-home.dev
-- https://privacy-sandbox-demos-shop.dev
+-
+-
- …
There are 2 ways to achieve remapping on your local development environment. You can choose either of the 2 options below :
@@ -31,16 +35,16 @@ There are 2 ways to achieve remapping on your local development environment. You
Edit `/etc/hosts` file
-```
+```shell
# /etc/hosts
-127.0.0.1 privacy-sandbox-demos.dev
-127.0.0.1 privacy-sandbox-demos-home.dev
-127.0.0.1 privacy-sandbox-demos-dsp.dev
-127.0.0.1 privacy-sandbox-demos-shop.dev
-127.0.0.1 privacy-sandbox-demos-travel.dev
-127.0.0.1 privacy-sandbox-demos-ssp.dev
-127.0.0.1 privacy-sandbox-demos-news.dev
-127.0.0.1 privacy-sandbox-demos-collector.dev
+127.0.0.1 privacy-sandbox-demos.dev
+127.0.0.1 privacy-sandbox-demos-home.dev
+127.0.0.1 privacy-sandbox-demos-dsp.dev
+127.0.0.1 privacy-sandbox-demos-shop.dev
+127.0.0.1 privacy-sandbox-demos-travel.dev
+127.0.0.1 privacy-sandbox-demos-ssp.dev
+127.0.0.1 privacy-sandbox-demos-news.dev
+127.0.0.1 privacy-sandbox-demos-collector.dev
```
Verifying mapping with :
@@ -63,7 +67,9 @@ google_chrome --host-resolver-rules="MAP privacy-sandbox-demos-* 127.0.0.1"
### HTTP SSL Certificate
-`https://` protocol requires a valid certificate for your browser. To get a valid certificate, use mkcert to create a local certification authority that will be trusted by your local browser. Later you will be creating a certificate for each of the Privacy Sandbox Demos services and configure nginx to serve those certificates.
+`https://` protocol requires a valid certificate for your browser. To get a valid certificate, use mkcert to create a local certification authority
+that will be trusted by your local browser. Later you will be creating a certificate for each of the Privacy Sandbox Demos services and configure
+nginx to serve those certificates.
Run the command below _with your current user (not root !)_ to create the development Certificate Authority:
@@ -73,9 +79,13 @@ mkcert -install
### Enrollment & Attestation exception
-To access the Privacy Sandbox relevance and measurement APIs on Chrome, developers need to [enroll their site](https://github.com/privacysandbox/attestation/blob/main/how-to-enroll.md) with the Privacy Sandbox, pass the verification process, and upload an attestation file on their site.
+To access the Privacy Sandbox relevance and measurement APIs on Chrome, developers need to
+[enroll their site](https://github.com/privacysandbox/attestation/blob/main/how-to-enroll.md) with the Privacy Sandbox, pass the verification process,
+and upload an attestation file on their site.
-However you do not need to enroll if you are only testing with local traffic. For local testing, Chrome provides [a flag and CLI switch](https://github.com/privacysandbox/attestation/blob/main/how-to-enroll.md#5-do-i-need-to-enroll-to-test-against-local-development-environments) to bypass enrollment on your local browser.
+However you do not need to enroll if you are only testing with local traffic. For local testing, Chrome provides
+[a flag and CLI switch](https://github.com/privacysandbox/attestation/blob/main/how-to-enroll.md#5-do-i-need-to-enroll-to-test-against-local-development-environments)
+to bypass enrollment on your local browser.
Open `chrome://flags/#privacy-sandbox-enrollment-overrides`
@@ -91,12 +101,13 @@ https://privacy-sandbox-demos-dsp.dev,https://privacy-sandbox-demos-ssp.dev
### Clone the Privacy Sandbox Demos repository
-1. Fork the repository https://github.com/privacysandbox/privacy-sandbox-demos using the button near the top-right corner.
+1. Fork the repository using the button near the top-right corner.
2. Clone your fork to your local development environment
### Setup .env file
-Edit `.env` file. For each`${SERVICE}_HOST` key, set a value matching the content of the `/etc/hosts` configuration. (if you are fine with the default site name, you don’t need to edit the file)
+Edit `.env` file. For each`${SERVICE}_HOST` key, set a value matching the content of the `/etc/hosts` configuration. (if you are fine with the default
+site name, you don’t need to edit the file)
Example with the domain `privacy-sandbox-demos-${SERVICE}.dev`
@@ -156,7 +167,7 @@ npm run build
$ sudo npm run start
```
-Open the home page: https://privacy-sandbox-demos-home.dev
+Open the home page:
## Stop your local development environment
@@ -202,7 +213,8 @@ sudo docker container rm sandcastle_shop
## Clean your docker images & containers
-After node dependency updates or major updates, you will need to clean your container images and volumes. There might be some situation where your local image registry is corrupted, inconsistent, or has accumulated too many unused images. You can take a fresh start by cleaning your local images.
+After node dependency updates or major updates, you will need to clean your container images and volumes. There might be some situation where your
+local image registry is corrupted, inconsistent, or has accumulated too many unused images. You can take a fresh start by cleaning your local images.
Execute the following commands from the project root directory :
diff --git a/services/dsp/.prettierrc b/services/dsp/.prettierrc
deleted file mode 100644
index 02379f92..00000000
--- a/services/dsp/.prettierrc
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "printWidth": 150,
- "tabWidth": 2,
- "trailingComma": "none",
- "semi": false,
- "arrowParens": "always"
-}
diff --git a/services/dsp/README.md b/services/dsp/README.md
index 89c0c7c4..0085aa00 100644
--- a/services/dsp/README.md
+++ b/services/dsp/README.md
@@ -2,9 +2,7 @@
## /ads
-Serving requested Ads.
-It includes ads creative (Image, Video etc).
-Clicking ads will guide user to SSP redirector for measurement CTC.
+Serving requested Ads. It includes ads creative (Image, Video etc). Clicking ads will guide user to SSP redirector for measurement CTC.
```html
diff --git a/services/dsp/src/arapi.ts b/services/dsp/src/arapi.ts
index 35529419..0aec0aba 100644
--- a/services/dsp/src/arapi.ts
+++ b/services/dsp/src/arapi.ts
@@ -15,62 +15,62 @@
*/
// type: 2bit
-const SOURCE_TYPE: { click: number; view: number } = {
+const SOURCE_TYPE: {click: number; view: number} = {
click: 0b10,
- view: 0b11
-}
+ view: 0b11,
+};
// advertiser: 16bit
-const ADVERTISER: { [index: string]: number } = {}
-ADVERTISER[process.env.SHOP_HOST as string] = 0b0
-ADVERTISER[process.env.TRAVEL_HOST as string] = 0b1
+const ADVERTISER: {[index: string]: number} = {};
+ADVERTISER[process.env.SHOP_HOST as string] = 0b0;
+ADVERTISER[process.env.TRAVEL_HOST as string] = 0b1;
// publisher: 16bit
-const PUBLISHER: { [index: string]: number } = {}
-PUBLISHER[process.env.NEWS_HOST as string] = 0b0
+const PUBLISHER: {[index: string]: number} = {};
+PUBLISHER[process.env.NEWS_HOST as string] = 0b0;
// dimension: 8bit
-const DIMENSION: { quantity: number; gross: number } = {
+const DIMENSION: {quantity: number; gross: number} = {
quantity: 0b0,
- gross: 0b1
-}
+ gross: 0b1,
+};
// type 8bit
const TRIGGER_TYPE = {
quantity: 0b1000_0000,
- gross: 0b1100_0000
-}
+ gross: 0b1100_0000,
+};
-export { SOURCE_TYPE, ADVERTISER, PUBLISHER, DIMENSION, TRIGGER_TYPE }
+export {SOURCE_TYPE, ADVERTISER, PUBLISHER, DIMENSION, TRIGGER_TYPE};
type AggregationKeyStructure = {
- type: number
- advertiser: number
- publisher: number
- id: number
- dimension: number
-}
+ type: number;
+ advertiser: number;
+ publisher: number;
+ id: number;
+ dimension: number;
+};
export function sourceKeyPiece(ako: AggregationKeyStructure) {
- console.log(ako)
- const source = encodeSource(ako)
- const uint64: bigint = new DataView(source).getBigUint64(0, false)
- return `0x${(uint64 << 64n).toString(16)}`
+ console.log(ako);
+ const source = encodeSource(ako);
+ const uint64: bigint = new DataView(source).getBigUint64(0, false);
+ return `0x${(uint64 << 64n).toString(16)}`;
}
type AggregatableTriggerData = {
- type: number
- id: number
- size: number
- category: number
- option: number
-}
+ type: number;
+ id: number;
+ size: number;
+ category: number;
+ option: number;
+};
export function triggerKeyPiece(atd: AggregatableTriggerData) {
- console.log(atd)
- const trigger = encodeTrigger(atd)
- const uint64 = new DataView(trigger).getBigUint64(0, false)
- return `0x${"0".repeat(16)}${uint64.toString(16)}`
+ console.log(atd);
+ const trigger = encodeTrigger(atd);
+ const uint64 = new DataView(trigger).getBigUint64(0, false);
+ return `0x${'0'.repeat(16)}${uint64.toString(16)}`;
}
// type: 2bit
@@ -81,25 +81,25 @@ export function triggerKeyPiece(atd: AggregatableTriggerData) {
// -----------------
// 64bit
function encodeSource(ako: AggregationKeyStructure) {
- const buffer = new ArrayBuffer(8)
- const view = new DataView(buffer)
- const first32 = (((ako.type << 6) + ako.dimension) << 24) + ako.id
- view.setUint32(0, first32)
- view.setUint16(4, ako.advertiser)
- view.setUint16(6, ako.publisher)
- return buffer
+ const buffer = new ArrayBuffer(8);
+ const view = new DataView(buffer);
+ const first32 = (((ako.type << 6) + ako.dimension) << 24) + ako.id;
+ view.setUint32(0, first32);
+ view.setUint16(4, ako.advertiser);
+ view.setUint16(6, ako.publisher);
+ return buffer;
}
function decodeSource(buffer: ArrayBuffer): AggregationKeyStructure {
- const view = new DataView(buffer)
- const first32 = view.getUint32(0)
- const id = first32 & (2 ** 24 - 1)
- const first8 = first32 >>> 24
- const dimension = first8 & 0b111111
- const type = first8 >>> 6
- const advertiser = view.getUint16(4)
- const publisher = view.getUint16(6)
- return { type, dimension, id, advertiser, publisher }
+ const view = new DataView(buffer);
+ const first32 = view.getUint32(0);
+ const id = first32 & (2 ** 24 - 1);
+ const first8 = first32 >>> 24;
+ const dimension = first8 & 0b111111;
+ const type = first8 >>> 6;
+ const advertiser = view.getUint16(4);
+ const publisher = view.getUint16(6);
+ return {type, dimension, id, advertiser, publisher};
}
// type: 8bit
@@ -110,91 +110,117 @@ function decodeSource(buffer: ArrayBuffer): AggregationKeyStructure {
// -----------------
// 64bit
function encodeTrigger(atd: AggregatableTriggerData) {
- const buffer = new ArrayBuffer(8)
- const view = new DataView(buffer)
- view.setUint32(0, (atd.type << 24) + atd.id)
- view.setUint8(4, atd.size)
- view.setUint8(5, atd.category)
- view.setUint16(6, atd.option)
- return buffer
+ const buffer = new ArrayBuffer(8);
+ const view = new DataView(buffer);
+ view.setUint32(0, (atd.type << 24) + atd.id);
+ view.setUint8(4, atd.size);
+ view.setUint8(5, atd.category);
+ view.setUint16(6, atd.option);
+ return buffer;
}
function decodeTrigger(buffer: ArrayBuffer): AggregatableTriggerData {
- const view = new DataView(buffer)
- const first32 = view.getUint32(0)
- const type = first32 >>> 24
- const id = first32 & 0xffffff
- const size = view.getUint8(4)
- const category = view.getUint8(5)
- const option = view.getUint16(6)
- return { type, id, size, category, option }
+ const view = new DataView(buffer);
+ const first32 = view.getUint32(0);
+ const type = first32 >>> 24;
+ const id = first32 & 0xffffff;
+ const size = view.getUint8(4);
+ const category = view.getUint8(5);
+ const option = view.getUint16(6);
+ return {type, id, size, category, option};
}
export function decodeBucket(buffer: ArrayBuffer) {
- const u8a = new Uint8Array(buffer)
- const sourceBuf = u8a.slice(0, u8a.length / 2)
- const source: AggregationKeyStructure = decodeSource(sourceBuf.buffer)
- const triggerBuf = u8a.slice(u8a.length / 2, u8a.length)
- const trigger: AggregatableTriggerData = decodeTrigger(triggerBuf.buffer)
-
- const aggregation_keys: { [index: string]: string } = {}
- aggregation_keys.type = key_from_value(SOURCE_TYPE, source.type)
- aggregation_keys.dimension = key_from_value(DIMENSION, source.dimension)
- aggregation_keys.id = source.id.toString(16)
- aggregation_keys.advertiser = key_from_value(ADVERTISER, source.advertiser)
- aggregation_keys.publisher = key_from_value(PUBLISHER, source.publisher)
-
- const aggregatable_trigger_data: { [index: string]: string } = {}
- aggregatable_trigger_data.type = key_from_value(TRIGGER_TYPE, trigger.type)
- aggregatable_trigger_data.id = trigger.id.toString(16)
- aggregatable_trigger_data.size = trigger.size.toString()
- aggregatable_trigger_data.category = trigger.category.toString()
- aggregatable_trigger_data.option = trigger.option.toString()
+ const u8a = new Uint8Array(buffer);
+ const sourceBuf = u8a.slice(0, u8a.length / 2);
+ const source: AggregationKeyStructure = decodeSource(sourceBuf.buffer);
+ const triggerBuf = u8a.slice(u8a.length / 2, u8a.length);
+ const trigger: AggregatableTriggerData = decodeTrigger(triggerBuf.buffer);
+
+ const aggregation_keys: {[index: string]: string} = {};
+ aggregation_keys.type = key_from_value(SOURCE_TYPE, source.type);
+ aggregation_keys.dimension = key_from_value(DIMENSION, source.dimension);
+ aggregation_keys.id = source.id.toString(16);
+ aggregation_keys.advertiser = key_from_value(ADVERTISER, source.advertiser);
+ aggregation_keys.publisher = key_from_value(PUBLISHER, source.publisher);
+
+ const aggregatable_trigger_data: {[index: string]: string} = {};
+ aggregatable_trigger_data.type = key_from_value(TRIGGER_TYPE, trigger.type);
+ aggregatable_trigger_data.id = trigger.id.toString(16);
+ aggregatable_trigger_data.size = trigger.size.toString();
+ aggregatable_trigger_data.category = trigger.category.toString();
+ aggregatable_trigger_data.option = trigger.option.toString();
return {
aggregation_keys,
- aggregatable_trigger_data
- }
+ aggregatable_trigger_data,
+ };
}
export function sourceEventId() {
// 64bit dummy value
- return ((1n << 64n) - 1n).toString()
+ return ((1n << 64n) - 1n).toString();
}
export function debugKey(): string {
// 64bit dummy value
- return ((1n << 64n) - 2n).toString()
+ return ((1n << 64n) - 2n).toString();
}
function key_from_value(object: any, value: any) {
- const key: string = Object.keys(object).find((key) => object[key] === value) as string
+ const key: string = Object.keys(object).find(
+ (key) => object[key] === value,
+ ) as string;
- return key
+ return key;
}
function test() {
- const advertiser = ADVERTISER["shop"]
- const publisher = PUBLISHER["news"]
- const id = 0xff
- const dimension = DIMENSION["gross"]
- const size = (26.5 - 20) * 10
- const category = 1
- const source_type = SOURCE_TYPE.click
- const trigger_type = TRIGGER_TYPE.gross
- const option = 2
-
- const source_key = sourceKeyPiece({ type: source_type, dimension, id, advertiser, publisher })
- console.log({ source_key })
-
- const trigger_key = triggerKeyPiece({ type: trigger_type, id, size, category, option })
- console.log({ trigger_key })
-
- const source = encodeSource({ type: source_type, dimension, id, advertiser, publisher })
- console.log(decodeSource(source))
-
- const trigger = encodeTrigger({ type: trigger_type, id, size, category, option })
- console.log(decodeTrigger(trigger))
-}
-
-test()
+ const advertiser = ADVERTISER['shop'];
+ const publisher = PUBLISHER['news'];
+ const id = 0xff;
+ const dimension = DIMENSION['gross'];
+ const size = (26.5 - 20) * 10;
+ const category = 1;
+ const source_type = SOURCE_TYPE.click;
+ const trigger_type = TRIGGER_TYPE.gross;
+ const option = 2;
+
+ const source_key = sourceKeyPiece({
+ type: source_type,
+ dimension,
+ id,
+ advertiser,
+ publisher,
+ });
+ console.log({source_key});
+
+ const trigger_key = triggerKeyPiece({
+ type: trigger_type,
+ id,
+ size,
+ category,
+ option,
+ });
+ console.log({trigger_key});
+
+ const source = encodeSource({
+ type: source_type,
+ dimension,
+ id,
+ advertiser,
+ publisher,
+ });
+ console.log(decodeSource(source));
+
+ const trigger = encodeTrigger({
+ type: trigger_type,
+ id,
+ size,
+ category,
+ option,
+ });
+ console.log(decodeTrigger(trigger));
+}
+
+test();
diff --git a/services/dsp/src/index.ts b/services/dsp/src/index.ts
index ca5822ad..cbcb7e20 100644
--- a/services/dsp/src/index.ts
+++ b/services/dsp/src/index.ts
@@ -15,9 +15,9 @@
*/
// DSP
-import express, { Application, Request, Response } from "express"
-import cbor from "cbor"
-import { decodeDict } from "structured-field-values"
+import express, {Application, Request, Response} from 'express';
+import cbor from 'cbor';
+import {decodeDict} from 'structured-field-values';
import {
debugKey,
sourceEventId,
@@ -28,103 +28,130 @@ import {
DIMENSION,
decodeBucket,
SOURCE_TYPE,
- TRIGGER_TYPE
-} from "./arapi.js"
-
-const { EXTERNAL_PORT, PORT, DSP_HOST, DSP_TOKEN, DSP_DETAIL, SSP_HOST, SHOP_HOST } = process.env
+ TRIGGER_TYPE,
+} from './arapi.js';
+
+const {
+ EXTERNAL_PORT,
+ PORT,
+ DSP_HOST,
+ DSP_TOKEN,
+ DSP_DETAIL,
+ SSP_HOST,
+ SHOP_HOST,
+} = process.env;
// in-memory storage for debug reports
-const Reports: any[] = []
+const Reports: any[] = [];
// clear in-memory storage every 10 min
-setInterval(() => {
- Reports.length = 0
-}, 1000 * 60 * 10)
+setInterval(
+ () => {
+ Reports.length = 0;
+ },
+ 1000 * 60 * 10,
+);
-const app: Application = express()
+const app: Application = express();
app.use((req, res, next) => {
- res.setHeader("Origin-Trial", DSP_TOKEN as string)
- next()
-})
+ res.setHeader('Origin-Trial', DSP_TOKEN as string);
+ next();
+});
-app.use(express.urlencoded({ extended: true }))
+app.use(express.urlencoded({extended: true}));
-app.use(express.json()) // To parse the incoming requests with JSON payloads
+app.use(express.json()); // To parse the incoming requests with JSON payloads
app.use((req, res, next) => {
// enable transitional debugging reports (https://github.com/WICG/attribution-reporting-api/blob/main/EVENT.md#optional-transitional-debugging-reports)
- res.cookie("ar_debug", "1", {
- sameSite: "none",
+ res.cookie('ar_debug', '1', {
+ sameSite: 'none',
secure: true,
- httpOnly: true
- })
- next()
-})
+ httpOnly: true,
+ });
+ next();
+});
app.use(
- express.static("src/public", {
+ express.static('src/public', {
setHeaders: (res: Response, path, stat) => {
- const url = new URL(path, `https://${DSP_HOST}`)
- if (url.pathname.endsWith("bidding_logic.js")) {
- return res.set("X-Allow-FLEDGE", "true")
+ const url = new URL(path, `https://${DSP_HOST}`);
+ if (url.pathname.endsWith('bidding_logic.js')) {
+ return res.set('X-Allow-FLEDGE', 'true');
}
- if (url.pathname.endsWith("bidding_signal.json")) {
- return res.set("X-Allow-FLEDGE", "true")
+ if (url.pathname.endsWith('bidding_signal.json')) {
+ return res.set('X-Allow-FLEDGE', 'true');
}
- }
- })
-)
+ },
+ }),
+);
app.use((req, res, next) => {
// opt-in fencedframe
- if (req.get("sec-fetch-dest") === "fencedframe") {
- res.setHeader("Supports-Loading-Mode", "fenced-frame")
+ if (req.get('sec-fetch-dest') === 'fencedframe') {
+ res.setHeader('Supports-Loading-Mode', 'fenced-frame');
}
- next()
-})
+ next();
+});
-app.set("view engine", "ejs")
-app.set("views", "src/views")
+app.set('view engine', 'ejs');
+app.set('views', 'src/views');
-app.get("/ads", async (req, res) => {
- const { advertiser, id } = req.query
- console.log("Loading frame content : ", { advertiser, id })
+app.get('/ads', async (req, res) => {
+ const {advertiser, id} = req.query;
+ console.log('Loading frame content : ', {advertiser, id});
- const title = `Your special ads from ${advertiser}`
+ const title = `Your special ads from ${advertiser}`;
- const move = new URL(`https://${advertiser}:${EXTERNAL_PORT}/items/${id}`)
+ const move = new URL(`https://${advertiser}:${EXTERNAL_PORT}/items/${id}`);
- const creative = new URL(`https://${advertiser}:${EXTERNAL_PORT}/ads/${id}`)
+ const creative = new URL(`https://${advertiser}:${EXTERNAL_PORT}/ads/${id}`);
- const registerSource = new URL(`https://${DSP_HOST}:${EXTERNAL_PORT}/register-source`)
- registerSource.searchParams.append("advertiser", advertiser as string)
- registerSource.searchParams.append("id", id as string)
+ const registerSource = new URL(
+ `https://${DSP_HOST}:${EXTERNAL_PORT}/register-source`,
+ );
+ registerSource.searchParams.append('advertiser', advertiser as string);
+ registerSource.searchParams.append('id', id as string);
- res.render("ads.html.ejs", { title, move, creative, registerSource })
-})
+ res.render('ads.html.ejs', {title, move, creative, registerSource});
+});
-app.get("/join-ad-interest-group.html", async (req: Request, res: Response) => {
- const title = "Join Ad Interest Group"
- res.render("join-ad-interest-group", { title, DSP_TOKEN, DSP_HOST, EXTERNAL_PORT })
-})
+app.get('/join-ad-interest-group.html', async (req: Request, res: Response) => {
+ const title = 'Join Ad Interest Group';
+ res.render('join-ad-interest-group', {
+ title,
+ DSP_TOKEN,
+ DSP_HOST,
+ EXTERNAL_PORT,
+ });
+});
-app.get("/interest-group.json", async (req: Request, res: Response) => {
- const { advertiser, id, adType } = req.query
+app.get('/interest-group.json', async (req: Request, res: Response) => {
+ const {advertiser, id, adType} = req.query;
if (advertiser === undefined || id === undefined) {
- return res.sendStatus(400)
+ return res.sendStatus(400);
}
- const imageCreative = new URL(`https://${DSP_HOST}:${EXTERNAL_PORT}/ads`)
- imageCreative.searchParams.append("advertiser", advertiser as string)
- imageCreative.searchParams.append("id", id as string)
- const videoCreative = new URL(`https://${DSP_HOST}:${EXTERNAL_PORT}/html/video-ad-creative.html`)
- const renderUrl = adType === "video" ? videoCreative : imageCreative.toString()
-
- const owner = new URL(`https://${DSP_HOST}:${EXTERNAL_PORT}`)
- const biddingLogicUrl = new URL(`https://${DSP_HOST}:${EXTERNAL_PORT}/js/bidding_logic.js`)
- const trustedBiddingSignalsUrl = new URL(`https://${DSP_HOST}:${EXTERNAL_PORT}/bidding_signal.json`)
- const dailyUpdateUrl = new URL(`https://${DSP_HOST}:${EXTERNAL_PORT}/daily_update_url`)
+ const imageCreative = new URL(`https://${DSP_HOST}:${EXTERNAL_PORT}/ads`);
+ imageCreative.searchParams.append('advertiser', advertiser as string);
+ imageCreative.searchParams.append('id', id as string);
+ const videoCreative = new URL(
+ `https://${DSP_HOST}:${EXTERNAL_PORT}/html/video-ad-creative.html`,
+ );
+ const renderUrl =
+ adType === 'video' ? videoCreative : imageCreative.toString();
+
+ const owner = new URL(`https://${DSP_HOST}:${EXTERNAL_PORT}`);
+ const biddingLogicUrl = new URL(
+ `https://${DSP_HOST}:${EXTERNAL_PORT}/js/bidding_logic.js`,
+ );
+ const trustedBiddingSignalsUrl = new URL(
+ `https://${DSP_HOST}:${EXTERNAL_PORT}/bidding_signal.json`,
+ );
+ const dailyUpdateUrl = new URL(
+ `https://${DSP_HOST}:${EXTERNAL_PORT}/daily_update_url`,
+ );
res.json({
name: advertiser,
@@ -135,41 +162,44 @@ app.get("/interest-group.json", async (req: Request, res: Response) => {
// x-allow-fledge: true
trustedBiddingSignalsUrl,
- trustedBiddingSignalsKeys: ["trustedBiddingSignalsKeys-1", "trustedBiddingSignalsKeys-2"],
+ trustedBiddingSignalsKeys: [
+ 'trustedBiddingSignalsKeys-1',
+ 'trustedBiddingSignalsKeys-2',
+ ],
// dailyUpdateUrl, // not implemented yet
userBiddingSignals: {
- user_bidding_signals: "user_bidding_signals"
+ user_bidding_signals: 'user_bidding_signals',
},
ads: [
{
renderUrl,
metadata: {
- type: advertiser
- }
- }
- ]
- })
-})
+ type: advertiser,
+ },
+ },
+ ],
+ });
+});
-app.get("/bidding_signal.json", async (req: Request, res: Response) => {
- res.setHeader("X-Allow-FLEDGE", "true")
- res.setHeader("X-fledge-bidding-signals-format-version", "2")
+app.get('/bidding_signal.json', async (req: Request, res: Response) => {
+ res.setHeader('X-Allow-FLEDGE', 'true');
+ res.setHeader('X-fledge-bidding-signals-format-version', '2');
res.json({
keys: {
- key1: "xxxxxxxx",
- key2: "yyyyyyyy"
+ key1: 'xxxxxxxx',
+ key2: 'yyyyyyyy',
},
perInterestGroupData: {
name1: {
priorityVector: {
signal1: 100,
- signal2: 200
- }
- }
- }
- })
-})
+ signal2: 200,
+ },
+ },
+ },
+ });
+});
// TODO: Implement
// app.get("/daily_update_url", async (req: Request, res: Response) => {
@@ -178,203 +208,243 @@ app.get("/bidding_signal.json", async (req: Request, res: Response) => {
// ************************************************************************
// [START] Section for Attribution Reporting API Code ***
// ************************************************************************
-app.get("/register-source", async (req: Request, res: Response) => {
- const advertiser: string = req.query.advertiser as string
- const id: string = req.query.id as string
+app.get('/register-source', async (req: Request, res: Response) => {
+ const advertiser: string = req.query.advertiser as string;
+ const id: string = req.query.id as string;
- console.log("Registering source attribution for", { advertiser, id })
- if (req.headers["attribution-reporting-eligible"]) {
+ console.log('Registering source attribution for', {advertiser, id});
+ if (req.headers['attribution-reporting-eligible']) {
//const are = req.headers["attribution-reporting-eligible"].split(",").map((e) => e.trim())
- const are = decodeDict(req.headers["attribution-reporting-eligible"] as string)
+ const are = decodeDict(
+ req.headers['attribution-reporting-eligible'] as string,
+ );
// register navigation source
- if ("navigation-source" in are) {
- const destination = `https://${advertiser}`
- const source_event_id = sourceEventId()
- const debug_key = debugKey()
+ if ('navigation-source' in are) {
+ const destination = `https://${advertiser}`;
+ const source_event_id = sourceEventId();
+ const debug_key = debugKey();
const AttributionReportingRegisterSource = {
destination,
source_event_id,
debug_key,
aggregation_keys: {
quantity: sourceKeyPiece({
- type: SOURCE_TYPE["click"], // click attribution
+ type: SOURCE_TYPE['click'], // click attribution
advertiser: ADVERTISER[advertiser],
- publisher: PUBLISHER["news"],
+ publisher: PUBLISHER['news'],
id: Number(`0x${id}`),
- dimension: DIMENSION["quantity"]
+ dimension: DIMENSION['quantity'],
}),
gross: sourceKeyPiece({
- type: SOURCE_TYPE["click"], // click attribution
+ type: SOURCE_TYPE['click'], // click attribution
advertiser: ADVERTISER[advertiser],
- publisher: PUBLISHER["news"],
+ publisher: PUBLISHER['news'],
id: Number(`0x${id}`),
- dimension: DIMENSION["gross"]
- })
- }
- }
-
- console.log("Registering navigation source :", { AttributionReportingRegisterSource })
- res.setHeader("Attribution-Reporting-Register-Source", JSON.stringify(AttributionReportingRegisterSource))
- res.status(200).send("attribution nevigation (click) source registered")
+ dimension: DIMENSION['gross'],
+ }),
+ },
+ };
+
+ console.log('Registering navigation source :', {
+ AttributionReportingRegisterSource,
+ });
+ res.setHeader(
+ 'Attribution-Reporting-Register-Source',
+ JSON.stringify(AttributionReportingRegisterSource),
+ );
+ res.status(200).send('attribution nevigation (click) source registered');
}
// register event source
- else if ("event-source" in are) {
- const destination = `https://${advertiser}`
- const source_event_id = sourceEventId()
- const debug_key = debugKey()
+ else if ('event-source' in are) {
+ const destination = `https://${advertiser}`;
+ const source_event_id = sourceEventId();
+ const debug_key = debugKey();
const AttributionReportingRegisterSource = {
destination,
source_event_id,
debug_key,
aggregation_keys: {
quantity: sourceKeyPiece({
- type: SOURCE_TYPE["view"], // view attribution
+ type: SOURCE_TYPE['view'], // view attribution
advertiser: ADVERTISER[advertiser],
- publisher: PUBLISHER["news"],
+ publisher: PUBLISHER['news'],
id: Number(`0x${id}`),
- dimension: DIMENSION["quantity"]
+ dimension: DIMENSION['quantity'],
}),
gross: sourceKeyPiece({
- type: SOURCE_TYPE["view"], // view attribution
+ type: SOURCE_TYPE['view'], // view attribution
advertiser: ADVERTISER[advertiser],
- publisher: PUBLISHER["news"],
+ publisher: PUBLISHER['news'],
id: Number(`0x${id}`),
- dimension: DIMENSION["gross"]
- })
- }
- }
-
- console.log("Registering event source :", { AttributionReportingRegisterSource })
- res.setHeader("Attribution-Reporting-Register-Source", JSON.stringify(AttributionReportingRegisterSource))
- res.status(200).send("attribution event (view) source registered")
+ dimension: DIMENSION['gross'],
+ }),
+ },
+ };
+
+ console.log('Registering event source :', {
+ AttributionReportingRegisterSource,
+ });
+ res.setHeader(
+ 'Attribution-Reporting-Register-Source',
+ JSON.stringify(AttributionReportingRegisterSource),
+ );
+ res.status(200).send('attribution event (view) source registered');
} else {
- res.status(400).send("'Attribution-Reporting-Eligible' header is malformed") // just send back response header. no content.
+ res
+ .status(400)
+ .send("'Attribution-Reporting-Eligible' header is malformed"); // just send back response header. no content.
}
} else {
- res.status(400).send("'Attribution-Reporting-Eligible' header is missing") // just send back response header. no content.
+ res.status(400).send("'Attribution-Reporting-Eligible' header is missing"); // just send back response header. no content.
}
-})
+});
-app.get("/register-trigger", async (req: Request, res: Response) => {
- const id: string = req.query.id as string
- const quantity: string = req.query.quantity as string
- const size: string = req.query.size as string
- const category: string = req.query.category as string
- const gross: string = req.query.gross as string
+app.get('/register-trigger', async (req: Request, res: Response) => {
+ const id: string = req.query.id as string;
+ const quantity: string = req.query.quantity as string;
+ const size: string = req.query.size as string;
+ const category: string = req.query.category as string;
+ const gross: string = req.query.gross as string;
const AttributionReportingRegisterTrigger = {
aggregatable_trigger_data: [
{
key_piece: triggerKeyPiece({
- type: TRIGGER_TYPE["quantity"],
+ type: TRIGGER_TYPE['quantity'],
id: parseInt(id, 16),
size: Number(size),
category: Number(category),
- option: 0
+ option: 0,
}),
- source_keys: ["quantity"]
+ source_keys: ['quantity'],
},
{
key_piece: triggerKeyPiece({
- type: TRIGGER_TYPE["gross"],
+ type: TRIGGER_TYPE['gross'],
id: parseInt(id, 16),
size: Number(size),
category: Number(category),
- option: 0
+ option: 0,
}),
- source_keys: ["gross"]
- }
+ source_keys: ['gross'],
+ },
],
aggregatable_values: {
// TODO: scaling
quantity: Number(quantity),
- gross: Number(gross)
+ gross: Number(gross),
},
- debug_key: debugKey()
- }
- res.setHeader("Attribution-Reporting-Register-Trigger", JSON.stringify(AttributionReportingRegisterTrigger))
- res.sendStatus(200)
-})
-
-app.post("/.well-known/attribution-reporting/debug/report-aggregate-attribution", async (req: Request, res: Response) => {
- console.log(`Attribution Reporting - Received Aggregatable Report on debug endpoint`)
- const debug_report = req.body
- debug_report.shared_info = JSON.parse(debug_report.shared_info)
-
- console.log(JSON.stringify(debug_report, null, "\t"))
-
- debug_report.aggregation_service_payloads = debug_report.aggregation_service_payloads.map((e: any) => {
- const plain = Buffer.from(e.debug_cleartext_payload, "base64")
- const debug_cleartext_payload = cbor.decodeAllSync(plain)
- e.debug_cleartext_payload = debug_cleartext_payload.map(({ data, operation }) => {
- return {
- operation,
- data: data.map(({ value, bucket }: any) => {
- return {
- value: value.readUInt32BE(0),
- bucket: decodeBucket(bucket)
- }
- })
- }
- })
- return e
- })
-
- console.log(JSON.stringify(debug_report, null, "\t"))
-
- // save to global storage
- Reports.push(debug_report)
-
- res.sendStatus(200)
-})
-
-app.post("/.well-known/attribution-reporting/report-aggregate-attribution", async (req: Request, res: Response) => {
- console.log(`Attribution Reporting - Received Aggregatable Report on live endpoint`)
- const report = req.body
- report.shared_info = JSON.parse(report.shared_info)
- console.log(JSON.stringify(report, null, "\t"))
- res.sendStatus(200)
-})
-
-app.get("/reports", async (req, res) => {
- res.render("reports.html.ejs", { title: "Report", Reports })
-})
+ debug_key: debugKey(),
+ };
+ res.setHeader(
+ 'Attribution-Reporting-Register-Trigger',
+ JSON.stringify(AttributionReportingRegisterTrigger),
+ );
+ res.sendStatus(200);
+});
+
+app.post(
+ '/.well-known/attribution-reporting/debug/report-aggregate-attribution',
+ async (req: Request, res: Response) => {
+ console.log(
+ `Attribution Reporting - Received Aggregatable Report on debug endpoint`,
+ );
+ const debug_report = req.body;
+ debug_report.shared_info = JSON.parse(debug_report.shared_info);
+
+ console.log(JSON.stringify(debug_report, null, '\t'));
+
+ debug_report.aggregation_service_payloads =
+ debug_report.aggregation_service_payloads.map((e: any) => {
+ const plain = Buffer.from(e.debug_cleartext_payload, 'base64');
+ const debug_cleartext_payload = cbor.decodeAllSync(plain);
+ e.debug_cleartext_payload = debug_cleartext_payload.map(
+ ({data, operation}) => {
+ return {
+ operation,
+ data: data.map(({value, bucket}: any) => {
+ return {
+ value: value.readUInt32BE(0),
+ bucket: decodeBucket(bucket),
+ };
+ }),
+ };
+ },
+ );
+ return e;
+ });
+
+ console.log(JSON.stringify(debug_report, null, '\t'));
+
+ // save to global storage
+ Reports.push(debug_report);
+
+ res.sendStatus(200);
+ },
+);
+
+app.post(
+ '/.well-known/attribution-reporting/report-aggregate-attribution',
+ async (req: Request, res: Response) => {
+ console.log(
+ `Attribution Reporting - Received Aggregatable Report on live endpoint`,
+ );
+ const report = req.body;
+ report.shared_info = JSON.parse(report.shared_info);
+ console.log(JSON.stringify(report, null, '\t'));
+ res.sendStatus(200);
+ },
+);
+
+app.get('/reports', async (req, res) => {
+ res.render('reports.html.ejs', {title: 'Report', Reports});
+});
// ************************************************************************
// [END] Section for Attribution Reporting API Code ***
// ************************************************************************
-app.post("/.well-known/private-aggregation/report-shared-storage", (req, res) => {
- console.log(`Private Aggregation for Shared Storage - Received Aggregatable Report on live endpoint`)
+app.post(
+ '/.well-known/private-aggregation/report-shared-storage',
+ (req, res) => {
+ console.log(
+ `Private Aggregation for Shared Storage - Received Aggregatable Report on live endpoint`,
+ );
- let aggregationReport = req.body
- console.log(req.body)
+ let aggregationReport = req.body;
+ console.log(req.body);
- res.sendStatus(200)
-})
+ res.sendStatus(200);
+ },
+);
-app.get("/private-aggregation", (req, res) => {
- res.render("private-aggregation")
-})
+app.get('/private-aggregation', (req, res) => {
+ res.render('private-aggregation');
+});
-app.post("/.well-known/private-aggregation/debug/report-shared-storage", (req, res) => {
- let timeStr = new Date().toISOString()
- console.log(`Private Aggregation for Shared Storage - Received Aggregatable Report on debug endpoint`)
+app.post(
+ '/.well-known/private-aggregation/debug/report-shared-storage',
+ (req, res) => {
+ let timeStr = new Date().toISOString();
+ console.log(
+ `Private Aggregation for Shared Storage - Received Aggregatable Report on debug endpoint`,
+ );
- let aggregationReport = req.body
+ let aggregationReport = req.body;
- console.log(aggregationReport)
+ console.log(aggregationReport);
- res.sendStatus(200)
-})
+ res.sendStatus(200);
+ },
+);
-app.get("/", async (req: Request, res: Response) => {
- const title = DSP_DETAIL
- res.render("index", { title, DSP_HOST, SHOP_HOST, EXTERNAL_PORT })
-})
+app.get('/', async (req: Request, res: Response) => {
+ const title = DSP_DETAIL;
+ res.render('index', {title, DSP_HOST, SHOP_HOST, EXTERNAL_PORT});
+});
app.listen(PORT, function () {
- console.log(`Listening on port ${PORT}`)
-})
+ console.log(`Listening on port ${PORT}`);
+});
diff --git a/services/dsp/src/public/.well-known/privacy-sandbox-attestations.json b/services/dsp/src/public/.well-known/privacy-sandbox-attestations.json
index 8f0c0b1a..49ce79f2 100644
--- a/services/dsp/src/public/.well-known/privacy-sandbox-attestations.json
+++ b/services/dsp/src/public/.well-known/privacy-sandbox-attestations.json
@@ -3,7 +3,9 @@
{
"attestation_parser_version": "2",
"attestation_version": "2",
- "privacy_policy": ["https://policies.google.com/privacy"],
+ "privacy_policy": [
+ "https://policies.google.com/privacy"
+ ],
"ownership_token": "SGYvJieJb5EaxhMAJUh9qmrfZNSQewLYbHWx6NpeRzDLZSF0DwfVfchu5sHCxaFv",
"issued_seconds_since_epoch": 1697505143,
"enrollment_id": "UNRJE",
@@ -38,7 +40,9 @@
{
"attestation_parser_version": "1",
"attestation_version": "1",
- "privacy_policy": ["https://policies.google.com/privacy"],
+ "privacy_policy": [
+ "https://policies.google.com/privacy"
+ ],
"ownership_token": "yE67UyEyxUmBsz0y0hyeiU44CnbliMjMS93fnBgeb8Jlst1YbwYXAE5ucVige0eX",
"issued_seconds_since_epoch": 1691175921,
"expiry_seconds_since_epoch": 1706731521,
diff --git a/services/dsp/src/public/css/index.css b/services/dsp/src/public/css/index.css
index c6549571..361a8fe5 100644
--- a/services/dsp/src/public/css/index.css
+++ b/services/dsp/src/public/css/index.css
@@ -15,7 +15,7 @@
*/
body {
- font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
width: 50vw;
margin: 0 auto;
color: #555;
diff --git a/services/dsp/src/public/dsp-tag.js b/services/dsp/src/public/dsp-tag.js
index 82e17633..fc294441 100644
--- a/services/dsp/src/public/dsp-tag.js
+++ b/services/dsp/src/public/dsp-tag.js
@@ -14,24 +14,24 @@
limitations under the License.
*/
-;(async () => {
- const $script = document.querySelector("script.dsp_tag")
- const src = new URL($script.src)
- const advertiser = $script.dataset.advertiser
- const id = $script.dataset.id
+(async () => {
+ const $script = document.querySelector('script.dsp_tag');
+ const src = new URL($script.src);
+ const advertiser = $script.dataset.advertiser;
+ const id = $script.dataset.id;
- src.pathname = "join-ad-interest-group.html"
- src.searchParams.append("advertiser", advertiser)
- src.searchParams.append("id", id)
- const currentUrl = new URL(location.href)
+ src.pathname = 'join-ad-interest-group.html';
+ src.searchParams.append('advertiser', advertiser);
+ src.searchParams.append('id', id);
+ const currentUrl = new URL(location.href);
for (const searchParam of currentUrl.searchParams) {
- src.searchParams.append(searchParam[0], searchParam[1])
+ src.searchParams.append(searchParam[0], searchParam[1]);
}
- const $iframe = document.createElement("iframe")
- $iframe.width = 1
- $iframe.height = 1
- $iframe.src = src
- $iframe.allow = "join-ad-interest-group"
- $script.parentElement.insertBefore($iframe, $script.nextSibling)
-})()
+ const $iframe = document.createElement('iframe');
+ $iframe.width = 1;
+ $iframe.height = 1;
+ $iframe.src = src;
+ $iframe.allow = 'join-ad-interest-group';
+ $script.parentElement.insertBefore($iframe, $script.nextSibling);
+})();
diff --git a/services/dsp/src/public/html/video-ad-creative.html b/services/dsp/src/public/html/video-ad-creative.html
index 10286d07..58c6e085 100644
--- a/services/dsp/src/public/html/video-ad-creative.html
+++ b/services/dsp/src/public/html/video-ad-creative.html
@@ -1,4 +1,4 @@
-
+
diff --git a/services/dsp/src/public/js/bidding_logic.js b/services/dsp/src/public/js/bidding_logic.js
index c8e694d6..bf0b1659 100644
--- a/services/dsp/src/public/js/bidding_logic.js
+++ b/services/dsp/src/public/js/bidding_logic.js
@@ -18,27 +18,38 @@ function log(label, o) {
// console.log(label, JSON.stringify(o, " ", " "))
}
-function generateBid(interestGroup, auctionSignals, perBuyerSignals, trustedBiddingSignals, browserSignals) {
- log("generateBid", {
+function generateBid(
+ interestGroup,
+ auctionSignals,
+ perBuyerSignals,
+ trustedBiddingSignals,
+ browserSignals,
+) {
+ log('generateBid', {
interestGroup,
auctionSignals,
perBuyerSignals,
trustedBiddingSignals,
- browserSignals
- })
+ browserSignals,
+ });
return {
- ad: "ad-metadata",
+ ad: 'ad-metadata',
bid: Math.floor(Math.random() * 100, 10),
- render: interestGroup.ads[0].renderUrl
- }
+ render: interestGroup.ads[0].renderUrl,
+ };
}
-function reportWin(auctionSignals, perBuyerSignals, sellerSignals, browserSignals) {
- log("reportWin", {
+function reportWin(
+ auctionSignals,
+ perBuyerSignals,
+ sellerSignals,
+ browserSignals,
+) {
+ log('reportWin', {
auctionSignals,
perBuyerSignals,
sellerSignals,
- browserSignals
- })
- sendReportTo(browserSignals.interestGroupOwner + "/reporting?report=win")
+ browserSignals,
+ });
+ sendReportTo(browserSignals.interestGroupOwner + '/reporting?report=win');
}
diff --git a/services/dsp/src/public/js/dsp.js b/services/dsp/src/public/js/dsp.js
index 7a591901..ac98465e 100644
--- a/services/dsp/src/public/js/dsp.js
+++ b/services/dsp/src/public/js/dsp.js
@@ -1,7 +1,7 @@
-let dsp = document.currentScript.getAttribute('dsp')
+let dsp = document.currentScript.getAttribute('dsp');
window.addEventListener('load', (event) => {
- let iframe = document.createElement('iframe')
+ let iframe = document.createElement('iframe');
// let dsp = document.currentScript.getAttribute('dsp');
- iframe.src = `https://${dsp}/private-aggregation`
- document.body.appendChild(iframe)
-})
+ iframe.src = `https://${dsp}/private-aggregation`;
+ document.body.appendChild(iframe);
+});
diff --git a/services/dsp/src/public/js/join-ad-interest-group.js b/services/dsp/src/public/js/join-ad-interest-group.js
index eabfe7a9..424fb1b5 100644
--- a/services/dsp/src/public/js/join-ad-interest-group.js
+++ b/services/dsp/src/public/js/join-ad-interest-group.js
@@ -16,28 +16,28 @@
// Protected Audience API
async function getInterestGroupFromServer() {
- const currentUrl = new URL(location.href)
- const interestGroupUrl = new URL(location.origin)
- interestGroupUrl.pathname = "/interest-group.json"
+ const currentUrl = new URL(location.href);
+ const interestGroupUrl = new URL(location.origin);
+ interestGroupUrl.pathname = '/interest-group.json';
for (const searchParam of currentUrl.searchParams) {
- interestGroupUrl.searchParams.append(searchParam[0], searchParam[1])
+ interestGroupUrl.searchParams.append(searchParam[0], searchParam[1]);
}
- const res = await fetch(interestGroupUrl)
+ const res = await fetch(interestGroupUrl);
if (res.ok) {
- return res.json()
+ return res.json();
}
}
-document.addEventListener("DOMContentLoaded", async (e) => {
+document.addEventListener('DOMContentLoaded', async (e) => {
if (navigator.joinAdInterestGroup === undefined) {
- return console.log("[DEMO] Protected Audience API is not supported")
+ return console.log('[DEMO] Protected Audience API is not supported');
}
- const interestGroup = await getInterestGroupFromServer()
- console.log(`[DEMO] ${{ interestGroup }}`)
- const kSecsPerDay = 3600 * 24 * 30
- console.log(await navigator.joinAdInterestGroup(interestGroup, kSecsPerDay))
+ const interestGroup = await getInterestGroupFromServer();
+ console.log(`[DEMO] ${{interestGroup}}`);
+ const kSecsPerDay = 3600 * 24 * 30;
+ console.log(await navigator.joinAdInterestGroup(interestGroup, kSecsPerDay));
// TODO: consider using Topics API for choosing Ads
// const topics = await document.browsingTopics?.()
// console.log({ topics })
-})
+});
diff --git a/services/dsp/src/public/js/main.js b/services/dsp/src/public/js/main.js
index e2393cc6..95f50e16 100644
--- a/services/dsp/src/public/js/main.js
+++ b/services/dsp/src/public/js/main.js
@@ -14,4 +14,4 @@
limitations under the License.
*/
-console.log("main.js")
+console.log('main.js');
diff --git a/services/dsp/src/public/js/private-aggregation-worklet.js b/services/dsp/src/public/js/private-aggregation-worklet.js
index fa4d4050..dfc4b533 100644
--- a/services/dsp/src/public/js/private-aggregation-worklet.js
+++ b/services/dsp/src/public/js/private-aggregation-worklet.js
@@ -1,22 +1,24 @@
class TestPrivateAggregation {
async run(data) {
- console.log('Enabling Private Aggregation Debug Mode')
- privateAggregation.enableDebugMode({ debugKey: 1234n })
- let campaignId = await sharedStorage.get('campaignId')
+ console.log('Enabling Private Aggregation Debug Mode');
+ privateAggregation.enableDebugMode({debugKey: 1234n});
+ let campaignId = await sharedStorage.get('campaignId');
if (!campaignId) {
- console.log('No campaign id found for client. Adding campaignId 1234567890.')
- campaignId = '1234567890'
- sharedStorage.set('campaignId', campaignId)
+ console.log(
+ 'No campaign id found for client. Adding campaignId 1234567890.',
+ );
+ campaignId = '1234567890';
+ sharedStorage.set('campaignId', campaignId);
} else {
- console.log(`Campaign ID found: ${campaignId}`)
+ console.log(`Campaign ID found: ${campaignId}`);
}
function convertToBucket(bucketId) {
- return BigInt(bucketId)
+ return BigInt(bucketId);
}
- const bucket = convertToBucket(campaignId)
- const value = 128
- privateAggregation.contributeToHistogram({ bucket, value })
+ const bucket = convertToBucket(campaignId);
+ const value = 128;
+ privateAggregation.contributeToHistogram({bucket, value});
}
}
-register('test-private-aggregation', TestPrivateAggregation)
+register('test-private-aggregation', TestPrivateAggregation);
diff --git a/services/dsp/src/public/js/private-aggregation.js b/services/dsp/src/public/js/private-aggregation.js
index a2593dc4..e071dc28 100644
--- a/services/dsp/src/public/js/private-aggregation.js
+++ b/services/dsp/src/public/js/private-aggregation.js
@@ -1,6 +1,8 @@
async function runPrivateAggregation() {
- await window.sharedStorage.worklet.addModule('js/private-aggregation-worklet.js')
- await window.sharedStorage.run('test-private-aggregation')
+ await window.sharedStorage.worklet.addModule(
+ 'js/private-aggregation-worklet.js',
+ );
+ await window.sharedStorage.run('test-private-aggregation');
}
-runPrivateAggregation()
+runPrivateAggregation();
diff --git a/services/dsp/src/public/js/video-ad-creative.js b/services/dsp/src/public/js/video-ad-creative.js
index 2e040206..f028e2b1 100644
--- a/services/dsp/src/public/js/video-ad-creative.js
+++ b/services/dsp/src/public/js/video-ad-creative.js
@@ -1,11 +1,11 @@
-;(async () => {
+(async () => {
const data = {
adVastUrl:
- "https://pubads.g.doubleclick.net/gampad/ads?" +
- "iu=/21775744923/external/single_ad_samples&sz=640x480&" +
- "cust_params=sample_ct%3Dlinear&ciu_szs=300x250%2C728x90&" +
- "gdfp_req=1&output=vast&unviewed_position_start=1&env=vp&" +
- "impl=s&correlator="
- }
- window.top.postMessage(JSON.stringify(data), "*")
-})()
+ 'https://pubads.g.doubleclick.net/gampad/ads?' +
+ 'iu=/21775744923/external/single_ad_samples&sz=640x480&' +
+ 'cust_params=sample_ct%3Dlinear&ciu_szs=300x250%2C728x90&' +
+ 'gdfp_req=1&output=vast&unviewed_position_start=1&env=vp&' +
+ 'impl=s&correlator=',
+ };
+ window.top.postMessage(JSON.stringify(data), '*');
+})();
diff --git a/services/home/babel.config.js b/services/home/babel.config.js
index 26b014a1..e00595da 100644
--- a/services/home/babel.config.js
+++ b/services/home/babel.config.js
@@ -1,3 +1,3 @@
module.exports = {
- presets: [require.resolve("@docusaurus/core/lib/babel/preset")]
-}
+ presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
+};
diff --git a/services/home/docs/demos/index.md b/services/home/docs/demos/index.md
index 27ebf474..9ff74bf0 100644
--- a/services/home/docs/demos/index.md
+++ b/services/home/docs/demos/index.md
@@ -5,10 +5,8 @@ sidebar_position: 1
# Privacy Sandbox Demos
-:::note
-Looking for running these demos on your local environment ?
-Check the [deployment guide](https://github.com/privacysandbox/privacy-sandbox-demos/blob/main/README.md) on the GitHub.
-:::
+:::note Looking for running these demos on your local environment ? Check the
+[deployment guide](https://github.com/privacysandbox/privacy-sandbox-demos/blob/main/README.md) on the GitHub. :::
| **Category** | **Use Case** | **Privacy Sandbox APIs** | **Relevant for** |
| :---------------------------: | :------------------------------------------------------------------------: | :--------------------------------------------: | :-----------------------------: |
@@ -16,7 +14,6 @@ Check the [deployment guide](https://github.com/privacysandbox/privacy-sandbox-d
| Show Relevant Content and Ads | [Retargeting / Remarketing](retargeting-remarketing) | Protected Audience API | Publisher, SSP, Advertiser, DSP |
| Measure Digital Ads | [Single-touch conversion Attribution](single-touch-conversion-attribution) | Attribution Reporting API, Aggregation Service | Publisher, SSP, Advertiser, DSP |
-:::info
-Looking for a use case not listed here ?
-Help us growing this repository by [contributing](https://github.com/privacysandbox/privacy-sandbox-demos/tree/main/docs/contribute) or [sharing feedback](https://github.com/privacysandbox/privacy-sandbox-demos/issues).
-:::
+:::info Looking for a use case not listed here ? Help us growing this repository by
+[contributing](https://github.com/privacysandbox/privacy-sandbox-demos/tree/main/docs/contribute) or
+[sharing feedback](https://github.com/privacysandbox/privacy-sandbox-demos/issues). :::
diff --git a/services/home/docs/demos/retargeting-remarketing.md b/services/home/docs/demos/retargeting-remarketing.md
index aceaf86b..572acdb9 100644
--- a/services/home/docs/demos/retargeting-remarketing.md
+++ b/services/home/docs/demos/retargeting-remarketing.md
@@ -11,8 +11,7 @@ more_data:
- DSP
---
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
+import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
# Retargeting / Remarketing
@@ -23,7 +22,9 @@ import TabItem from '@theme/TabItem';
### Description
-Remarketing is a type of online advertising that allows you to show ads to people who have already visited your website. You can create custom audiences based on different criteria, such as pages visited or products added to the cart. Remarketing can help you increase brand awareness, drive traffic back to your website, and boost sales.
+Remarketing is a type of online advertising that allows you to show ads to people who have already visited your website. You can create custom
+audiences based on different criteria, such as pages visited or products added to the cart. Remarketing can help you increase brand awareness, drive
+traffic back to your website, and boost sales.
### Privacy Sandbox APIs
@@ -43,25 +44,30 @@ Remarketing is a type of online advertising that allows you to show ads to peopl
### Goals
-In this demo, we assume an advertiser would like to drive traffic back to their website. Remarketing can help an advertiser to get people who have already visited their website to come back for more or to complete a purchase. This can be done by showing them ads about the product they have previously looked at, on other websites.
+In this demo, we assume an advertiser would like to drive traffic back to their website. Remarketing can help an advertiser to get people who have
+already visited their website to come back for more or to complete a purchase. This can be done by showing them ads about the product they have
+previously looked at, on other websites.
### Assumptions
-This use case assumes the advertiser (shop site) can bid on the publisher (news site) inventory through an agreement between their respective DSP and SSP platforms.
+This use case assumes the advertiser (shop site) can bid on the publisher (news site) inventory through an agreement between their respective DSP and
+SSP platforms.
### Key Exclusions
-The demo does not integrate existing auction mechanisms (prebid or header bidding…). it is only scoped to on-device auction with Protected Audience API.
-The ad selection is very straightforward (only 1 bidder).
-The bidding logic does not include real-time signals from Key/Value service.
+The demo does not integrate existing auction mechanisms (prebid or header bidding…). it is only scoped to on-device auction with Protected Audience
+API. The ad selection is very straightforward (only 1 bidder). The bidding logic does not include real-time signals from Key/Value service.
### System Design
-Using Protected Audience API, the user visits a shopping site, and gets added to an interest group. Later the same user visits a news site. There the browser runs an on-device Auction, bidding logic will select the winning interest group, and relevant ads will be dynamically rendered on the publisher page.
+Using Protected Audience API, the user visits a shopping site, and gets added to an interest group. Later the same user visits a news site. There the
+browser runs an on-device Auction, bidding logic will select the winning interest group, and relevant ads will be dynamically rendered on the
+publisher page.
#### Protected Audience Flow
-Below is a general introduction of Remarketing using Privacy Sandbox Protected Audience API. For further information see [Protected Audience API - Chrome Developers](https://developer.chrome.com/docs/privacy-sandbox/protected-audience/#overview).
+Below is a general introduction of Remarketing using Privacy Sandbox Protected Audience API. For further information see
+[Protected Audience API - Chrome Developers](https://developer.chrome.com/docs/privacy-sandbox/protected-audience/#overview).
![Protected Audience Flow](./img/retargeting-remarketing-flow.png)
@@ -124,18 +130,22 @@ Note right of Browser:Scenario 1 stops here
1. [Navigate to shop site](https://privacy-sandbox-demos-shop.dev/) (advertiser)
2. Click on a “shoe” product item on the shop site.
- - The shop (advertiser) would assume the user is interested in this type of product, so they would leverage Protected Audience API and ask the browser to join an ad interest group for this product or this specific product category.
+ - The shop (advertiser) would assume the user is interested in this type of product, so they would leverage Protected Audience API and ask the
+ browser to join an ad interest group for this product or this specific product category.
3. [Navigate to the news site](https://privacy-sandbox-demos-news.dev/) (publisher)
4. Observe the ad served on the news site
- If you previously browsed the “shoe” product on the shop site, you will be shown an ad for the same product.
- When the page was loaded, Protected Audience API allowed the SSP to run an ad auction on the publisher site.
- - The winning advertiser of this ad auction gets their ad creative to be displayed on the publisher site. In this case you have cleared the browser history and only browsed 1 advertiser site page so you are only seeing 1 ad creative from the same advertiser.
+ - The winning advertiser of this ad auction gets their ad creative to be displayed on the publisher site. In this case you have cleared the browser
+ history and only browsed 1 advertiser site page so you are only seeing 1 ad creative from the same advertiser.
### Implementation details
#### How is the user added to an Interest Group based on his browsing behavior ? (see step #2 of User Journey)
-The shop product page [includes dsp-tag.js ](https://github.com/privacysandbox/privacy-sandbox-demos/blob/939dd4928ec9cb4628b3f9424081bbd912346bcf/services/shop/src/index.ts#L93) from the DSP service. This is a third-party tag from the DSP service.
+The shop product page
+[includes dsp-tag.js](https://github.com/privacysandbox/privacy-sandbox-demos/blob/939dd4928ec9cb4628b3f9424081bbd912346bcf/services/shop/src/index.ts#L93)
+from the DSP service. This is a third-party tag from the DSP service.
```html
```
-The [dsp-tags.js](https://github.com/privacysandbox/privacy-sandbox-demos/blob/939dd4928ec9cb4628b3f9424081bbd912346bcf/services/dsp/src/public/dsp-tag.js#L17) dynamically embeds an iframe
+The
+[dsp-tags.js](https://github.com/privacysandbox/privacy-sandbox-demos/blob/939dd4928ec9cb4628b3f9424081bbd912346bcf/services/dsp/src/public/dsp-tag.js#L17)
+dynamically embeds an iframe
```html
```
-The iframe calls a third-party script [join-ad-interest-group.js](https://github.com/privacysandbox/privacy-sandbox-demos/blob/939dd4928ec9cb4628b3f9424081bbd912346bcf/services/dsp/src/public/js/join-ad-interest-group.js#L31) to join interest group using Protected Audience API
+The iframe calls a third-party script
+[join-ad-interest-group.js](https://github.com/privacysandbox/privacy-sandbox-demos/blob/939dd4928ec9cb4628b3f9424081bbd912346bcf/services/dsp/src/public/js/join-ad-interest-group.js#L31)
+to join interest group using Protected Audience API
```js title="https://github.com/privacysandbox/privacy-sandbox-demos/blob/main/services/dsp/src/public/js/join-ad-interest-group.js"
// Protected Audience API
@@ -185,12 +199,16 @@ document.addEventListener("DOMContentLoaded", async (e) => {
})
```
-This code sets up the interest groups options. Those options are fetched dynamically from [interest-group.json](https://github.com/privacysandbox/privacy-sandbox-demos/blob/939dd4928ec9cb4628b3f9424081bbd912346bcf/services/dsp/src/index.ts#L50).
-Finally the code requests the browser to [join the interest group](https://github.com/privacysandbox/privacy-sandbox-demos/blob/939dd4928ec9cb4628b3f9424081bbd912346bcf/services/dsp/src/public/js/join-ad-interest-group.js#L38)
+This code sets up the interest groups options. Those options are fetched dynamically from
+[interest-group.json](https://github.com/privacysandbox/privacy-sandbox-demos/blob/939dd4928ec9cb4628b3f9424081bbd912346bcf/services/dsp/src/index.ts#L50).
+Finally the code requests the browser to
+[join the interest group](https://github.com/privacysandbox/privacy-sandbox-demos/blob/939dd4928ec9cb4628b3f9424081bbd912346bcf/services/dsp/src/public/js/join-ad-interest-group.js#L38)
#### How do we serve an ad relevant to the user’s interest ? (see step #4 of User Journey)
-The news page [includes ad-tag.js ](https://github.com/privacysandbox/privacy-sandbox-demos/blob/8a33afb7433ed70e639047316c5bff30d61be58b/services/news/src/views/index.ejs#L29)from the SSP service. This is a third-party tag from the SSP service.
+The news page
+[includes ad-tag.js](https://github.com/privacysandbox/privacy-sandbox-demos/blob/8a33afb7433ed70e639047316c5bff30d61be58b/services/news/src/views/index.ejs#L29)from
+the SSP service. This is a third-party tag from the SSP service.
```html
@@ -209,7 +227,9 @@ This [ssp-tags.js](https://github.com/privacysandbox/privacy-sandbox-demos/blob/
>
```
-The iframe calls a third-party script [run-ad-auction.js](https://github.com/privacysandbox/privacy-sandbox-demos/blob/main/services/ssp/src/public/js/run-ad-auction.js) to run an ondevice ad auction using Protected Audience API
+The iframe calls a third-party script
+[run-ad-auction.js](https://github.com/privacysandbox/privacy-sandbox-demos/blob/main/services/ssp/src/public/js/run-ad-auction.js) to run an ondevice
+ad auction using Protected Audience API
```js title=”https://github.com/privacysandbox/privacy-sandbox-demos/blob/main/services/ssp/src/public/js/run-ad-auction.js”
document.addEventListener("DOMContentLoaded", async (e) => {
@@ -229,8 +249,10 @@ document.addEventListener("DOMContentLoaded", async (e) => {
})
```
-The `runAdAuction` code is executed by the browser and will decide which ad will be served to the user.
-The result of the auction is displayed within a Fenced Frame by specifying the adAuctionResult object to the fenced frame configuration. Developers would traditionally use https urls to load creative in an iframe, however Protected Audience API is hiding the creative url from the parent page. This is a privacy protection mechanism to not reveal the user's interest to the parent page.
+The `runAdAuction` code is executed by the browser and will decide which ad will be served to the user. The result of the auction is displayed within
+a Fenced Frame by specifying the adAuctionResult object to the fenced frame configuration. Developers would traditionally use https urls to load
+creative in an iframe, however Protected Audience API is hiding the creative url from the parent page. This is a privacy protection mechanism to not
+reveal the user's interest to the parent page.
```html
@@ -240,9 +262,12 @@ The result of the auction is displayed within a Fenced Frame by specifying the a
```
-note that Fenced Frame attribute `mode` must be set to “[opaque-ads](https://github.com/WICG/fenced-frame/blob/master/explainer/use_cases.md#opaque-ads)” to make the url opaque to the embedding context.
-Fenced Frame size (width and height) only allow pre-defined values, please refer to the allow-list from the documentation.
-The request to the `renderURL`from the winning Interest Group [returns the ad creative](https://github.com/privacysandbox/privacy-sandbox-demos/blob/9f8578e6d99da4dd52007843a283c97885c07146/services/dsp/src/index.ts#L57) to be displayed
+note that Fenced Frame attribute `mode` must be set to
+“[opaque-ads](https://github.com/WICG/fenced-frame/blob/master/explainer/use_cases.md#opaque-ads)” to make the url opaque to the embedding context.
+Fenced Frame size (width and height) only allow pre-defined values, please refer to the allow-list from the documentation. The request to the
+`renderURL`from the winning Interest Group
+[returns the ad creative](https://github.com/privacysandbox/privacy-sandbox-demos/blob/9f8578e6d99da4dd52007843a283c97885c07146/services/dsp/src/index.ts#L57)
+to be displayed
```html
@@ -266,7 +291,8 @@ The request to the `renderURL`from the winning Interest Group [returns the ad cr
```
-This code contains the `img` tag with `src` attribute specifying the product the user might be interested in, and a link “href” pointing to the advertiser page.
+This code contains the `img` tag with `src` attribute specifying the product the user might be interested in, and a link “href” pointing to the
+advertiser page.
### Related API documentation
diff --git a/services/home/docs/demos/single-touch-conversion-attribution.md b/services/home/docs/demos/single-touch-conversion-attribution.md
index 06fecbfd..c0b07cd7 100644
--- a/services/home/docs/demos/single-touch-conversion-attribution.md
+++ b/services/home/docs/demos/single-touch-conversion-attribution.md
@@ -11,8 +11,7 @@ more_data:
- DSP
---
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
+import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
# Single-touch conversion Attribution
@@ -23,8 +22,10 @@ import TabItem from '@theme/TabItem';
### Description
-This type of measurement tracks conversions that occur directly as a result of seeing an ad, such as when a user clicks on an ad and then makes a purchase on the advertiser's website.
-Single-touch attribution models are easy to understand and implement, and they can be a good option for businesses with limited data or resources. However, they can also be inaccurate, as they do not account for the influence of other touch points on a conversion.
+This type of measurement tracks conversions that occur directly as a result of seeing an ad, such as when a user clicks on an ad and then makes a
+purchase on the advertiser's website. Single-touch attribution models are easy to understand and implement, and they can be a good option for
+businesses with limited data or resources. However, they can also be inaccurate, as they do not account for the influence of other touch points on a
+conversion.
### Privacy Sandbox APIs
@@ -44,23 +45,32 @@ Single-touch attribution models are easy to understand and implement, and they c
### Goals
-In this demo, we assume an advertiser would like to measure the effectiveness of marketing campaigns. Single-touch attribution can help by tracking the number of conversions as well as the purchased value that can be attributed to each touch point (viewing or clicking an ad). This information can be used to improve the advertiser’s marketing campaigns and get more value from their marketing budget.
+In this demo, we assume an advertiser would like to measure the effectiveness of marketing campaigns. Single-touch attribution can help by tracking
+the number of conversions as well as the purchased value that can be attributed to each touch point (viewing or clicking an ad). This information can
+be used to improve the advertiser’s marketing campaigns and get more value from their marketing budget.
### Assumptions
-This use case assumes the advertiser (e.g. EC site) has contracted with a publisher or won a bid to display their product ads on the publisher site (e.g. News site). This use case does not cover ad-targeting specifics, so we assume the user would be presented with a relevant ad leading to a conversion (purchase of a product).
+This use case assumes the advertiser (e.g. EC site) has contracted with a publisher or won a bid to display their product ads on the publisher site
+(e.g. News site). This use case does not cover ad-targeting specifics, so we assume the user would be presented with a relevant ad leading to a
+conversion (purchase of a product).
### Key Exclusions
-The demo does not show Aggregation Service and noised aggregation reports. The current demo shows the aggregatable reports (debug report non-encrypted) before it is sent to the aggregation service (encrypted).
+The demo does not show Aggregation Service and noised aggregation reports. The current demo shows the aggregatable reports (debug report
+non-encrypted) before it is sent to the aggregation service (encrypted).
### System Design
-The user visits a news site where an ad is rendered. Using Attribution Reporting API & Summary Reports, a view-through conversion source event is registered. The user clicks on the ads, a click-through conversion source is registered. Then the user navigates to the advertiser page (shopping site), clicks “Add to cart” then “Checkout”. The browser registers a conversion trigger event and a summary report (debug report) is sent to the advertiser.
+The user visits a news site where an ad is rendered. Using Attribution Reporting API & Summary Reports, a view-through conversion source event is
+registered. The user clicks on the ads, a click-through conversion source is registered. Then the user navigates to the advertiser page (shopping
+site), clicks “Add to cart” then “Checkout”. The browser registers a conversion trigger event and a summary report (debug report) is sent to the
+advertiser.
#### Attribution Reporting Flow
-Below is a general introduction of Single-Touch conversion Attribution using Privacy Sandbox Attribution Reporting API. For further information see [Attribution Reporting - Chrome Developers](https://developer.chrome.com/docs/privacy-sandbox/attribution-reporting/) .
+Below is a general introduction of Single-Touch conversion Attribution using Privacy Sandbox Attribution Reporting API. For further information see
+[Attribution Reporting - Chrome Developers](https://developer.chrome.com/docs/privacy-sandbox/attribution-reporting/) .
![Protected Audience Flow](./img/attribution-reporting-flow.png)
@@ -127,44 +137,58 @@ Note over DSP:Scenario 1 stops here where we visualize debug reports
1. [Navigate to shop site](https://privacy-sandbox-demos-shop.dev/) (advertiser)
2. Click on a “shoe” product item on the shop site.
-- The shop (advertiser) would assume the user is interested in this type of product, so they would leverage Protected Audience API and ask the browser to join an ad interest group for this product or this specific product category.
+- The shop (advertiser) would assume the user is interested in this type of product, so they would leverage Protected Audience API and ask the browser
+ to join an ad interest group for this product or this specific product category.
3. [Navigate to the news site](https://privacy-sandbox-demos-news.dev/) (publisher)
4. Observe the ad served on the news site
- If you previously browsed the “shoe” product on the shop site, you will be shown an ad for the same product.
-- Displaying the ad will also register an attribution **source** of type **event** into your browser using the **Attribution Reporting API** (used for view-through conversion measurement)
+- Displaying the ad will also register an attribution **source** of type **event** into your browser using the **Attribution Reporting API** (used for
+ view-through conversion measurement)
5. Click on the ad served on the news site
- your browser will open a new window with the product page
-- Clicking the ad will also register an attribution **source** of type **navigation** into your browser using the **Attribution Reporting API** (used for click-through conversion measurement)
+- Clicking the ad will also register an attribution **source** of type **navigation** into your browser using the **Attribution Reporting API** (used
+ for click-through conversion measurement)
6. Navigate to chrome://attribution-internals/ and click the `Active Sources` tab
-- At the bottom of the page, you will see 2 **sources** with the status `Attributable`, the `source origin` is the **news** site, the `destination` is the **shop** site and the `reporting origin` is the **DSP** service. One of the `source type` is **event** (for view-through) and the other one is **navigation** (for click-through) This reference will be used later to attribute (match) the conversion (here the. purchase of an item on the **shop** site) to a previous event (here. The user saw/clicked an ad on the **news** site)
+- At the bottom of the page, you will see 2 **sources** with the status `Attributable`, the `source origin` is the **news** site, the `destination` is
+ the **shop** site and the `reporting origin` is the **DSP** service. One of the `source type` is **event** (for view-through) and the other one is
+ **navigation** (for click-through) This reference will be used later to attribute (match) the conversion (here the. purchase of an item on the
+ **shop** site) to a previous event (here. The user saw/clicked an ad on the **news** site)
7. On the product page, click “Add to cart”
8. On the cart page, click “Checkout”
- In this scenario the “checkout” event is the conversion event the advertiser wants to measure to evaluate the performance of their ad campaign,
-- The checkout page **triggers** the conversion attribution for the `source` whose eTLD+1 matches the eTLD+1 of the site provided in `destination` (here privacy-sandbox-demos-shop.dev) . The **Attribution Reporting API** logic will then process the event.
+- The checkout page **triggers** the conversion attribution for the `source` whose eTLD+1 matches the eTLD+1 of the site provided in `destination`
+ (here privacy-sandbox-demos-shop.dev) . The **Attribution Reporting API** logic will then process the event.
9. Navigate to chrome://attribution-internals/ and click the `Trigger Registration` tab
-- At the bottom of the page, you will see 1 **trigger** . the `destination` is the **shop** site and the `reporting origin` is the **DSP** service. The `Registration JSON` contains information about the conversion event. In this scenario the advertiser chose to report the gross price and the quantity of the product item purchased. the `Aggregatable Status` indicates **Success: Report stored**, it means Attribution Reporting API has now stored this report in the browser. It will then be scheduled for sending to the `reporting origin` at a later time.
+- At the bottom of the page, you will see 1 **trigger** . the `destination` is the **shop** site and the `reporting origin` is the **DSP** service.
+ The `Registration JSON` contains information about the conversion event. In this scenario the advertiser chose to report the gross price and the
+ quantity of the product item purchased. the `Aggregatable Status` indicates **Success: Report stored**, it means Attribution Reporting API has now
+ stored this report in the browser. It will then be scheduled for sending to the `reporting origin` at a later time.
10. Navigate to [DSP service report visualization page](https://privacy-sandbox-demos-dsp.dev/reports)
-- on this page you can see the aggregatable report sent by the browser to the DSP. In a production environment, the aggregatable report is encrypted by the browser and sent to the DSP. There, they will be batched and sent to the Aggregation Service where they will be aggregated and noised to preserve privacy. However for development and testing purposes, you can also send an unencrypted version called **debug report**. This is what you are seeing now.
+- on this page you can see the aggregatable report sent by the browser to the DSP. In a production environment, the aggregatable report is encrypted
+ by the browser and sent to the DSP. There, they will be batched and sent to the Aggregation Service where they will be aggregated and noised to
+ preserve privacy. However for development and testing purposes, you can also send an unencrypted version called **debug report**. This is what you
+ are seeing now.
- The report shows aggregation data on 2 dimensions : gross with a value of 180 and quantity with a value of 1.
### Implementation details
#### how do we attribute the conversion to seeing an ad ? (see step #5 of User Journey)
-First on the Attribution Source registration side.
-Look at the [code](https://github.com/privacysandbox/privacy-sandbox-demos/blob/cd28aba4e85b641d50d6ee999019d25607c439fc/services/ssp/src/views/ads.html.ejs#L29) displaying the ad creative
+First on the Attribution Source registration side. Look at the
+[code](https://github.com/privacysandbox/privacy-sandbox-demos/blob/cd28aba4e85b641d50d6ee999019d25607c439fc/services/ssp/src/views/ads.html.ejs#L29)
+displaying the ad creative
```html
@@ -188,9 +212,10 @@ Look at the [code](https://github.com/privacysandbox/privacy-sandbox-demos/blob/
```
-The `img` tag also specifies the `attributionsrc` attribute. It means that showing this ad will register an attribution source of type `event` in the browser.
-Now using Developers Tools, look at the HTTP request you will see a new attribute added by the browser `Attribution-Reporting-Eligible` with the value `event-source, trigger`
-In the HTTP response to the `/register-source` request, you will see a new header `Attribution-Reporting-Register-Source:` with a value that contains the attribution source parameters.
+The `img` tag also specifies the `attributionsrc` attribute. It means that showing this ad will register an attribution source of type `event` in the
+browser. Now using Developers Tools, look at the HTTP request you will see a new attribute added by the browser `Attribution-Reporting-Eligible` with
+the value `event-source, trigger` In the HTTP response to the `/register-source` request, you will see a new header
+`Attribution-Reporting-Register-Source:` with a value that contains the attribution source parameters.
```json
{
@@ -201,10 +226,11 @@ In the HTTP response to the `/register-source` request, you will see a new heade
}
```
-You can also refer to the [source code](https://github.com/privacysandbox/privacy-sandbox-demos/blob/cd28aba4e85b641d50d6ee999019d25607c439fc/services/ssp/src/index.js#L113) to see how the response header `Attribution-Reporting-Register-Source` was formed.
+You can also refer to the
+[source code](https://github.com/privacysandbox/privacy-sandbox-demos/blob/cd28aba4e85b641d50d6ee999019d25607c439fc/services/ssp/src/index.js#L113) to
+see how the response header `Attribution-Reporting-Register-Source` was formed.
-Second, on the Attribution Trigger side (=Conversion)
-The checkout page contains a 1 pixel image loaded from the code
+Second, on the Attribution Trigger side (=Conversion) The checkout page contains a 1 pixel image loaded from the code
```html
```
-Now using the Developers Tools, look at the HTTP response to the `/register-trigger` request. You will see a new header `Attribution-Reporting-Register-Trigger:` with a value that contains the attribution trigger parameters, including the values the advertiser would like to see aggregated in the summary report (in this example gross and quantities)
+Now using the Developers Tools, look at the HTTP response to the `/register-trigger` request. You will see a new header
+`Attribution-Reporting-Register-Trigger:` with a value that contains the attribution trigger parameters, including the values the advertiser would
+like to see aggregated in the summary report (in this example gross and quantities)
```json
{
@@ -228,7 +256,9 @@ Now using the Developers Tools, look at the HTTP response to the `/register-trig
}
```
-You can also refer to the [source code](https://github.com/privacysandbox/privacy-sandbox-demos/blob/cd28aba4e85b641d50d6ee999019d25607c439fc/services/ssp/src/index.js#L205) to see how the response header `aggregatable_trigger_data` was formed.
+You can also refer to the
+[source code](https://github.com/privacysandbox/privacy-sandbox-demos/blob/cd28aba4e85b641d50d6ee999019d25607c439fc/services/ssp/src/index.js#L205) to
+see how the response header `aggregatable_trigger_data` was formed.
### Related API documentation
diff --git a/services/home/docs/demos/vast-video-protected-audience.md b/services/home/docs/demos/vast-video-protected-audience.md
index 4f7920ea..403233c5 100644
--- a/services/home/docs/demos/vast-video-protected-audience.md
+++ b/services/home/docs/demos/vast-video-protected-audience.md
@@ -11,8 +11,7 @@ more_data:
- DSP
---
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
+import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
# VAST Video Protected Audience
@@ -23,7 +22,10 @@ import TabItem from '@theme/TabItem';
### Description
-VAST (Video Ad Serving Template) is a template for structuring ad tags that serve video and audio ads to media players. Using an XML schema, VAST transfers important metadata about an ad from the ad server to a media player. In the context of Protected Audience API the VAST XML response is provided within the iFrame for a winning DSP. The following demo provides details on a temporary solution allowing Top Level embedded video players access to the VAST XML URL.
+VAST (Video Ad Serving Template) is a template for structuring ad tags that serve video and audio ads to media players. Using an XML schema, VAST
+transfers important metadata about an ad from the ad server to a media player. In the context of Protected Audience API the VAST XML response is
+provided within the iFrame for a winning DSP. The following demo provides details on a temporary solution allowing Top Level embedded video players
+access to the VAST XML URL.
### Privacy Sandbox APIs
@@ -41,19 +43,24 @@ VAST (Video Ad Serving Template) is a template for structuring ad tags that serv
### Goals
-In this demo, we assume an advertiser would like to display VAST XML URL’s from the winning auction and provide that back to the Top Level embedded video player resident on the publisher's website. This workaround facilitates this until future fenced frame requirements are enforced.
+In this demo, we assume an advertiser would like to display VAST XML URL’s from the winning auction and provide that back to the Top Level embedded
+video player resident on the publisher's website. This workaround facilitates this until future fenced frame requirements are enforced.
### Assumptions
-This use case assumes the publisher (news site) has one of the many standard VAST XML compliant video players embedded on their site at the top level and also has an SSP supplying advertisements in the VAST XML format back to said player.
+This use case assumes the publisher (news site) has one of the many standard VAST XML compliant video players embedded on their site at the top level
+and also has an SSP supplying advertisements in the VAST XML format back to said player.
### Key Exclusions
-The demo does not integrate existing auction mechanisms (prebid or header bidding…). it is only scoped to on-device auction with Protected Audience API. The ad selection is very straightforward (only 1 bidder).
+The demo does not integrate existing auction mechanisms (prebid or header bidding…). it is only scoped to on-device auction with Protected Audience
+API. The ad selection is very straightforward (only 1 bidder).
### System Design
-Using Protected Audience API, the user visits a shopping site, and gets added to an interest group. Later the same user visits a news site. There the browser runs an on-device Auction, bidding logic will select the winning interest group, and relevant VAST XML video ads will be dynamically rendered on the publisher page.
+Using Protected Audience API, the user visits a shopping site, and gets added to an interest group. Later the same user visits a news site. There the
+browser runs an on-device Auction, bidding logic will select the winning interest group, and relevant VAST XML video ads will be dynamically rendered
+on the publisher page.
#### Protected Audience Flow
@@ -123,7 +130,8 @@ deactivate iframe
1. [Navigate to shop site](https://privacy-sandbox-demos-shop.dev/) (advertiser)
2. Click on a “shoe” product item on the shop site. Append ?adType=video to the product page url and refresh the page.
- - The shop (advertiser) would assume the user is interested in this type of product, so they would leverage Protected Audience API and ask the browser to join an ad interest group for this product or this specific product category.
+ - The shop (advertiser) would assume the user is interested in this type of product, so they would leverage Protected Audience API and ask the
+ browser to join an ad interest group for this product or this specific product category.
3. [Navigate to the news site video page](https://privacy-sandbox-demos-news.dev/video-ad) (publisher)
4. Scroll to the embedded video player and click ‘Play Content’. Observe the video ad pre-roll served on the news sites embedded player.
- When the page was loaded, Protected Audience API allowed the SSP to run an ad auction on the publisher site.
@@ -133,7 +141,9 @@ deactivate iframe
#### How is the user added to an Interest Group with video ads based on his browsing behavior? (see step #2 of User Journey)
-The shop product page [includes dsp-tag.js ](https://github.com/privacysandbox/privacy-sandbox-demos/blob/939dd4928ec9cb4628b3f9424081bbd912346bcf/services/shop/src/index.ts#L93) from the DSP service. This is a third-party tag from the DSP service.
+The shop product page
+[includes dsp-tag.js](https://github.com/privacysandbox/privacy-sandbox-demos/blob/939dd4928ec9cb4628b3f9424081bbd912346bcf/services/shop/src/index.ts#L93)
+from the DSP service. This is a third-party tag from the DSP service.
```html
```
-The [dsp-tags.js](https://github.com/privacysandbox/privacy-sandbox-demos/blob/939dd4928ec9cb4628b3f9424081bbd912346bcf/services/dsp/src/public/dsp-tag.js#L17) dynamically embeds an iframe
+The
+[dsp-tags.js](https://github.com/privacysandbox/privacy-sandbox-demos/blob/939dd4928ec9cb4628b3f9424081bbd912346bcf/services/dsp/src/public/dsp-tag.js#L17)
+dynamically embeds an iframe
```html