whos_got_dirt 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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
+ }