1package model
2
3import (
4	"encoding/json"
5	"time"
6)
7
8// MeasurementTarget is the target of a OONI measurement.
9type MeasurementTarget string
10
11// MarshalJSON serializes the MeasurementTarget.
12func (t MeasurementTarget) MarshalJSON() ([]byte, error) {
13	if t == "" {
14		return json.Marshal(nil)
15	}
16	return json.Marshal(string(t))
17}
18
19// Measurement is a OONI measurement.
20//
21// This structure is compatible with the definition of the base data format in
22// https://github.com/ooni/spec/blob/master/data-formats/df-000-base.md.
23type Measurement struct {
24	// Annotations contains results annotations
25	Annotations map[string]string `json:"annotations,omitempty"`
26
27	// DataFormatVersion is the version of the data format
28	DataFormatVersion string `json:"data_format_version"`
29
30	// Extensions contains information about the extensions included
31	// into the test_keys of this measurement.
32	Extensions map[string]int64 `json:"extensions,omitempty"`
33
34	// ID is the locally generated measurement ID
35	ID string `json:"id,omitempty"`
36
37	// Input is the measurement input
38	Input MeasurementTarget `json:"input"`
39
40	// InputHashes contains input hashes
41	InputHashes []string `json:"input_hashes,omitempty"`
42
43	// MeasurementStartTime is the time when the measurement started
44	MeasurementStartTime string `json:"measurement_start_time"`
45
46	// MeasurementStartTimeSaved is the moment in time when we
47	// started the measurement. This is not included into the JSON
48	// and is only used within probe-engine as a "zero" time.
49	MeasurementStartTimeSaved time.Time `json:"-"`
50
51	// Options contains command line options
52	Options []string `json:"options,omitempty"`
53
54	// ProbeASN contains the probe autonomous system number
55	ProbeASN string `json:"probe_asn"`
56
57	// ProbeCC contains the probe country code
58	ProbeCC string `json:"probe_cc"`
59
60	// ProbeCity contains the probe city
61	ProbeCity string `json:"probe_city,omitempty"`
62
63	// ProbeIP contains the probe IP
64	ProbeIP string `json:"probe_ip,omitempty"`
65
66	// ProbeNetworkName contains the probe network name
67	ProbeNetworkName string `json:"probe_network_name"`
68
69	// ReportID contains the report ID
70	ReportID string `json:"report_id"`
71
72	// ResolverASN is the ASN of the resolver
73	ResolverASN string `json:"resolver_asn"`
74
75	// ResolverIP is the resolver IP
76	ResolverIP string `json:"resolver_ip"`
77
78	// ResolverNetworkName is the network name of the resolver.
79	ResolverNetworkName string `json:"resolver_network_name"`
80
81	// SoftwareName contains the software name
82	SoftwareName string `json:"software_name"`
83
84	// SoftwareVersion contains the software version
85	SoftwareVersion string `json:"software_version"`
86
87	// TestHelpers contains the test helpers. It seems this structure is more
88	// complex than we would like. In particular, using a map from string to
89	// string does not fit into the web_connectivity use case. Hence, for now
90	// we're going to represent this using interface{}. In going forward we
91	// may probably want to have more uniform test helpers.
92	TestHelpers map[string]interface{} `json:"test_helpers,omitempty"`
93
94	// TestKeys contains the real test result. This field is opaque because
95	// each experiment will insert here a different structure.
96	TestKeys interface{} `json:"test_keys"`
97
98	// TestName contains the test name
99	TestName string `json:"test_name"`
100
101	// MeasurementRuntime contains the measurement runtime. The JSON name
102	// is test_runtime because this is the name expected by the OONI backend
103	// even though that name is clearly a misleading one.
104	MeasurementRuntime float64 `json:"test_runtime"`
105
106	// TestStartTime contains the test start time
107	TestStartTime string `json:"test_start_time"`
108
109	// TestVersion contains the test version
110	TestVersion string `json:"test_version"`
111}
112
113// AddAnnotations adds the annotations from input to m.Annotations.
114func (m *Measurement) AddAnnotations(input map[string]string) {
115	for key, value := range input {
116		m.AddAnnotation(key, value)
117	}
118}
119
120// AddAnnotation adds a single annotations to m.Annotations.
121func (m *Measurement) AddAnnotation(key, value string) {
122	if m.Annotations == nil {
123		m.Annotations = make(map[string]string)
124	}
125	m.Annotations[key] = value
126}
127