Skip to content

Commit fd50dd3

Browse files
feat(schemas): Add Firefox Labs fields to SDK experiment schemas (#15883)
This commit is a breaking change. Because: - we are bringing Firefox Labs to the SDK; and - our Pydantic models cannot distinguish between the firefoxLabsDescriptionLinks field being absent or null This commit: - adds most Firefox Labs fields (no firefoxLabsGroup) to the SDK experiment schemas; and - removes the hard requirement for the presence of the firefoxLabsDescriptionLinks field in order to align the validation behaviour between the Pydantic model and the generated JSON schema. Fixes #15865
1 parent def51cd commit fd50dd3

10 files changed

Lines changed: 595 additions & 152 deletions

File tree

schemas/index.d.ts

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -153,23 +153,15 @@ export interface DesktopAllVersionsNimbusExperiment {
153153
*/
154154
publishedDate?: string | null;
155155
/**
156-
* Branch configuration for the experiment.
157-
*/
158-
branches: DesktopAllVersionsExperimentBranch[];
159-
/**
160-
* When this property is set to true, treat this experiment as aFirefox Labs experiment
156+
* When this property is set to true, treat this experiment as a Firefox Labs experiment
161157
*/
162158
isFirefoxLabsOptIn?: boolean;
163159
/**
164-
* The group this should appear under in Firefox Labs
165-
*/
166-
firefoxLabsGroup?: string | null;
167-
/**
168-
* The title shown in Firefox Labs (Fluent ID)
160+
* The title shown in Firefox Labs (Fluent ID or Resource ID)
169161
*/
170162
firefoxLabsTitle?: string | null;
171163
/**
172-
* The description shown in Firefox Labs (Fluent ID)
164+
* The description shown in Firefox Labs (Fluent ID or Resource ID)
173165
*/
174166
firefoxLabsDescription?: string | null;
175167
/**
@@ -178,16 +170,24 @@ export interface DesktopAllVersionsNimbusExperiment {
178170
firefoxLabsDescriptionLinks?: {
179171
[k: string]: string;
180172
} | null;
181-
/**
182-
* Opt out of feature schema validation.
183-
*/
184-
featureValidationOptOut?: boolean;
185173
/**
186174
* Does the experiment require a restart to take effect?
187175
*
188176
* Only used by Firefox Labs Opt-Ins.
189177
*/
190178
requiresRestart?: boolean;
179+
/**
180+
* Branch configuration for the experiment.
181+
*/
182+
branches: DesktopAllVersionsExperimentBranch[];
183+
/**
184+
* Opt out of feature schema validation.
185+
*/
186+
featureValidationOptOut?: boolean;
187+
/**
188+
* The group this should appear under in Firefox Labs
189+
*/
190+
firefoxLabsGroup?: string | null;
191191
}
192192
export interface ExperimentBucketConfig {
193193
randomizationUnit: RandomizationUnit;
@@ -399,23 +399,15 @@ export interface DesktopNimbusExperiment {
399399
*/
400400
publishedDate?: string | null;
401401
/**
402-
* Branch configuration for the experiment.
403-
*/
404-
branches: DesktopExperimentBranch[];
405-
/**
406-
* When this property is set to true, treat this experiment as aFirefox Labs experiment
402+
* When this property is set to true, treat this experiment as a Firefox Labs experiment
407403
*/
408404
isFirefoxLabsOptIn?: boolean;
409405
/**
410-
* The group this should appear under in Firefox Labs
411-
*/
412-
firefoxLabsGroup?: string | null;
413-
/**
414-
* The title shown in Firefox Labs (Fluent ID)
406+
* The title shown in Firefox Labs (Fluent ID or Resource ID)
415407
*/
416408
firefoxLabsTitle?: string | null;
417409
/**
418-
* The description shown in Firefox Labs (Fluent ID)
410+
* The description shown in Firefox Labs (Fluent ID or Resource ID)
419411
*/
420412
firefoxLabsDescription?: string | null;
421413
/**
@@ -424,16 +416,24 @@ export interface DesktopNimbusExperiment {
424416
firefoxLabsDescriptionLinks?: {
425417
[k: string]: string;
426418
} | null;
427-
/**
428-
* Opt out of feature schema validation.
429-
*/
430-
featureValidationOptOut?: boolean;
431419
/**
432420
* Does the experiment require a restart to take effect?
433421
*
434422
* Only used by Firefox Labs Opt-Ins.
435423
*/
436424
requiresRestart?: boolean;
425+
/**
426+
* Branch configuration for the experiment.
427+
*/
428+
branches: DesktopExperimentBranch[];
429+
/**
430+
* Opt out of feature schema validation.
431+
*/
432+
featureValidationOptOut?: boolean;
433+
/**
434+
* The group this should appear under in Firefox Labs
435+
*/
436+
firefoxLabsGroup?: string | null;
437437
}
438438
/**
439439
* The branch definition supported on Firefox Desktop 95+.
@@ -577,6 +577,30 @@ export interface SdkNimbusExperiment {
577577
* If null, it has not yet been published.
578578
*/
579579
publishedDate?: string | null;
580+
/**
581+
* When this property is set to true, treat this experiment as a Firefox Labs experiment
582+
*/
583+
isFirefoxLabsOptIn?: boolean;
584+
/**
585+
* The title shown in Firefox Labs (Fluent ID or Resource ID)
586+
*/
587+
firefoxLabsTitle?: string | null;
588+
/**
589+
* The description shown in Firefox Labs (Fluent ID or Resource ID)
590+
*/
591+
firefoxLabsDescription?: string | null;
592+
/**
593+
* Links that will be used with the firefoxLabsDescription Fluent ID. May be null for Firefox Labs Opt-In recipes that do not use links.
594+
*/
595+
firefoxLabsDescriptionLinks?: {
596+
[k: string]: string;
597+
} | null;
598+
/**
599+
* Does the experiment require a restart to take effect?
600+
*
601+
* Only used by Firefox Labs Opt-Ins.
602+
*/
603+
requiresRestart?: boolean;
580604
/**
581605
* Branch configuration for the SDK experiment.
582606
*/

schemas/mozilla_nimbus_schemas/experimenter_apis/common.py

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
from enum import Enum
33
from typing import Any
44

5-
from pydantic import BaseModel, ConfigDict, Field, RootModel
5+
from pydantic import BaseModel, ConfigDict, Field, HttpUrl, RootModel, model_validator
66
from pydantic.json_schema import SkipJsonSchema
7+
from typing_extensions import Self
78

89

910
class RandomizationUnit(str, Enum):
@@ -26,9 +27,7 @@ class ExperimentBucketConfig(BaseModel):
2627
count: int = Field(description="Number of buckets in the range.")
2728
total: int = Field(
2829
description=(
29-
"The total number of buckets.\n"
30-
"\n"
31-
"You can assume this will always be 10000."
30+
"The total number of buckets.\n\nYou can assume this will always be 10000."
3231
)
3332
)
3433

@@ -225,4 +224,73 @@ class BaseExperiment(BaseModel):
225224
default=None,
226225
)
227226

228-
model_config = ConfigDict(use_enum_values=True)
227+
# Firefox Labs-related fields.
228+
229+
isFirefoxLabsOptIn: bool = Field(
230+
description=(
231+
"When this property is set to true, treat this experiment as a "
232+
"Firefox Labs experiment"
233+
),
234+
default=None,
235+
)
236+
firefoxLabsTitle: str | None = Field(
237+
description="The title shown in Firefox Labs (Fluent ID or Resource ID)",
238+
default=None,
239+
)
240+
firefoxLabsDescription: str | None = Field(
241+
description="The description shown in Firefox Labs (Fluent ID or Resource ID)",
242+
default=None,
243+
)
244+
firefoxLabsDescriptionLinks: dict[str, HttpUrl] | None = Field(
245+
description=(
246+
"Links that will be used with the firefoxLabsDescription Fluent ID. May be "
247+
"null for Firefox Labs Opt-In recipes that do not use links."
248+
),
249+
default=None,
250+
)
251+
requiresRestart: bool | SkipJsonSchema[None] = Field(
252+
description=(
253+
"Does the experiment require a restart to take effect?\n"
254+
"\n"
255+
"Only used by Firefox Labs Opt-Ins."
256+
),
257+
default=False,
258+
)
259+
260+
@model_validator(mode="after")
261+
@classmethod
262+
def validate_firefox_labs_base(cls, data: Self) -> Self:
263+
if data.isFirefoxLabsOptIn:
264+
if data.firefoxLabsTitle is None:
265+
raise ValueError(
266+
"missing field firefoxLabsTitle (required because isFirefoxLabsOptIn "
267+
"is True)"
268+
)
269+
if data.firefoxLabsDescription is None:
270+
raise ValueError(
271+
"missing field firefoxLabsDescription (required because "
272+
"isFirefoxLabsOptIn is True)"
273+
)
274+
275+
return data
276+
277+
model_config = ConfigDict(
278+
use_enum_values=True,
279+
json_schema_extra={
280+
"dependentSchemas": {
281+
"isFirefoxLabsOptIn": {
282+
"if": {
283+
"properties": {
284+
"isFirefoxLabsOptIn": {"const": True},
285+
},
286+
},
287+
"then": {
288+
"required": [
289+
"firefoxLabsDescription",
290+
"firefoxLabsTitle",
291+
],
292+
},
293+
}
294+
}
295+
},
296+
)

0 commit comments

Comments
 (0)