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