whos_got_dirt 0.0.2 → 0.0.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 98d4adb0f91ffac1ff9ceabe8232a3861e2f900a
4
- data.tar.gz: 627276f14cba96da225df90590eb71267ef13c1a
3
+ metadata.gz: 063529b54e6a2c0acb0ab21f1eb44bce5683afe8
4
+ data.tar.gz: 428acf538ee358d701565695ca601078ba6db10b
5
5
  SHA512:
6
- metadata.gz: d9e8cdc8e2076c071af133ac8abdcf97d0676c4edf6712c9591ab661ade771f5729abde4cef9766834fb49c282e6a6f1b134ab7daed572aaa55a9427b912184c
7
- data.tar.gz: 1ff4a4d9c4afb312c5550afb83c48b503ecb82186fcb106240b6db7ca05187458d9fe067f1cef806e5c6f02841692150e0fb88ea21194e2560ed37f4b0010b89
6
+ metadata.gz: 79d13ce720b98a1e70f7adabab7ad6797faeb304edaca55400a40ed9c3b679376be61d1b303cb75bdc4b67538c20be89c5d6ae149c49dc055c9c2d1563e31d3f
7
+ data.tar.gz: 1d8433a1cf7d0b01784ddf2840baba3b93c3d515b0ce99e280eb74f54d25eee6d4db194cb230283f03a6bf3563c33590b520e4e072c1135458922b22e5feba85
data/Gemfile CHANGED
@@ -2,6 +2,3 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in the gemspec
4
4
  gemspec
