glean-parser 17.2.0__tar.gz → 18.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {glean_parser-17.2.0 → glean_parser-18.0.0}/PKG-INFO +3 -4
- {glean_parser-17.2.0 → glean_parser-18.0.0}/README.md +1 -1
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/__main__.py +8 -1
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/go_server.py +1 -3
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/lint.py +76 -2
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/markdown.py +2 -2
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/metrics.py +31 -2
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/pings.py +3 -1
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/schemas/metrics.2-0-0.schema.yaml +0 -9
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/schemas/pings.2-0-0.schema.yaml +0 -4
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/templates/kotlin.jinja2 +44 -27
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/templates/rust.jinja2 +16 -1
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/templates/swift.jinja2 +40 -6
- {glean_parser-17.2.0 → glean_parser-18.0.0}/pyproject.toml +1 -2
- glean_parser-18.0.0/tests/data/events_data_sensitivity.yaml +78 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/object.yaml +26 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test_go_server.py +15 -15
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test_javascript_server.py +6 -6
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test_kotlin.py +5 -3
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test_lint.py +231 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test_parser.py +78 -2
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test_python_server.py +3 -3
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test_ruby_server.py +3 -3
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test_rust_server.py +29 -22
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test_swift.py +9 -6
- {glean_parser-17.2.0 → glean_parser-18.0.0}/.gitignore +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/AUTHORS.md +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/LICENSE +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/__init__.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/coverage.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/data_review.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/javascript.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/javascript_server.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/kotlin.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/parser.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/python_server.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/ruby_server.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/rust.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/rust_server.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/schemas/metrics.1-0-0.schema.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/schemas/pings.1-0-0.schema.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/schemas/tags.1-0-0.schema.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/swift.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/tags.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/templates/data_review.jinja2 +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/templates/go_server.jinja2 +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/templates/javascript.buildinfo.jinja2 +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/templates/javascript.jinja2 +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/templates/javascript_server.jinja2 +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/templates/kotlin.buildinfo.jinja2 +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/templates/markdown.jinja2 +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/templates/python_server.jinja2 +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/templates/qmldir.jinja2 +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/templates/ruby_server.jinja2 +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/templates/rust_server.jinja2 +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/translate.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/translation_options.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/util.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/glean_parser/validate_ping.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/server_telemetry/sdk-metrics-compat.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/server_telemetry/server-side-pings.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/conftest.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/all_metrics.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/all_pings.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/attribution.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/bad_attribution.yamlx +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/bad_ping.yamlx +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/core.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/dual_labeled.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/dual_labeled_invalid.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/duplicate_labeled.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/duplicate_send_in_ping.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/empty.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/event_key_ordering.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/events_with_types.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/fxa-server-metrics.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/fxa-server-pings.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/go_server_custom_ping_only_metrics.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/go_server_custom_ping_only_pings.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/go_server_events_and_custom_ping_metrics.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/go_server_events_and_custom_ping_pings.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/go_server_events_only_metrics.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/go_server_metrics_unsupported.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/invalid-ping-names.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/invalid.yamlx +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/jwe.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/metric-with-tags.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/mixed-expirations.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/name_too_similar.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/old_event_api.yamlx +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/ordering.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/pings.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/rate.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/redefined_category.yamlx +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/redefined_metric.yamlx +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/redefined_ping.yamlx +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/reserved_categories.yamlx +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/ruby_server_metrics_unsupported.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/ruby_server_pings_unsupported.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/rust_server_custom_ping_only_metrics.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/rust_server_custom_ping_only_pings.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/rust_server_events_and_custom_ping_metrics.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/rust_server_events_and_custom_ping_pings.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/rust_server_events_only_metrics.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/rust_server_metrics_unsupported.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/same_name_different_category.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/schema-violation.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/send_if_empty_with_metrics.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/server_custom_ping_only_compare.go +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/server_custom_ping_only_compare.rs +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/server_events_and_custom_ping_compare.go +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/server_events_and_custom_ping_compare.rs +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/server_events_compare.rb +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/server_events_only_compare.go +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/server_events_only_compare.rs +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/server_metrics_no_events_no_pings.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/server_metrics_with_event.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/server_pings.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/single_labeled.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/smaller.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/tags.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/telemetry_mirror.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/text.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/text_invalid.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/unknown_ping_used.yaml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/wrong_key.yamlx +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/data/yaml_nits.yamlx +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/detekt.yml +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test-go/test.go.tmpl +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test-js/package.json +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test-js/test.js.tmpl +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test-py/test.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test-rb/test.rb.tmpl +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test-rs/test.rs.tmpl +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test_cli.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test_javascript.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test_markdown.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test_metrics.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test_pings.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test_rust.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test_tags.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test_translate.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test_utils.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/test_validate_ping.py +0 -0
- {glean_parser-17.2.0 → glean_parser-18.0.0}/tests/util.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: glean-parser
|
|
3
|
-
Version:
|
|
3
|
+
Version: 18.0.0
|
|
4
4
|
Summary: Parser tools for Mozilla's Glean telemetry
|
|
5
5
|
Project-URL: Homepage, https://mozilla.github.io/glean
|
|
6
6
|
Project-URL: Repository, https://github.com/mozilla/glean_parser
|
|
@@ -12,13 +12,12 @@ Classifier: Development Status :: 5 - Production/Stable
|
|
|
12
12
|
Classifier: Intended Audience :: Developers
|
|
13
13
|
Classifier: Natural Language :: English
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.9
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.10
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
-
Requires-Python: >=3.
|
|
20
|
+
Requires-Python: >=3.9
|
|
22
21
|
Requires-Dist: click>=7
|
|
23
22
|
Requires-Dist: diskcache>=4
|
|
24
23
|
Requires-Dist: jinja2>=2.10.1
|
|
@@ -45,7 +44,7 @@ code for various integrations, linting and coverage testing.
|
|
|
45
44
|
|
|
46
45
|
## Requirements
|
|
47
46
|
|
|
48
|
-
- Python 3.
|
|
47
|
+
- Python 3.9 (or later)
|
|
49
48
|
|
|
50
49
|
## Usage
|
|
51
50
|
|
|
@@ -166,7 +166,13 @@ def check(schema):
|
|
|
166
166
|
is_flag=True,
|
|
167
167
|
help=("Require tags to be specified for metrics and pings."),
|
|
168
168
|
)
|
|
169
|
-
|
|
169
|
+
@click.option(
|
|
170
|
+
"--expire-by-version",
|
|
171
|
+
help="Expire metrics by version, with the provided major version.",
|
|
172
|
+
type=click.INT,
|
|
173
|
+
required=False,
|
|
174
|
+
)
|
|
175
|
+
def glinter(input, allow_reserved, allow_missing_files, require_tags, expire_by_version):
|
|
170
176
|
"""
|
|
171
177
|
Runs a linter over the metrics.
|
|
172
178
|
"""
|
|
@@ -177,6 +183,7 @@ def glinter(input, allow_reserved, allow_missing_files, require_tags):
|
|
|
177
183
|
"allow_reserved": allow_reserved,
|
|
178
184
|
"allow_missing_files": allow_missing_files,
|
|
179
185
|
"require_tags": require_tags,
|
|
186
|
+
"expire_by_version": expire_by_version,
|
|
180
187
|
},
|
|
181
188
|
)
|
|
182
189
|
)
|
|
@@ -147,8 +147,6 @@ def output_go(
|
|
|
147
147
|
with filepath.open("w", encoding="utf-8") as fd:
|
|
148
148
|
fd.write(
|
|
149
149
|
template.render(
|
|
150
|
-
parser_version=__version__,
|
|
151
|
-
pings=ping_to_metrics,
|
|
152
|
-
events=event_metrics
|
|
150
|
+
parser_version=__version__, pings=ping_to_metrics, events=event_metrics
|
|
153
151
|
)
|
|
154
152
|
)
|
|
@@ -34,6 +34,12 @@ LintGenerator = Generator[str, None, None]
|
|
|
34
34
|
NitGenerator = Generator["GlinterNit", None, None]
|
|
35
35
|
|
|
36
36
|
|
|
37
|
+
def noop(*args):
|
|
38
|
+
""" A noop `LintGenerator`. Never yields a GlinterNit."""
|
|
39
|
+
return
|
|
40
|
+
yield
|
|
41
|
+
|
|
42
|
+
|
|
37
43
|
class CheckType(enum.Enum):
|
|
38
44
|
warning = 0
|
|
39
45
|
error = 1
|
|
@@ -100,7 +106,7 @@ def check_common_prefix(
|
|
|
100
106
|
common_prefix = "_".join(first[:i])
|
|
101
107
|
yield (
|
|
102
108
|
f"Within category '{category_name}', all metrics begin with "
|
|
103
|
-
f"prefix '{common_prefix}'."
|
|
109
|
+
f"prefix '{common_prefix}'. "
|
|
104
110
|
"Remove the prefixes on the metric names and (possibly) "
|
|
105
111
|
"rename the category."
|
|
106
112
|
)
|
|
@@ -313,13 +319,43 @@ def check_unexpected_unit(
|
|
|
313
319
|
def check_empty_datareview(
|
|
314
320
|
metric: metrics.Metric, parser_config: Dict[str, Any]
|
|
315
321
|
) -> LintGenerator:
|
|
316
|
-
disallowed_datareview = ["", "todo"]
|
|
322
|
+
disallowed_datareview = ["", "todo", "tbd"]
|
|
317
323
|
data_reviews = [dr.lower() in disallowed_datareview for dr in metric.data_reviews]
|
|
318
324
|
|
|
319
325
|
if any(data_reviews):
|
|
320
326
|
yield "List of data reviews should not contain empty strings or TODO markers."
|
|
321
327
|
|
|
322
328
|
|
|
329
|
+
def check_event_extras_potential_data_sensitivity_required(
|
|
330
|
+
metric: metrics.Metric, parser_config: Dict[str, Any]
|
|
331
|
+
) -> LintGenerator:
|
|
332
|
+
# Only looking at event metrics
|
|
333
|
+
if not isinstance(metric, metrics.Event):
|
|
334
|
+
return
|
|
335
|
+
|
|
336
|
+
# TODO(bug 1890648): Not all metrics have `data_sensitivity` defined.
|
|
337
|
+
has_data_sensitivity = hasattr(metric, "data_sensitivity")
|
|
338
|
+
# If already marked as "highly sensitive" no need for further checks
|
|
339
|
+
if has_data_sensitivity and any(
|
|
340
|
+
[
|
|
341
|
+
sensitivity == metrics.DataSensitivity.highly_sensitive
|
|
342
|
+
for sensitivity in metric.data_sensitivity
|
|
343
|
+
]
|
|
344
|
+
):
|
|
345
|
+
return
|
|
346
|
+
|
|
347
|
+
# List of potentially sensitive extra key names we want to flag.
|
|
348
|
+
potential_sensitive_names = ["url", "uri"]
|
|
349
|
+
name_list = ", ".join(potential_sensitive_names)
|
|
350
|
+
|
|
351
|
+
for extra_key in metric.extra_keys.keys():
|
|
352
|
+
if extra_key in potential_sensitive_names:
|
|
353
|
+
yield (
|
|
354
|
+
f"`{extra_key}` could potentially be used to collect sensitive data. Increase the metric's data sensitivity or disable the lint."
|
|
355
|
+
+ f" (This lint applies for the following extra key names: {name_list})"
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
|
|
323
359
|
def check_redundant_ping(
|
|
324
360
|
pings: pings.Ping, parser_config: Dict[str, Any]
|
|
325
361
|
) -> LintGenerator:
|
|
@@ -432,6 +468,12 @@ METRIC_CHECKS: Dict[
|
|
|
432
468
|
"METRIC_ON_EVENTS_LIFETIME": (check_metric_on_events_lifetime, CheckType.error),
|
|
433
469
|
"UNEXPECTED_UNIT": (check_unexpected_unit, CheckType.warning),
|
|
434
470
|
"EMPTY_DATAREVIEW": (check_empty_datareview, CheckType.warning),
|
|
471
|
+
"HIGHER_DATA_SENSITIVITY_REQUIRED": (
|
|
472
|
+
check_event_extras_potential_data_sensitivity_required,
|
|
473
|
+
CheckType.warning,
|
|
474
|
+
),
|
|
475
|
+
# Implemented inline, listed here so that `UNKNOWN_LINT` knows about it.
|
|
476
|
+
"UNUSED_NO_LINT": (noop, CheckType.warning),
|
|
435
477
|
}
|
|
436
478
|
|
|
437
479
|
|
|
@@ -627,8 +669,40 @@ def lint_metrics(
|
|
|
627
669
|
)
|
|
628
670
|
|
|
629
671
|
for _metric_name, metric in sorted(list(category_metrics.items())):
|
|
672
|
+
check_unused_lints = "UNUSED_NO_LINT" not in metric.no_lint
|
|
673
|
+
check_unknown_lint = "UNKNOWN_LINT" not in metric.no_lint
|
|
674
|
+
|
|
675
|
+
if check_unknown_lint and metric.no_lint:
|
|
676
|
+
known_lint_names = (
|
|
677
|
+
set(METRIC_CHECKS.keys())
|
|
678
|
+
| set(ALL_OBJECT_CHECKS.keys())
|
|
679
|
+
| set(CATEGORY_CHECKS.keys())
|
|
680
|
+
)
|
|
681
|
+
unknown_lints = [
|
|
682
|
+
lint for lint in metric.no_lint if lint not in known_lint_names
|
|
683
|
+
]
|
|
684
|
+
if unknown_lints:
|
|
685
|
+
nits.append(
|
|
686
|
+
GlinterNit(
|
|
687
|
+
"UNKNOWN_LINT",
|
|
688
|
+
".".join([metric.category, metric.name]),
|
|
689
|
+
f"Metric contains unknown no_lints: {unknown_lints}. Please remove the `no_lint` entry.",
|
|
690
|
+
CheckType.warning,
|
|
691
|
+
)
|
|
692
|
+
)
|
|
693
|
+
|
|
630
694
|
for check_name, (check_func, check_type) in METRIC_CHECKS.items():
|
|
631
695
|
new_nits = list(check_func(metric, parser_config))
|
|
696
|
+
if check_unused_lints and check_name in metric.no_lint and not len(new_nits):
|
|
697
|
+
nits.append(
|
|
698
|
+
GlinterNit(
|
|
699
|
+
"UNUSED_NO_LINT",
|
|
700
|
+
".".join([metric.category, metric.name]),
|
|
701
|
+
f"Metric contains a no_lint: {check_name}, but {check_name} does not apply. Please remove the `no_lint` entry.",
|
|
702
|
+
CheckType.warning,
|
|
703
|
+
)
|
|
704
|
+
)
|
|
705
|
+
|
|
632
706
|
if len(new_nits):
|
|
633
707
|
if check_name not in metric.no_lint:
|
|
634
708
|
nits.extend(
|
|
@@ -52,9 +52,9 @@ def ping_desc(
|
|
|
52
52
|
|
|
53
53
|
if ping_name in pings.RESERVED_PING_NAMES:
|
|
54
54
|
desc = (
|
|
55
|
-
"This is a built-in ping that is assembled out of the "
|
|
56
|
-
"box by the Glean SDK."
|
|
55
|
+
"This is a built-in ping that is assembled out of the box by the Glean SDK."
|
|
57
56
|
)
|
|
57
|
+
|
|
58
58
|
elif ping_name == "all-pings":
|
|
59
59
|
desc = "These metrics are sent in every ping."
|
|
60
60
|
elif custom_pings_cache is not None and ping_name in custom_pings_cache:
|
|
@@ -462,8 +462,10 @@ class Object(Metric):
|
|
|
462
462
|
self._generate_structure = self.validate_structure(structure)
|
|
463
463
|
super().__init__(*args, **kwargs)
|
|
464
464
|
|
|
465
|
-
ALLOWED_TOPLEVEL = {"type", "properties", "items"}
|
|
465
|
+
ALLOWED_TOPLEVEL = {"type", "properties", "items", "description", "oneOf"}
|
|
466
466
|
ALLOWED_TYPES = ["object", "array", "number", "string", "boolean"]
|
|
467
|
+
ALLOWED_SUBTYPES = ["number", "string", "boolean"]
|
|
468
|
+
ALLOWED_ONEOF_FIELDS = {"description", "type"}
|
|
467
469
|
|
|
468
470
|
@staticmethod
|
|
469
471
|
def _validate_substructure(structure):
|
|
@@ -475,6 +477,34 @@ class Object(Metric):
|
|
|
475
477
|
f"Found additional fields: {extra}. Only allowed: {allowed}"
|
|
476
478
|
)
|
|
477
479
|
|
|
480
|
+
if "oneOf" in structure:
|
|
481
|
+
subtypes = structure.pop("oneOf")
|
|
482
|
+
structure["type"] = "oneof"
|
|
483
|
+
structure["subtypes"] = []
|
|
484
|
+
if not subtypes:
|
|
485
|
+
raise ValueError("List of types required.")
|
|
486
|
+
|
|
487
|
+
for typ in subtypes:
|
|
488
|
+
extra = set(typ.keys()) - Object.ALLOWED_ONEOF_FIELDS
|
|
489
|
+
if extra:
|
|
490
|
+
extra = ", ".join(extra)
|
|
491
|
+
allowed = ", ".join(Object.ALLOWED_ONEOF_FIELDS)
|
|
492
|
+
raise ValueError(
|
|
493
|
+
f"Found additional fields: {extra}. Only allowed: {allowed}"
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
ty = typ.get("type")
|
|
497
|
+
if not ty:
|
|
498
|
+
raise ValueError("element of `oneOf` list must contain a type")
|
|
499
|
+
|
|
500
|
+
if ty not in Object.ALLOWED_SUBTYPES:
|
|
501
|
+
raise ValueError(
|
|
502
|
+
f"invalid `type` in `oneOf` list. found: {ty}, only allowed: {Object.ALLOWED_SUBTYPES}"
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
structure["subtypes"].append(ty)
|
|
506
|
+
return structure
|
|
507
|
+
|
|
478
508
|
if "type" not in structure:
|
|
479
509
|
raise ValueError(
|
|
480
510
|
f"missing `type` in object structure. Allowed: {Object.ALLOWED_TYPES}"
|
|
@@ -579,7 +609,6 @@ class DualLabeledCounter(Metric):
|
|
|
579
609
|
d["categories"] = self.ordered_categories
|
|
580
610
|
del d["ordered_keys"]
|
|
581
611
|
del d["ordered_categories"]
|
|
582
|
-
del d["dual_labeled"]
|
|
583
612
|
return d
|
|
584
613
|
|
|
585
614
|
|
|
@@ -48,7 +48,9 @@ class Ping:
|
|
|
48
48
|
self.metadata = metadata
|
|
49
49
|
self.precise_timestamps = self.metadata.get("precise_timestamps", True)
|
|
50
50
|
self.include_info_sections = self.metadata.get("include_info_sections", True)
|
|
51
|
-
self.follows_collection_enabled = self.metadata.get(
|
|
51
|
+
self.follows_collection_enabled = self.metadata.get(
|
|
52
|
+
"follows_collection_enabled", True
|
|
53
|
+
)
|
|
52
54
|
if enabled is None:
|
|
53
55
|
enabled = True
|
|
54
56
|
self.enabled = enabled
|
|
@@ -15,10 +15,6 @@ description: |
|
|
|
15
15
|
$id: moz://mozilla.org/schemas/glean/metrics/2-0-0
|
|
16
16
|
|
|
17
17
|
definitions:
|
|
18
|
-
token:
|
|
19
|
-
type: string
|
|
20
|
-
pattern: "^[A-Za-z_][A-Za-z0-9_\\.]*$"
|
|
21
|
-
|
|
22
18
|
snake_case:
|
|
23
19
|
type: string
|
|
24
20
|
pattern: "^[a-z_][a-z0-9_]*$"
|
|
@@ -39,11 +35,6 @@ definitions:
|
|
|
39
35
|
type: string
|
|
40
36
|
pattern: "^[a-z][a-z0-9-]{0,29}$"
|
|
41
37
|
|
|
42
|
-
long_id:
|
|
43
|
-
allOf:
|
|
44
|
-
- $ref: "#/definitions/snake_case"
|
|
45
|
-
- maxLength: 40
|
|
46
|
-
|
|
47
38
|
short_id:
|
|
48
39
|
allOf:
|
|
49
40
|
- $ref: "#/definitions/snake_case"
|
|
@@ -15,10 +15,6 @@ description: |
|
|
|
15
15
|
$id: moz://mozilla.org/schemas/glean/pings/2-0-0
|
|
16
16
|
|
|
17
17
|
definitions:
|
|
18
|
-
dotted_snake_case:
|
|
19
|
-
type: string
|
|
20
|
-
pattern: "^[a-z_][a-z0-9_]{0,29}(\\.[a-z_][a-z0-9_]{0,29})*$"
|
|
21
|
-
maxLength: 40
|
|
22
18
|
# Prior to version 2.0.0 of the schema, special ping names with underscores
|
|
23
19
|
# were also supported.
|
|
24
20
|
kebab_case:
|
|
@@ -67,8 +67,17 @@ data class {{ obj.name|Camelize }}{{ suffix }}(
|
|
|
67
67
|
{%- endmacro -%}
|
|
68
68
|
|
|
69
69
|
{%- macro generate_structure(name, struct) %}
|
|
70
|
-
{%- if struct.type == "
|
|
71
|
-
|
|
70
|
+
{%- if struct.type == "oneof" -%}
|
|
71
|
+
sealed class {{ name }}(): ObjectSerialize {
|
|
72
|
+
{% for ty in struct.subtypes %}
|
|
73
|
+
class {{ty|Camelize}}(val inner: kotlin.{{ty|structure_type_name}}? = null) : {{ name }}() {
|
|
74
|
+
override fun intoSerializedObject(): kotlin.String {
|
|
75
|
+
return this.inner?.intoSerializedObject() ?: "null"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
{% endfor %}
|
|
79
|
+
}
|
|
80
|
+
{%- elif struct.type == "array" -%}
|
|
72
81
|
data class {{ name }}(var items: MutableList<{{ name }}Item> = mutableListOf()) : ObjectSerialize {
|
|
73
82
|
fun add(elem: {{ name }}Item) = items.add(elem)
|
|
74
83
|
|
|
@@ -83,41 +92,54 @@ data class {{ obj.name|Camelize }}{{ suffix }}(
|
|
|
83
92
|
fun set(index: Int, element: {{ name }}Item) = items.set(index, element)
|
|
84
93
|
|
|
85
94
|
override fun intoSerializedObject(): String {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
override fun serialize(encoder: Encoder, value: {{name}}) {
|
|
99
|
-
encoder.encodeSerializableValue(ListSerializer(serializer<{{name}}Item>()), value.items)
|
|
95
|
+
var json = buildString {
|
|
96
|
+
append("[")
|
|
97
|
+
var first = true
|
|
98
|
+
for (item in items) {
|
|
99
|
+
if (!first) {
|
|
100
|
+
append(",")
|
|
101
|
+
}
|
|
102
|
+
first = false
|
|
103
|
+
append(item.intoSerializedObject())
|
|
104
|
+
}
|
|
105
|
+
append("]")
|
|
100
106
|
}
|
|
107
|
+
return json
|
|
101
108
|
}
|
|
102
109
|
}
|
|
103
110
|
|
|
104
111
|
{{ generate_structure(name ~ "Item", struct["items"]) }}
|
|
105
112
|
|
|
106
113
|
{%- elif struct.type == "object" -%}
|
|
107
|
-
@Serializable
|
|
108
114
|
data class {{ name }}(
|
|
109
115
|
{% for itemname, val in struct.properties.items() %}
|
|
110
116
|
{% if val.type == "array" %}
|
|
111
117
|
var {{itemname|camelize}}: {{ name ~ itemname|Camelize }} = {{ name ~ itemname|Camelize }}(),
|
|
112
118
|
{% elif val.type == "object" %}
|
|
113
119
|
var {{itemname|camelize}}: {{ name ~ "Item" ~ itemname|Camelize ~ "Object" }}? = null,
|
|
120
|
+
{% elif val.type == "oneof" %}
|
|
121
|
+
var {{itemname|camelize}}: {{ name ~ itemname|Camelize ~ "Enum" }}? = null,
|
|
114
122
|
{% else %}
|
|
115
123
|
var {{itemname|camelize}}: {{val.type|structure_type_name}}? = null,
|
|
116
124
|
{% endif %}
|
|
117
125
|
{% endfor %}
|
|
118
126
|
): ObjectSerialize {
|
|
119
127
|
override fun intoSerializedObject(): String {
|
|
120
|
-
|
|
128
|
+
var data: MutableList<String> = mutableListOf()
|
|
129
|
+
{% for itemname, val in struct.properties.items() %}
|
|
130
|
+
this.{{itemname|camelize}}?.let { {{itemname|camelize}} ->
|
|
131
|
+
val elem = buildString {
|
|
132
|
+
append("\"{{itemname}}\":")
|
|
133
|
+
append({{itemname|camelize}}.intoSerializedObject())
|
|
134
|
+
}
|
|
135
|
+
data.add(elem)
|
|
136
|
+
}
|
|
137
|
+
{% endfor %}
|
|
138
|
+
return buildString {
|
|
139
|
+
append("{")
|
|
140
|
+
append(data.joinToString(","))
|
|
141
|
+
append("}")
|
|
142
|
+
}
|
|
121
143
|
}
|
|
122
144
|
}
|
|
123
145
|
|
|
@@ -128,6 +150,9 @@ data class {{ obj.name|Camelize }}{{ suffix }}(
|
|
|
128
150
|
{% elif val.type == "object" %}
|
|
129
151
|
{% set nested_name = name ~ "Item" ~ itemname|Camelize ~ "Object" %}
|
|
130
152
|
{{ generate_structure(nested_name, val) }}
|
|
153
|
+
{% elif val.type == "oneof" %}
|
|
154
|
+
{% set nested_name = name ~ itemname|Camelize ~ "Enum" %}
|
|
155
|
+
{{ generate_structure(nested_name, val) }}
|
|
131
156
|
{% endif %}
|
|
132
157
|
{% endfor %}
|
|
133
158
|
|
|
@@ -174,15 +199,7 @@ import {{ glean_namespace }}.private.{{ obj_type }} // ktlint-disable import-ord
|
|
|
174
199
|
import {{ glean_namespace }}.private.LabeledMetricType // ktlint-disable import-ordering
|
|
175
200
|
{% endif %}
|
|
176
201
|
{% if has_object_metrics %}
|
|
177
|
-
import
|
|
178
|
-
import kotlinx.serialization.Serializable
|
|
179
|
-
import kotlinx.serialization.builtins.ListSerializer
|
|
180
|
-
import kotlinx.serialization.descriptors.listSerialDescriptor
|
|
181
|
-
import kotlinx.serialization.encodeToString
|
|
182
|
-
import kotlinx.serialization.encoding.Decoder
|
|
183
|
-
import kotlinx.serialization.encoding.Encoder
|
|
184
|
-
import kotlinx.serialization.json.Json
|
|
185
|
-
import kotlinx.serialization.serializer
|
|
202
|
+
import {{ glean_namespace }}.private.intoSerializedObject
|
|
186
203
|
{% endif %}
|
|
187
204
|
|
|
188
205
|
{# HACK HACK HACK -- typealiases MUST BE top-level #}
|
|
@@ -9,7 +9,16 @@ Jinja2 template is not. Please file bugs! #}
|
|
|
9
9
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
10
10
|
|
|
11
11
|
{%- macro generate_structure(name, struct) %}
|
|
12
|
-
{
|
|
12
|
+
{%- if struct.type == "oneof" -%}
|
|
13
|
+
#[derive(Debug, Hash, Eq, PartialEq, Clone, ::glean::traits::__serde::Serialize, ::glean::traits::__serde::Deserialize)]
|
|
14
|
+
#[serde(crate = "::glean::traits::__serde")]
|
|
15
|
+
#[serde(untagged)]
|
|
16
|
+
pub enum {{ name }} {
|
|
17
|
+
{% for ty in struct.subtypes %}
|
|
18
|
+
{{ty|Camelize}}({{ty|structure_type_name}}),
|
|
19
|
+
{% endfor %}
|
|
20
|
+
}
|
|
21
|
+
{% elif struct.type == "array" %}
|
|
13
22
|
pub type {{ name }} = Vec<{{ name }}Item>;
|
|
14
23
|
|
|
15
24
|
{{ generate_structure(name ~ "Item", struct["items"]) }}
|
|
@@ -26,6 +35,9 @@ Jinja2 template is not. Please file bugs! #}
|
|
|
26
35
|
{% elif val.type == "object" %}
|
|
27
36
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
28
37
|
pub {{itemname|snake_case}}: Option<{{ name ~ "Item" ~ itemname|Camelize ~ "Object" }}>,
|
|
38
|
+
{% elif val.type == "oneof" %}
|
|
39
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
40
|
+
pub {{itemname|snake_case}}: Option<{{ name ~ itemname|Camelize ~ "Enum" }}>,
|
|
29
41
|
{% else %}
|
|
30
42
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
31
43
|
pub {{itemname|snake_case}}: Option<{{val.type|structure_type_name}}>,
|
|
@@ -40,6 +52,9 @@ Jinja2 template is not. Please file bugs! #}
|
|
|
40
52
|
{% elif val.type == "object" %}
|
|
41
53
|
{% set nested_name = name ~ "Item" ~ itemname|Camelize ~ "Object" %}
|
|
42
54
|
{{ generate_structure(nested_name, val) }}
|
|
55
|
+
{% elif val.type == "oneof" %}
|
|
56
|
+
{% set nested_name = name ~ itemname|Camelize ~ "Enum" %}
|
|
57
|
+
{{ generate_structure(nested_name, val) }}
|
|
43
58
|
{% endif %}
|
|
44
59
|
{% endfor %}
|
|
45
60
|
|
|
@@ -45,7 +45,23 @@ struct {{ obj.name|Camelize }}{{ suffix }}: EventExtras {
|
|
|
45
45
|
{% endmacro %}
|
|
46
46
|
|
|
47
47
|
{%- macro generate_structure(name, struct) %}
|
|
48
|
-
{%- if struct.type == "
|
|
48
|
+
{%- if struct.type == "oneof" -%}
|
|
49
|
+
enum {{ name }}: Codable, Equatable, ObjectSerialize {
|
|
50
|
+
{% for ty in struct.subtypes %}
|
|
51
|
+
case {{ty}}({{ty|structure_type_name}})
|
|
52
|
+
{% endfor %}
|
|
53
|
+
|
|
54
|
+
func intoSerializedObject() -> String {
|
|
55
|
+
switch self {
|
|
56
|
+
{% for ty in struct.subtypes %}
|
|
57
|
+
case .{{ty}}(let val):
|
|
58
|
+
return val.intoSerializedObject()
|
|
59
|
+
{% endfor %}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
{%- elif struct.type == "array" -%}
|
|
49
65
|
typealias {{ name }} = [{{ name }}Item]
|
|
50
66
|
|
|
51
67
|
{{ generate_structure(name ~ "Item", struct["items"]) }}
|
|
@@ -57,15 +73,30 @@ struct {{ obj.name|Camelize }}{{ suffix }}: EventExtras {
|
|
|
57
73
|
var {{itemname|camelize|variable_name}}: {{ name ~ itemname|Camelize }} = []
|
|
58
74
|
{% elif val.type == "object" %}
|
|
59
75
|
var {{itemname|camelize|variable_name}}: {{ name ~ "Item" ~ itemname|Camelize ~ "Object" }}?
|
|
76
|
+
{% elif val.type == "oneof" %}
|
|
77
|
+
var {{itemname|camelize|variable_name}}: {{ name ~ itemname|Camelize ~ "Enum" }}?
|
|
60
78
|
{% else %}
|
|
61
79
|
var {{itemname|camelize|variable_name}}: {{val.type|structure_type_name}}?
|
|
62
80
|
{% endif %}
|
|
63
81
|
{% endfor %}
|
|
64
82
|
|
|
65
83
|
func intoSerializedObject() -> String {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
84
|
+
var data = [String]()
|
|
85
|
+
{% for itemname, val in struct.properties.items() %}
|
|
86
|
+
{% if val.type == "array" %}
|
|
87
|
+
if {{itemname|camelize|variable_name}}.count > 0 {
|
|
88
|
+
let {{itemname|camelize|variable_name}} = self.{{itemname|camelize|variable_name}}
|
|
89
|
+
{% else %}
|
|
90
|
+
if let {{itemname}} = self.{{itemname}} {
|
|
91
|
+
{% endif %}
|
|
92
|
+
var elem = "\"{{itemname}}\":"
|
|
93
|
+
elem.append({{itemname|camelize|variable_name}}.intoSerializedObject())
|
|
94
|
+
data.append(elem)
|
|
95
|
+
}
|
|
96
|
+
{% endfor %}
|
|
97
|
+
var json = "{"
|
|
98
|
+
json.append(data.joined(separator: ","))
|
|
99
|
+
json.append("}")
|
|
69
100
|
return json
|
|
70
101
|
}
|
|
71
102
|
}
|
|
@@ -77,6 +108,9 @@ struct {{ obj.name|Camelize }}{{ suffix }}: EventExtras {
|
|
|
77
108
|
{% elif val.type == "object" %}
|
|
78
109
|
{% set nested_name = name ~ "Item" ~ itemname|Camelize ~ "Object" %}
|
|
79
110
|
{{ generate_structure(nested_name, val) }}
|
|
111
|
+
{% elif val.type == "oneof" %}
|
|
112
|
+
{% set nested_name = name ~ itemname|Camelize ~ "Enum" %}
|
|
113
|
+
{{ generate_structure(nested_name, val) }}
|
|
80
114
|
{% endif %}
|
|
81
115
|
{% endfor %}
|
|
82
116
|
|
|
@@ -111,7 +145,7 @@ extension {{ namespace }} {
|
|
|
111
145
|
|
|
112
146
|
{% for category in categories %}
|
|
113
147
|
{% if category.contains_pings %}
|
|
114
|
-
class {{ category.name|Camelize }} {
|
|
148
|
+
final class {{ category.name|Camelize }}: Sendable {
|
|
115
149
|
public static let shared = {{ category.name|Camelize }}()
|
|
116
150
|
private init() {
|
|
117
151
|
// Intentionally left private, no external user can instantiate a new global object.
|
|
@@ -121,7 +155,7 @@ extension {{ namespace }} {
|
|
|
121
155
|
{% if obj|attr("_generate_enums") %}
|
|
122
156
|
{% for name, suffix in obj["_generate_enums"] %}
|
|
123
157
|
{% if obj|attr(name)|length %}
|
|
124
|
-
enum {{ obj.name|Camelize }}{{ suffix }}: Int, ReasonCodes {
|
|
158
|
+
enum {{ obj.name|Camelize }}{{ suffix }}: Int, ReasonCodes, Sendable {
|
|
125
159
|
{% for key in obj|attr(name) %}
|
|
126
160
|
case {{ key|camelize|variable_name }} = {{ loop.index-1 }}
|
|
127
161
|
{% endfor %}
|
|
@@ -11,14 +11,13 @@ classifiers = [
|
|
|
11
11
|
"Intended Audience :: Developers",
|
|
12
12
|
"Natural Language :: English",
|
|
13
13
|
"Programming Language :: Python :: 3",
|
|
14
|
-
"Programming Language :: Python :: 3.8",
|
|
15
14
|
"Programming Language :: Python :: 3.9",
|
|
16
15
|
"Programming Language :: Python :: 3.10",
|
|
17
16
|
"Programming Language :: Python :: 3.11",
|
|
18
17
|
"Programming Language :: Python :: 3.12",
|
|
19
18
|
"Programming Language :: Python :: 3.13",
|
|
20
19
|
]
|
|
21
|
-
requires-python = ">=3.
|
|
20
|
+
requires-python = ">=3.9"
|
|
22
21
|
dependencies = [
|
|
23
22
|
"Click>=7",
|
|
24
23
|
"diskcache>=4",
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Any copyright is dedicated to the Public Domain.
|
|
2
|
+
# https://creativecommons.org/publicdomain/zero/1.0/
|
|
3
|
+
|
|
4
|
+
---
|
|
5
|
+
$schema: moz://mozilla.org/schemas/glean/metrics/2-0-0
|
|
6
|
+
|
|
7
|
+
event:
|
|
8
|
+
low_sensitivity:
|
|
9
|
+
type: event
|
|
10
|
+
description: |
|
|
11
|
+
Just testing events
|
|
12
|
+
bugs:
|
|
13
|
+
- https://bugzilla.mozilla.org/show_bug.cgi?id=1973017
|
|
14
|
+
data_reviews:
|
|
15
|
+
- http://example.com/reviews
|
|
16
|
+
notification_emails:
|
|
17
|
+
- CHANGE-ME@example.com
|
|
18
|
+
extra_keys:
|
|
19
|
+
url:
|
|
20
|
+
type: string
|
|
21
|
+
description: "This is key one"
|
|
22
|
+
expires: never
|
|
23
|
+
data_sensitivity:
|
|
24
|
+
- technical
|
|
25
|
+
|
|
26
|
+
no_data_sensitivity:
|
|
27
|
+
type: event
|
|
28
|
+
description: |
|
|
29
|
+
Just testing events
|
|
30
|
+
bugs:
|
|
31
|
+
- https://bugzilla.mozilla.org/show_bug.cgi?id=1973017
|
|
32
|
+
data_reviews:
|
|
33
|
+
- http://example.com/reviews
|
|
34
|
+
notification_emails:
|
|
35
|
+
- CHANGE-ME@example.com
|
|
36
|
+
extra_keys:
|
|
37
|
+
url:
|
|
38
|
+
type: string
|
|
39
|
+
description: "This is key one"
|
|
40
|
+
expires: never
|
|
41
|
+
|
|
42
|
+
correct_sensitivity:
|
|
43
|
+
type: event
|
|
44
|
+
description: |
|
|
45
|
+
Just testing events
|
|
46
|
+
bugs:
|
|
47
|
+
- https://bugzilla.mozilla.org/show_bug.cgi?id=1973017
|
|
48
|
+
data_reviews:
|
|
49
|
+
- http://example.com/reviews
|
|
50
|
+
notification_emails:
|
|
51
|
+
- CHANGE-ME@example.com
|
|
52
|
+
extra_keys:
|
|
53
|
+
url:
|
|
54
|
+
type: string
|
|
55
|
+
description: "This is key one"
|
|
56
|
+
expires: never
|
|
57
|
+
data_sensitivity:
|
|
58
|
+
- highly_sensitive
|
|
59
|
+
|
|
60
|
+
exempt:
|
|
61
|
+
type: event
|
|
62
|
+
description: |
|
|
63
|
+
Just testing events
|
|
64
|
+
bugs:
|
|
65
|
+
- https://bugzilla.mozilla.org/show_bug.cgi?id=1973017
|
|
66
|
+
data_reviews:
|
|
67
|
+
- http://example.com/reviews
|
|
68
|
+
notification_emails:
|
|
69
|
+
- CHANGE-ME@example.com
|
|
70
|
+
extra_keys:
|
|
71
|
+
url:
|
|
72
|
+
type: string
|
|
73
|
+
description: "This is key one"
|
|
74
|
+
expires: never
|
|
75
|
+
data_sensitivity:
|
|
76
|
+
- technical
|
|
77
|
+
no_lint:
|
|
78
|
+
- HIGHER_DATA_SENSITIVITY_REQUIRED
|