Skip to content
This repository has been archived by the owner on Apr 27, 2023. It is now read-only.

Commit

Permalink
Merge branch 'release-v0.9.0' into release
Browse files Browse the repository at this point in the history
  • Loading branch information
rhelmer committed Feb 10, 2021
2 parents c1026e0 + 7b1880e commit 703a06e
Show file tree
Hide file tree
Showing 112 changed files with 4,437 additions and 3,258 deletions.
5 changes: 3 additions & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ module.exports = {
ChromeUtils: false,
ExtensionAPI: false,
// NOTE: These get injected via Rollup.
__ION_STUDIES_LIST__: false,
__ION_WEBSITE_URL__: false,
__STUDIES_LIST__: false,
__DISABLE_REMOTE_SETTINGS__: false,
__DISABLE_LOCALE_CHECK__: false,
__ENABLE_DATA_SUBMISSION__: false,
__WEBSITE_URL__: false,
},
overrides: [
{
Expand Down
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
18 changes: 17 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
# Unreleased changes

[Full changelog](https://github.com/mozilla-rally/core-addon/compare/v0.8.0...master)
[Full changelog](https://github.com/mozilla-rally/core-addon/compare/v0.9.0...master)

# v0.9.0 (2021-02-09)

[Full changelog](https://github.com/mozilla-rally/core-addon/compare/v0.8.0...v0.9.0)

* [#318](https://github.com/mozilla-rally/rally-core-addon/pull/311): Use Remote Settings for fetching study metadata.
* [#328](https://github.com/mozilla-rally/rally-core-addon/pull/328): Update demographic survey to use "Latinx"; list answers for race question 4 alphabetically.
* [#334](https://github.com/mozilla-rally/rally-core-addon/pull/334): Make Mocha stricter about unhandled exceptions in tests; fix the enable data submission option.
* [341](https://github.com/mozilla-rally/rally-core-addon/pull/341): Update options page favicon, toolbar icon, and addon image to use the new Rally branding.
* [#344](https://github.com/mozilla-rally/rally-core-addon/pull/344): Create splash page for non-US users in the Core Add-On.
* [#286](https://github.com/mozilla-rally/core-addon/pull/286): Adds a "clear this response" button to demographic survey questions.
* [#349](https://github.com/mozilla-rally/core-addon/pull/349): Adds a "manage profile" page, where a user can update their demographic survey answers.
* [#350](https://github.com/mozilla-rally/rally-core-addon/pull/350): Simplify study schema for the remote settings, and make code consistent with new names.
* [#361](https://github.com/mozilla-rally/rally-core-addon/pull/361): Implements a new study card design.
* [#362](https://github.com/mozilla-rally/rally-core-addon/pull/362): Adds redirect to "sorry to see you go" page when user uninstalls Rally.
* [#363](https://github.com/mozilla-rally/rally-core-addon/pull/363): Adds an arrow flourish to the first run welcome page.

# v0.8.0 (2021-02-01)

Expand Down
36 changes: 34 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This is the Core Add-on for the Rally platform. It is implemented as a cross-bro

Rally studies are implemented as Add-ons. A [study template](https://github.com/mozilla-rally/study-template) is provided to help study authors start writing their study. A [demo website landing page](https://mozilla-rally.github.io/core-addon) is also available for testing the Core Add-on.

The "source of truth" for Rally study metadata is on the [Firefox remote settings server](https://firefox.settings.services.mozilla.com/v1/buckets/main/collections/pioneer-study-addons-v1/records).
The "source of truth" for Rally study metadata is on the [Firefox remote settings server](https://firefox.settings.services.mozilla.com/v1/buckets/main/collections/rally-study-addons-v1/records).

## Get started

Expand Down Expand Up @@ -71,7 +71,7 @@ The output will be a `.zip` file in `web-ext-artifacts/` - this should be rename

## Building and testing a study locally

To run an end-to-end local test with your own study add-on, first build your study (if you don't have one, you can [build the Rally Study Template](https://github.com/mozilla-rally/study-template)) and export the signed build as `<name-of-study>.xpi`. Edit `/public/locally-available-studies.json` so that `sourceURI.spec` is `/public/<name-of-study>.xpi` (you can change the other fields in `/public/locally-available-studies.json` as well for demo purposes as needed).
To run an end-to-end local test with your own study add-on, first build your study (if you don't have one, you can [build the Rally Study Template](https://github.com/mozilla-rally/study-template)) and export the signed build as `<name-of-study>.xpi`. Edit `/public/locally-available-studies.json` so that `downloadLink` is `/public/<name-of-study>.xpi` (you can change the other fields in `/public/locally-available-studies.json` as well for demo purposes as needed).

Then run:

Expand All @@ -87,6 +87,38 @@ To test the QA-signed extension in Firefox, you must be running the Nightly rele

> **Note:** this will cause production-signed extensions (such as those from addons.mozilla.org) to not load. To allow these, set `xpinstall.signatures.required` pref to `false`.
## Testing Remote Settings changes

The Rally core-addon uses the Firefox [remote settings server](https://remote-settings.readthedocs.io/en/latest/) to publish metadata about current studies.
Changes may be tested locally, or on the dev and staging servers, before pushing live.

> **Note:** `npm run build-local-addon` will produce a build that uses `public/locally-available-studies.json` instead of fetching from Remote Settings. If you are doing local development you most likely want this option.
For developer convenience, a [public remote-settings dev server](https://remote-settings.readthedocs.io/en/latest/tutorial-dev-server.html) is available. Making
changes to staging or production require multiple sign-offs from Mozilla.

You may also set up your own [local remote settings server](https://remote-settings.readthedocs.io/en/latest/tutorial-local-server.html).

> **Note:** Firefox Release cannot be reconfigured to use a different server, an unbranded build such as Firefox Nightly must be used for testing.
1. In `about:config` on Firefox Nightly, change `services.settings.server` to the URL for your server. If you're using the public remote-settings dev server
described above, then the value will be `https://kinto.dev.mozaws.net/v1`.
2. In the Firefox Browser console, first import the `RemoteSettings` module:
```js
const { RemoteSettings } = ChromeUtils.import(
"resource://services-settings/remote-settings.js"
);
```
3. Then, disable signature checking and fetch the latest `rally-studies-v1` collection:
```js
RemoteSettings("rally-studies-v1").verifySignature = false;
await RemoteSettings("rally-studies-v1").get();
```
4. The UI for the core add-on options page should respond immediately. After making changes to the RS server, you can either wait or explicitly poll for updates:
```js
await RemoteSettings.pollChanges()
```

## Developing new frontend components

The Core Add-On uses Storybook to assist in isolated component work. If you're building a new component, look at the examples in `/stories`. To run the storybook, run `npm run storybook`.
118 changes: 69 additions & 49 deletions core-addon/Core.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,43 @@ const DataCollection = require("./DataCollection.js");
// The path of the embedded resource used to control options.
const OPTIONS_PAGE_PATH = "public/index.html";

const DEFAULT_ARGS = {
// NOTE: if this URL ever changes, you will have to update the domain in
// the permissions in manifest.json.
availableStudiesURI: "https://firefox.settings.services.mozilla.com/v1/buckets/main/collections/pioneer-study-addons-v1/records",
website: "https://mozilla-rally.github.io",
}

module.exports = class Core {
/**
* @param {Object} args arguments passed in from the user.
* @param {String} args.availableStudiesURI the URI where the available studies
* information is listed.
* @param {String} args.website the URL of the platform website.
*/
constructor(args = {}) {
this._userArguments = {...DEFAULT_ARGS, ...args};
/**
* @param {Object} args arguments passed in from the user.
* @param {String} args.availableStudiesURI the URI where the available studies
* information is listed. Only used when disableRemoteSettings is `true`.
* @param {Boolean} args.disableRemoteSettings do not use the official RemoteSettings server.
* Default is `true`.
* @param {String} args.website the URL of the platform website.
*/
constructor(args) {
this._userArguments = args;

this._storage = new Storage();
this._dataCollection = new DataCollection();

// Asynchronously get the available studies. We don't need to wait
// for this to finish, the UI can handle the wait.
this._availableStudies =
this._fetchAvailableStudies()
.then(studies => this._updateInstalledStudies(studies));

this._availableStudies = this._fetchAvailableStudies().then((studies) =>
this._updateInstalledStudies(studies)
);
if (this._userArguments.disableRemoteSettings) {
console.warn("RemoteSettings disabled, not adding a new studies listener.");
} else {
// Register a listener to react to remote-settings updates.
browser.firefoxPrivilegedApi.onRemoteSettingsSync.addListener((studies) => {
// FIXME Important: this may be racing with the initial update or if there's two consecutive updates?
// @see https://github.com/mozilla-rally/rally-core-addon/issues/318
this._availableStudies = this._updateInstalledStudies(studies);
this._sendStateUpdateToUI();
});
}
this._connectionPort = null;
}

initialize() {
// set the URL to redirect when a user uninstalls Rally
browser.runtime.setUninstallURL("__BASE_SITE__/leaving-rally");
// Whenever the addon icon is clicked, open the control page.
browser.browserAction.onClicked.addListener(this._openControlPanel);
// After installing the addon, make sure to show the control page.
Expand Down Expand Up @@ -108,7 +115,7 @@ module.exports = class Core {
// Don't do anything if we received an updated from an addon
// that's not a study.
let knownStudies = await this._availableStudies;
if (!knownStudies.map(s => s.addon_id).includes(info.id)) {
if (!knownStudies.map(s => s.addonId).includes(info.id)) {
console.debug(
`Core._handleAddonLifecycle - non-study addon ${info.id} was ${installed ? "installed" : "uninstalled"}`
);
Expand All @@ -118,11 +125,11 @@ module.exports = class Core {
// Update the available studies list with the installation
// information.
this._availableStudies = Promise.resolve(knownStudies.map(s => {
if (s.addon_id == info.id) {
s.studyInstalled = installed;
}
return s;
})
if (s.addonId == info.id) {
s.studyInstalled = installed;
}
return s;
})
);

if (installed) {
Expand Down Expand Up @@ -179,7 +186,7 @@ module.exports = class Core {
switch (message.type) {
case "enrollment":
return this._enroll()
.then(r => this._sendStateUpdateToUI());
.then(r => this._sendStateUpdateToUI());
case "get-studies":
return this._sendStateUpdateToUI();
case "study-unenrollment":
Expand All @@ -190,7 +197,11 @@ module.exports = class Core {
case "unenrollment":
return this._unenroll();
case "update-demographics":
return this._updateDemographics(message.data);
return this._updateDemographics(message.data)
.then(r => this._sendStateUpdateToUI());
case "first-run-completion":
return this._storage.setFirstRunCompletion(message.data.firstRunCompleted)
.then(() => this._sendStateUpdateToUI());
default:
return Promise.reject(
new Error(`Core - unexpected message type ${message.type}`));
Expand Down Expand Up @@ -218,7 +229,7 @@ module.exports = class Core {
// We only expect messages coming from known installed studies.
let installedStudies = (await this._availableStudies)
.filter(s => s.studyInstalled)
.map(s => s.addon_id);
.map(s => s.addonId);
if (!installedStudies.includes(sender.id)) {
throw new Error(`Core._handleExternalMessage - unexpected sender ${sender.id}`);
}
Expand All @@ -234,7 +245,7 @@ module.exports = class Core {
};
}
case "telemetry-ping": {
const {payloadType, payload, namespace, keyId, key} = message.data;
const { payloadType, payload, namespace, keyId, key } = message.data;
let rallyId = await this._storage.getRallyID();
return await this._dataCollection.sendPing(
rallyId, payloadType, payload, namespace, keyId, key
Expand Down Expand Up @@ -333,7 +344,7 @@ module.exports = class Core {
async _enrollStudy(studyAddonId) {
// We only expect to enroll in known studies.
let knownStudies = await this._availableStudies;
if (!knownStudies.map(s => s.addon_id).includes(studyAddonId)) {
if (!knownStudies.map(s => s.addonId).includes(studyAddonId)) {
return Promise.reject(
new Error(`Core._enrollStudy - Unknown study ${studyAddonId}`));
}
Expand All @@ -359,7 +370,7 @@ module.exports = class Core {
async _unenrollStudy(studyAddonId) {
// We only expect to unenroll in known studies.
let knownStudies = await this._availableStudies;
if (!knownStudies.map(s => s.addon_id).includes(studyAddonId)) {
if (!knownStudies.map(s => s.addonId).includes(studyAddonId)) {
return Promise.reject(
new Error(`Core._unenrollStudy - Unknown study ${studyAddonId}`));
}
Expand Down Expand Up @@ -393,7 +404,7 @@ module.exports = class Core {
// Uninstall all known studies that are still installed.
let installedStudies = (await this._availableStudies)
.filter(s => s.studyInstalled)
.map(s => s.addon_id);
.map(s => s.addonId);
for (let studyId of installedStudies) {
// Attempt to send an uninstall message to each study, but
// move on if the delivery fails: studies will not be able
Expand Down Expand Up @@ -422,7 +433,7 @@ module.exports = class Core {
await this._storage.clearActivatedStudies();

// Finally, uninstall the addon.
await browser.management.uninstallSelf({showConfirmDialog: false});
await browser.management.uninstallSelf({ showConfirmDialog: false });
}

/**
Expand Down Expand Up @@ -470,20 +481,20 @@ module.exports = class Core {
*
* @returns {Promise(Array<Object>)} resolved with an array of studies
* objects, or an empty array on failures. Each study object
* has at least the `addon_id` and `studyInstalled` properties.
* has at least the `addonId` and `studyInstalled` properties.
*/
async _updateInstalledStudies(studies) {
console.debug("Core._updateInstalledStudies");
console.debug("Core._updateInstalledStudies:", studies);

// If we were able to fetch studies definitions, see if any
// of them were installed. Start by getting the list of installed
// addons.
let installedAddonsIds =
await browser.management.getAll().then(addons =>
addons.filter(a => a.type == "extension")
.map(a => a.id));
.map(a => a.id));
return studies.map(s => {
s.studyInstalled = installedAddonsIds.includes(s.addon_id);
s.studyInstalled = installedAddonsIds.includes(s.addonId);
return s;
});
}
Expand All @@ -492,14 +503,23 @@ module.exports = class Core {
* Fetch the available studies.
*
* This loads the studies from the Firefox Remote Settings service.
* If `disableRemoteSettings` is `true`, then an URL will be used instead. This is intended for local testing.
*
* @returns {Promise(Array<Object>)} resolved with an array of studies
* objects, or an empty array on failures.
*/
async _fetchAvailableStudies() {
try {
const request = await fetch(this._userArguments.availableStudiesURI);
return (await request.json()).data;
let studies = [];
if (this._userArguments.disableRemoteSettings) {
console.warn("Not using RemoteSettings, fetching from:", this._userArguments.availableStudiesURI);
const request = await fetch(this._userArguments.availableStudiesURI);
studies = await request.json();
} else {
console.debug("Using RemoteSettings for studies.");
studies = await browser.firefoxPrivilegedApi.getRemoteSettings();
}
return studies;
} catch (err) {
console.error(err);
return [];
Expand All @@ -522,22 +542,18 @@ module.exports = class Core {
* {
* name: "Demo Study",
* icons: { ... },
* schema: ...,
* authors { ... },
* version: "1.0",
* addon_id: "[email protected]",
* addonId: "[email protected]",
* moreInfo: { ... },
* isDefault: false,
* sourceURI: { ... },
* studyType: "extension",
* studyEnded: false,
* downloadLink: "https://example.com",
* studyPaused: false,
* description: "Some nice description",
* privacyPolicy: { ... },
* privacyPolicyLink: "https://example.com",
* joinStudyConsent: "...",
* leaveStudyConsent: "...",
* dataCollectionDetails: [ ... ],
* id:"...",
* last_modified: ...,
* // Whether or not the study is currently installed.
* studyInstalled: false
* },
Expand All @@ -547,16 +563,20 @@ module.exports = class Core {
*/
async _sendStateUpdateToUI() {
let enrolled = !!(await this._storage.getRallyID());
let firstRunCompleted = !!(await this._storage.getFirstRunCompletion());
let availableStudies = await this._availableStudies;
let demographicsData = await this._storage.getDemographicsData();

const newState = {
enrolled,
firstRunCompleted,
availableStudies,
demographicsData,
};

// Send a message to the UI to update the list of studies.
this._connectionPort.postMessage(
{type: "update-state", data: newState});
{ type: "update-state", data: newState });
}

/**
Expand All @@ -570,7 +590,7 @@ module.exports = class Core {
* information submitted by the user.
*/
async _updateDemographics(data) {
await this._storage.setItem("demographicsData", data)
await this._storage.setDemographicsData(data)
.catch(e => console.error(`Core._updateDemographics - failed to save data`, e));

let rallyId = await this._storage.getRallyID();
Expand Down
2 changes: 1 addition & 1 deletion core-addon/DataCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ module.exports = class DataCollection {
const FIELD_MAPPING = {
"age": "age",
"gender": "gender",
"hispanicLatinoSpanishOrigin": "origin",
"hispanicLatinxSpanishOrigin": "origin",
"school": "education",
"income": "income",
};
Expand Down
Loading

1 comment on commit 703a06e

@firefoxci-taskcluster
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uh oh! Looks like an error! Details

Taskcluster-GitHub attempted to create a task for this event with the following scopes:

["assume:repo:github.com/mozilla-rally/rally-core-addon:tag:v0.9.0","queue:route:checks","queue:scheduler-id:taskcluster-github"]

The expansion of these scopes is not sufficient to create the task, leading to the following:

Client ID static/taskcluster/github does not have sufficient scopes and is missing the following scopes:

assume:repo:github.com/mozilla-rally/rally-core-addon:branch:v0.9.0

This request requires the client to satisfy the following scope expression:

{
  "AllOf": [
    "assume:repo:github.com/mozilla-rally/rally-core-addon:branch:v0.9.0",
    "queue:route:checks",
    "queue:route:index.xpi.v2.rally-core-addon.revision.703a06e19a23da55c9dd7e702dec3efe67a80327.taskgraph.decision",
    "queue:scheduler-id:xpi-level-1",
    {
      "AnyOf": [
        "queue:create-task:highest:xpi-1/decision",
        "queue:create-task:very-high:xpi-1/decision",
        "queue:create-task:high:xpi-1/decision",
        "queue:create-task:medium:xpi-1/decision",
        "queue:create-task:low:xpi-1/decision",
        "queue:create-task:very-low:xpi-1/decision",
        "queue:create-task:lowest:xpi-1/decision"
      ]
    }
  ]
}

  • method: createTask
  • errorCode: InsufficientScopes
  • statusCode: 403
  • time: 2021-02-10T01:14:01.199Z

Please sign in to comment.