5
-
6
- # Remove once vcr 2.9.4 released.
7
- gem 'vcr', git: 'https://github.com/vcr/vcr.git'
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Who's got dirt? A federated search API for entities and relations
1
+ # Who's got dirt? A federated search API for influence data
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/whos_got_dirt.svg)](https://badge.fury.io/rb/whos_got_dirt)
4
4
  [![Build Status](https://secure.travis-ci.org/influencemapping/whos_got_dirt-gem.png)](https://travis-ci.org/influencemapping/whos_got_dirt-gem)
data/Rakefile CHANGED
@@ -14,56 +14,3 @@ rescue LoadError
14
14
  abort 'YARD is not available. In order to run yard, you must: gem install yard'
15
15
  end
16
16
  end
17
-
18
- desc 'Fetch schemas, rewrite references, and store locally'
19
- task :schemas do
20
- require 'json'
21
- require 'open-uri'
22
-
23
- require 'json-schema'
24
-
25
- def process_value(value, definitions)
26
- url = value['$ref']
27
- if url
28
- name = url.rpartition('/')[2].chomp('.json#')
29
- value['$ref'] = "#/definitions/#{name}"
30
- define(name, url, definitions)
31
- end
32
- end
33
-
34
- def process_schema(url, definitions)
35
- schema = JSON.load(open(url).read)
36
- schema['properties'].each do |_,value|
37
- process_value(value, definitions)
38
- if value.key?('items')
39
- process_value(value['items'], definitions)
40
- end
41
- end
42
- schema
43
- end
44
-
45
- def define(name, url, definitions)
46
- unless definitions.key?(name)
47
- definitions[name] = {} # to avoid recursion
48
- definitions[name] = process_schema(url, definitions)
49
- definitions[name].delete('id')
50
- end
51
- end
52
-
53
- definitions = {} # passed by reference
54
-
55
- %w(organization person).each do |name|
56
- define(name, "http://www.popoloproject.com/schemas/#{name}.json#", definitions)
57
- end
58
-
59
- schema = {
60
- '$schema' => 'http://json-schema.org/draft-03/schema#',
61
- 'definitions' => definitions,
62
- }
63
-
64
- JSON::Validator.validate!(schema, {}, validate_schema: true)
65
-
66
- File.open(File.expand_path(File.join('..', 'schemas', 'popolo.json'), __FILE__), 'w') do |f|
67
- f.write(JSON.pretty_generate(schema))
68
- end
69
- end
@@ -33,10 +33,10 @@ module WhosGotDirt
33
33
  equal('user_key', 'poderopedia_api_key')
34
34
  equal('entity', 'type', valid: ['Person', 'Organization'], transform: lambda{|v|
35
35
  case v
36
- when 'persona'
37
- 'Person'
38
- when 'organizacion'
39
- 'Organization'
36
+ when 'Person'
37
+ 'persona'
38
+ when 'Organization'
39
+ 'organizacion'
40
40
  end
41
41
  })
42
42
 
@@ -56,6 +56,13 @@ module WhosGotDirt
56
56
  raise NotImplementedError
57
57
  end
58
58
 
59
+ # Returns the error message in the response body.
60
+ #
61
+ # @return [String] the error message in the response body
62
+ def error_message
63
+ body
64
+ end
65
+
59
66
  # @!method initialize(response)
60
67
  # Sets the response's response.
61
68
  # @param [Faraday::Response] response a response
@@ -75,5 +82,9 @@ module WhosGotDirt
75
82
  # @!method status
76
83
  # Returns the HTTP status code.
77
84
  # @return [Fixnum] the HTTP status code
85
+
86
+ # @!method success?
87
+ # Returns whether the request succeeded.
88
+ # @return [Boolean] whether the request succeeded
78
89
  end
79
90
  end
@@ -26,7 +26,7 @@ module WhosGotDirt
26
26
  'url' => '/api_uri',
27
27
  'note' => 'LittleSis API detail',
28
28
  }],
29
- 'updated_at' => '/updated_at',
29
+ 'updated_at' => date_formatter('updated_at', '/updated_at'),
30
30
 
31
31
  # Class-specific.
32
32
  'start_date' => lambda{|data|
@@ -11,8 +11,6 @@ module WhosGotDirt
11
11
  'classification' => '/company_type',
12
12
  'founding_date' => '/incorporation_date',
13
13
  'dissolution_date' => '/dissolution_date',
14
- 'created_at' => '/created_at',
15
- 'updated_at' => '/updated_at',
16
14
  'other_names' => [{
17
15
  'name' => '/previous_names/company_name',
18
16
  'start_date' => '/previous_names/start_date',
@@ -37,14 +35,17 @@ module WhosGotDirt
37
35
  'sources' => [{
38
36
  'url' => '/sources/url',
39
37
  'note' => '/sources/publisher',
40
- 'retrieved_at' => '/sources/retrieved_at', # @todo check Dublin Core, etc.
38
+ # API-specific.
39
+ 'retrieved_at' => '/sources/retrieved_at',
41
40
  }],
41
+ 'created_at' => '/created_at',
42
+ 'updated_at' => '/updated_at',
42
43
  # API-specific.
43
- 'branch_status' => '/branch_status', # @todo if boolean, use "branch" as in request
44
+ 'branch_status' => '/branch_status',
44
45
  'current_status' => '/current_status',
45
46
  'inactive' => '/inactive',
46
47
  'jurisdiction_code' => '/jurisdiction_code',
47
- 'retrieved_at' => '/retrieved_at', # @todo check Dublin Core, etc.
48
+ 'retrieved_at' => '/retrieved_at',
48
49
  }
49
50
 
50
51
  # Transforms the parsed response body into results.
@@ -52,7 +53,7 @@ module WhosGotDirt
52
53
  # @return [Array<Hash>] the results
53
54
  def to_a
54
55
  parsed_body['companies'].map do |data|
55
- Result.new('Organization', renderer.result(data['company']), self).finalize!
56
+ Result.new('Entity', renderer.result(data['company']), self).finalize!
56
57
  end
57
58
  end
58
59
  end
@@ -36,6 +36,14 @@ module WhosGotDirt
36
36
  Result.new('Entity', renderer.result(data), self).finalize!
37
37
  end
38
38
  end
39
+
40
+ def success?
41
+ super && Array === parsed_body
42
+ end
43
+
44
+ def error_message
45
+ parsed_body['error']
46
+ end
39
47
  end
40
48
  end
41
49
  end
@@ -8,11 +8,11 @@ module WhosGotDirt
8
8
  @template = {
9
9
  '@type' => 'Entity',
10
10
  'name' => '/alias',
11
+ 'description' => '/shortBio',
11
12
  'identifiers' => [{
12
13
  'identifier' => '/id',
13
14
  'scheme' => 'Poderopedia',
14
15
  }],
15
- 'description' => '/shortBio'
16
16
  }
17
17
 
18
18
  # Parses the response body.
@@ -6,6 +6,20 @@ module WhosGotDirt
6
6
  # @!attribute [r] count_field
7
7
  # @return [Hash] the field storing the number of results
8
8
  attr_reader :count_field
9
+
10
+ # @private
11
+ def date_formatter(property, path)
12
+ return lambda{|data|
13
+ [property, JsonPointer.new(data, path).value.sub(' ', 'T') + 'Z']
14
+ }
15
+ end
16
+
17
+ # @private
18
+ def integer_formatter(property, path)
19
+ return lambda{|data|
20
+ [property, Integer(JsonPointer.new(data, path).value)]
21
+ }
22
+ end
9
23
  end
10
24
 
11
25
  # Parses the response body.
@@ -28,6 +42,11 @@ module WhosGotDirt
28
42
  def page
29
43
  Integer(parsed_body['Meta']['Parameters']['page'] || 1)
30
44
  end
45
+
46
+ # Returns the error message.
47
+ def error_message
48
+ parsed_body.strip
49
+ end
31
50
  end
32
51
  end
33
52
  end
@@ -11,7 +11,7 @@ module WhosGotDirt
11
11
  '@type' => 'List',
12
12
  'name' => '/name',
13
13
  'description' => '/description',
14
- 'number_of_items' => '/num_entities',
14
+ 'number_of_items' => integer_formatter('number_of_items', '/num_entities'),
15
15
  'item_list_order' => lambda{|data|
16
16
  v = JsonPointer.new(data, '/is_ranked').value
17
17
  if v == '1'
@@ -21,11 +21,11 @@ module WhosGotDirt
21
21
  end
22
22
  ['item_list_order', v]
23
23
  },
24
- 'updated_at' => '/updated_at',
25
24
  'identifiers' => [{
26
25
  'identifier' => '/id',
27
26
  'scheme' => 'LittleSis',
28
27
  }],
28
+ 'updated_at' => date_formatter('updated_at', '/updated_at'),
29
29
  }
30
30
 
31
31
  # Transforms the parsed response body into results.
@@ -8,8 +8,6 @@ module WhosGotDirt
8
8
  @template = {
9
9
  '@type' => 'List',
10
10
  'name' => '/name',
11
- 'created_at' => '/created_at',
12
- 'updated_at' => '/updated_at',
13
11
  'identifiers' => [{
14
12
  'identifier' => '/wikipedia_id',
15
13
  'scheme' => 'Wikipedia',
@@ -18,6 +16,8 @@ module WhosGotDirt
18
16
  'url' => '/opencorporates_url',
19
17
  'note' => 'OpenCorporates page',
20
18
  }],
19
+ 'created_at' => '/created_at',
20
+ 'updated_at' => '/updated_at',
21
21
  }
22
22
 
23
23
  # Transforms the parsed response body into results.
@@ -5,12 +5,23 @@ module WhosGotDirt
5
5
  #
6
6
  # @see http://openoil.net/openoil-api/
7
7
  class OpenOil < Response
8
+ class << self
9
+ # @private
10
+ def date_formatter(property, path)
11
+ return lambda{|data|
12
+ v = JsonPointer.new(data, path).value
13
+ [property, v.empty? ? nil : v + 'T00:00:00Z']
14
+ }
15
+ end
16
+ end
17
+
8
18
  @template = {
9
19
  '@type' => 'Relation',
10
20
  'subject' => lambda{|data|
11
21
  v = JsonPointer.new(data, '/licensees').value
12
22
  ['subject', v.map{|licensee| {'name' => licensee}}]
13
23
  },
24
+ 'name' => '/name',
14
25
  'identifiers' => [{
15
26
  'identifier' => '/identifier',
16
27
  'scheme' => 'OpenOil',
@@ -22,12 +33,11 @@ module WhosGotDirt
22
33
  'url' => '/url_wiki',
23
34
  'note' => 'OpenOil wiki page',
24
35
  }],
25
- 'name' => '/name',
26
- 'created_at' => '/source_date',
27
- 'updated_at' => '/retrieved_date',
28
36
  'sources' => [{
29
37
  'url' => '/source_document',
30
38
  }],
39
+ 'created_at' => date_formatter('created_at', '/source_date'),
40
+ 'updated_at' => date_formatter('updated_at', '/retrieved_date'),
31
41
  # API-specific.
32
42
  'additional_properties' => '/details',
33
43
  'country_code' => '/country',
@@ -49,7 +49,7 @@ module WhosGotDirt
49
49
  def validate!(opts = {})
50
50
  # The code assumes that processing errors in reverse avoids re-indexing
51
51
  # issues when deleting items from arrays. If this assumption is invalid,
52
- # we can delete items on a time and re-validate using this code skeleton:
52
+ # we can delete items one at a time and re-validate using this skeleton:
53
53
  #
54
54
  # begin
55
55
  # Validator.validate(result, type)
@@ -82,7 +82,7 @@ module WhosGotDirt
82
82
  # the source having an integer instead of string value.
83
83
  pointer.value = pointer.value.to_s
84
84
  else
85
- raise ValidationError.new(error.fetch(:message))
85
+ raise ValidationError.new("#{error.fetch(:message)} (#{pointer.value})")
86
86
  end
87
87
  end
88
88
 
@@ -34,7 +34,7 @@ module WhosGotDirt
34
34
  # Memoizes a validator using the schema.
35
35
  def validator
36
36
  # `JSON::Validator#initialize_schema` runs faster if given a `Hash`.
37
- @@validator ||= JSON::Validator.new(JSON.load(File.read(File.expand_path(File.join('..', '..', '..', 'schemas', 'popolo.json'), __FILE__))), {}, {
37
+ @@validator ||= JSON::Validator.new(JSON.load(File.read(File.expand_path(File.join('..', '..', '..', 'schemas', 'schema.json'), __FILE__))), {}, {
38
38
  # Keep the cache - whatever it is.
39
39
  clear_cache: false,
40
40
  # It's safe to skip data parsing if the data is a `Hash`.
@@ -1,3 +1,3 @@
1
1
  module WhosGotDirt
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -0,0 +1,388 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-03/schema#",
3
+ "definitions": {
4
+ "entity": {
5
+ "$schema": "http://json-schema.org/draft-03/schema#",
6
+ "type": "object",
7
+ "properties": {
8
+ "@type": {
9
+ "type": "string",
10
+ "enum": ["Entity"]
11
+ },
12
+ "type": {
13
+ "description": "An entity type, e.g. organization",
14
+ "type": [
15
+ "string",
16
+ "null"
17
+ ]
18
+ },
19
+ "name": {
20
+ "description": "A primary name, e.g. a legally recognized name",
21
+ "type": [
22
+ "string",
23
+ "null"
24
+ ]
25
+ },
26
+ "description": {
27
+ "description": "A description of the entity",
28
+ "type": [
29
+ "string",
30
+ "null"
31
+ ]
32
+ },
33
+ "classification": {
34
+ "description": "An entity category, e.g. committee",
35
+ "type": [
36
+ "string",
37
+ "null"
38
+ ]
39
+ },
40
+ "birth_date": {
41
+ "description": "A date of birth",
42
+ "type": [
43
+ "string",
44
+ "null"
45
+ ],
46
+ "pattern": "^[0-9]{4}(-[0-9]{2}){0,2}$"
47
+ },
48
+ "death_date": {
49
+ "description": "A date of death",
50
+ "type": [
51
+ "string",
52
+ "null"
53
+ ],
54
+ "pattern": "^[0-9]{4}(-[0-9]{2}){0,2}$"
55
+ },
56
+ "founding_date": {
57
+ "description": "A date of founding",
58
+ "type": [
59
+ "string",
60
+ "null"
61
+ ],
62
+ "pattern": "^[0-9]{4}(-[0-9]{2}){0,2}$"
63
+ },
64
+ "dissolution_date": {
65
+ "description": "A date of dissolution",
66
+ "type": [
67
+ "string",
68
+ "null"
69
+ ],
70
+ "pattern": "^[0-9]{4}(-[0-9]{2}){0,2}$"
71
+ },
72
+ "parent_id": {
73
+ "description": "The ID of the organization that contains this organization",
74
+ "type": [
75
+ "string",
76
+ "null"
77
+ ]
78
+ },
79
+ "other_names": {
80
+ "description": "Alternate or former names",
81
+ "type": "array",
82
+ "items": {
83
+ "$ref": "#/definitions/other_name"
84
+ }
85
+ },
86
+ "identifiers": {
87
+ "description": "Issued identifiers",
88
+ "type": "array",
89
+ "items": {
90
+ "$ref": "#/definitions/identifier"
91
+ }
92
+ },
93
+ "contact_details": {
94
+ "description": "Means of contacting the entity",
95
+ "type": "array",
96
+ "items": {
97
+ "$ref": "#/definitions/contact_detail"
98
+ }
99
+ },
100
+ "links": {
101
+ "description": "URLs to documents about the entity",
102
+ "type": "array",
103
+ "items": {
104
+ "$ref": "#/definitions/link"
105
+ }
106
+ },
107
+ "sources": {
108
+ "description": "URLs to documents from which the resource is derived",
109
+ "type": "array",
110
+ "items": {
111
+ "$ref": "#/definitions/link"
112
+ }
113
+ },
114
+ "created_at": {
115
+ "description": "The time at which the resource was created",
116
+ "type": [
117
+ "string",
118
+ "null"
119
+ ],
120
+ "format": "date-time"
121
+ },
122
+ "updated_at": {
123
+ "description": "The time at which the resource was last modified",
124
+ "type": [
125
+ "string",
126
+ "null"
127
+ ],
128
+ "format": "date-time"
129
+ }
130
+ }
131
+ },
132
+ "relation": {
133
+ "$schema": "http://json-schema.org/draft-03/schema#",
134
+ "type": "object",
135
+ "properties": {
136
+ "@type": {
137
+ "type": "string",
138
+ "enum": ["Relation"]
139
+ },
140
+ "subject": {
141
+ "description": "The subject of the relation",
142
+ "type": "array",
143
+ "items": {
144
+ "$ref": "#/definitions/entity"
145
+ }
146
+ },
147
+ "object": {
148
+ "description": "The object of the relation",
149
+ "$ref": "#/definitions/entity"
150
+ },
151
+ "name": {
152
+ "description": "The name of the relation",
153
+ "type": [
154
+ "string",
155
+ "null"
156
+ ]
157
+ },
158
+ "start_date": {
159
+ "description": "The date on which the relationship began",
160
+ "type": [
161
+ "string",
162
+ "null"
163
+ ],
164
+ "pattern": "^[0-9]{4}((-[0-9]{2}){0,2}|(-[0-9]{2}){2}(T[0-9]{2}(:[0-9]{2}(:[0-9]{2})?)?Z)?)$"
165
+ },
166
+ "end_date": {
167
+ "description": "The date on which the relationship ended",
168
+ "type": [
169
+ "string",
170
+ "null"
171
+ ],
172
+ "pattern": "^[0-9]{4}((-[0-9]{2}){0,2}|(-[0-9]{2}){2}(T[0-9]{2}(:[0-9]{2}(:[0-9]{2})?)?Z)?)$"
173
+ },
174
+ "identifiers": {
175
+ "description": "Issued identifiers",
176
+ "type": "array",
177
+ "items": {
178
+ "$ref": "#/definitions/identifier"
179
+ }
180
+ },
181
+ "links": {
182
+ "description": "URLs to documents about the list",
183
+ "type": "array",
184
+ "items": {
185
+ "$ref": "#/definitions/link"
186
+ }
187
+ },
188
+ "sources": {
189
+ "description": "URLs to documents from which the resource is derived",
190
+ "type": "array",
191
+ "items": {
192
+ "$ref": "#/definitions/link"
193
+ }
194
+ },
195
+ "created_at": {
196
+ "description": "The time at which the resource was created",
197
+ "type": [
198
+ "string",
199
+ "null"
200
+ ],
201
+ "format": "date-time"
202
+ },
203
+ "updated_at": {
204
+ "description": "The time at which the resource was last modified",
205
+ "type": [
206
+ "string",
207
+ "null"
208
+ ],
209
+ "format": "date-time"
210
+ }
211
+ }
212
+ },
213
+ "list": {
214
+ "$schema": "http://json-schema.org/draft-03/schema#",
215
+ "type": "object",
216
+ "properties": {
217
+ "@type": {
218
+ "type": "string",
219
+ "enum": ["List"]
220
+ },
221
+ "name": {
222
+ "description": "The name of the list",
223
+ "type": [
224
+ "string",
225
+ "null"
226
+ ]
227
+ },
228
+ "description": {
229
+ "description": "A description of the list",
230
+ "type": [
231
+ "string",
232
+ "null"
233
+ ]
234
+ },
235
+ "number_of_items": {
236
+ "description": "The number of items in the list",
237
+ "type": [
238
+ "integer",
239
+ "null"
240
+ ]
241
+ },
242
+ "item_list_order": {
243
+ "description": "The type of ordering (e.g. ascending, descending, unordered)",
244
+ "type": [
245
+ "string",
246
+ "null"
247
+ ],
248
+ "enum": [
249
+ "ascending",
250
+ "descending",
251
+ "unordered"
252
+ ]
253
+ },
254
+ "identifiers": {
255
+ "description": "Issued identifiers",
256
+ "type": "array",
257
+ "items": {
258
+ "$ref": "#/definitions/identifier"
259
+ }
260
+ },
261
+ "links": {
262
+ "description": "URLs to documents about the list",
263
+ "type": "array",
264
+ "items": {
265
+ "$ref": "#/definitions/link"
266
+ }
267
+ },
268
+ "sources": {
269
+ "description": "URLs to documents from which the resource is derived",
270
+ "type": "array",
271
+ "items": {
272
+ "$ref": "#/definitions/link"
273
+ }
274
+ },
275
+ "created_at": {
276
+ "description": "The time at which the resource was created",
277
+ "type": [
278
+ "string",
279
+ "null"
280
+ ],
281
+ "format": "date-time"
282
+ },
283
+ "updated_at": {
284
+ "description": "The time at which the resource was last modified",
285
+ "type": [
286
+ "string",
287
+ "null"
288
+ ],
289
+ "format": "date-time"
290
+ }
291
+ }
292
+ },
293
+ "other_name": {
294
+ "$schema": "http://json-schema.org/draft-03/schema#",
295
+ "title": "Other name",
296
+ "description": "An alternate or former name",
297
+ "type": "object",
298
+ "properties": {
299
+ "name": {
300
+ "description": "An alternate or former name",
301
+ "type": "string"
302
+ },
303
+ "start_date": {
304
+ "description": "The date on which the name was adopted",
305
+ "type": [
306
+ "string",
307
+ "null"
308
+ ],
309
+ "pattern": "^[0-9]{4}(-[0-9]{2}){0,2}$"
310
+ },
311
+ "end_date": {
312
+ "description": "The date on which the name was abandoned",
313
+ "type": [
314
+ "string",
315
+ "null"
316
+ ],
317
+ "pattern": "^[0-9]{4}(-[0-9]{2}){0,2}$"
318
+ },
319
+ "note": {
320
+ "description": "A note, e.g. 'Birth name'",
321
+ "type": [
322
+ "string",
323
+ "null"
324
+ ]
325
+ }
326
+ }
327
+ },
328
+ "identifier": {
329
+ "$schema": "http://json-schema.org/draft-03/schema#",
330
+ "title": "Identifier",
331
+ "description": "An issued identifier",
332
+ "type": "object",
333
+ "properties": {
334
+ "identifier": {
335
+ "description": "An issued identifier, e.g. a DUNS number",
336
+ "type": "string",
337
+ "required": true
338
+ },
339
+ "scheme": {
340
+ "description": "An identifier scheme, e.g. DUNS",
341
+ "type": [
342
+ "string",
343
+ "null"
344
+ ]
345
+ }
346
+ }
347
+ },
348
+ "contact_detail": {
349
+ "$schema": "http://json-schema.org/draft-03/schema#",
350
+ "title": "Contact detail",
351
+ "description": "A means of contacting an entity",
352
+ "type": "object",
353
+ "properties": {
354
+ "type": {
355
+ "description": "A type of medium, e.g. 'fax' or 'email'",
356
+ "type": "string",
357
+ "required": true
358
+ },
359
+ "value": {
360
+ "description": "A value, e.g. a phone number or email address",
361
+ "type": "string",
362
+ "required": true
363
+ }
364
+ }
365
+ },
366
+ "link": {
367
+ "$schema": "http://json-schema.org/draft-03/schema#",
368
+ "title": "Link",
369
+ "description": "A URL",
370
+ "type": "object",
371
+ "properties": {
372
+ "url": {
373
+ "description": "A URL",
374
+ "type": "string",
375
+ "format": "uri",
376
+ "required": true
377
+ },
378
+ "note": {
379
+ "description": "A note, e.g. 'Wikipedia page'",
380
+ "type": [
381
+ "string",
382
+ "null"
383
+ ]
384
+ }
385
+ }
386
+ }
387
+ }
388
+ }