fdp_client 0.0.2
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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +13 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +60 -0
- data/LICENSE.txt +21 -0
- data/README.md +33 -0
- data/Rakefile +12 -0
- data/fdp_client_ruby.gemspec +33 -0
- data/lib/fdp_client/GUI/access_service.rb +53 -0
- data/lib/fdp_client/GUI/catalog.rb +56 -0
- data/lib/fdp_client/GUI/dataset.rb +59 -0
- data/lib/fdp_client/GUI/distribution.rb +55 -0
- data/lib/fdp_client/GUI/resource.rb +302 -0
- data/lib/fdp_client/GUI/version.rb +7 -0
- data/lib/fdp_client/fdp_client.rb +223 -0
- data/lib/fdp_client/fdp_resource.rb +366 -0
- data/lib/fdp_client/fdp_schema.rb +293 -0
- data/lib/fdp_client.rb +10 -0
- metadata +62 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
require "linkeddata"
|
|
2
|
+
require "rest-client"
|
|
3
|
+
|
|
4
|
+
module FDPMate
|
|
5
|
+
DCAT = RDF::Vocabulary.new("http://www.w3.org/ns/dcat#")
|
|
6
|
+
FOAF = RDF::Vocabulary.new("http://xmlns.com/foaf/0.1/")
|
|
7
|
+
BS = RDF::Vocabulary.new("http://rdf.biosemantics.org/ontologies/fdp-o#")
|
|
8
|
+
|
|
9
|
+
class DCATResource
|
|
10
|
+
attr_accessor :baseURI, :parentURI, :serverURL, :accessRights,
|
|
11
|
+
:conformsTo, :contactName, :contactEmail, :creator,
|
|
12
|
+
:creatorName, :title, :description, :issued, :modified,
|
|
13
|
+
:hasVersion, :publisher, :identifier, :license, :language,
|
|
14
|
+
:dataset, :keyword, :landingPage, :qualifiedRelation,
|
|
15
|
+
:theme, :service, :themeTaxonomy, :homepage, :types, :g # the graph
|
|
16
|
+
|
|
17
|
+
def initialize(types: [DCAT.Resource], baseURI: nil, parentURI: nil,
|
|
18
|
+
accessRights: nil, conformsTo: nil, contactEmail: nil, contactName: nil, creator: nil, creatorName: nil,
|
|
19
|
+
title: nil, description: nil, issued: nil, modified: nil, hasVersion: nil, publisher: nil,
|
|
20
|
+
identifier: nil, license: nil, language: "http://id.loc.gov/vocabulary/iso639-1/en",
|
|
21
|
+
dataset: nil, keyword: nil, landingPage: nil, qualifiedRelation: nil, theme: nil,
|
|
22
|
+
service: nil, themeTaxonomy: nil, homepage: nil, serverURL: "http://localhost:7070",
|
|
23
|
+
**_args)
|
|
24
|
+
|
|
25
|
+
@accessRights = accessRights
|
|
26
|
+
@conformsTo = conformsTo
|
|
27
|
+
@contactName = contactName
|
|
28
|
+
@contactEmail = contactEmail
|
|
29
|
+
@creator = creator
|
|
30
|
+
@creatorName = creatorName
|
|
31
|
+
@title = title
|
|
32
|
+
@description = description
|
|
33
|
+
@issued = issued
|
|
34
|
+
@modified = modified
|
|
35
|
+
@hasVersion = hasVersion
|
|
36
|
+
@publisher = publisher
|
|
37
|
+
@identifier = identifier
|
|
38
|
+
@license = license
|
|
39
|
+
@language = language
|
|
40
|
+
|
|
41
|
+
@dataset = dataset
|
|
42
|
+
@keyword = keyword
|
|
43
|
+
@landingPage = landingPage
|
|
44
|
+
@qualifiedRelation = qualifiedRelation
|
|
45
|
+
@theme = theme
|
|
46
|
+
@service = service # this is now defunct, I think
|
|
47
|
+
@themeTaxonomy = themeTaxonomy
|
|
48
|
+
@homepage = homepage
|
|
49
|
+
|
|
50
|
+
@serverURL = RDF::URI(serverURL)
|
|
51
|
+
@baseURI = RDF::URI(baseURI)
|
|
52
|
+
@parentURI = RDF::URI(parentURI)
|
|
53
|
+
@types = types
|
|
54
|
+
|
|
55
|
+
abort "you must set baseURI and serverURL parameters" unless self.baseURI and self.serverURL
|
|
56
|
+
|
|
57
|
+
set_headers
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def set_headers
|
|
61
|
+
return if $headers
|
|
62
|
+
|
|
63
|
+
puts ENV.fetch("FDPUSER", nil)
|
|
64
|
+
puts ENV.fetch("FDPPASS", nil)
|
|
65
|
+
payload = '{ "email": "' + ENV.fetch("FDPUSER", nil) + '", "password": "' + ENV.fetch("FDPPASS", nil) + '" }'
|
|
66
|
+
warn "#{serverURL}/tokens", payload
|
|
67
|
+
resp = RestClient.post("#{serverURL}/tokens", payload, headers = { content_type: "application/json" })
|
|
68
|
+
$token = JSON.parse(resp.body)["token"]
|
|
69
|
+
puts $token
|
|
70
|
+
$headers = { content_type: "text/turtle", authorization: "Bearer #{$token}", accept: "text/turtle" }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def build
|
|
74
|
+
@g = RDF::Graph.new # reset graph
|
|
75
|
+
abort "an identifier has not been set" unless identifier
|
|
76
|
+
types.each do |type|
|
|
77
|
+
g << [identifier, RDF.type, type]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
g << [identifier, RDF::Vocab::RDFS.label, @title] if @title
|
|
81
|
+
g << [identifier, RDF::Vocab::DC.isPartOf, @parentURI] if @parentURI
|
|
82
|
+
|
|
83
|
+
# DCAT
|
|
84
|
+
%w[landingPage qualifiedRelation themeTaxonomy endpointURL endpointDescription].each do |f|
|
|
85
|
+
(pred, value) = get_pred_value(f, "DCAT")
|
|
86
|
+
next unless pred and value
|
|
87
|
+
|
|
88
|
+
g << [identifier, pred, value]
|
|
89
|
+
end
|
|
90
|
+
# DCAT Multi-value
|
|
91
|
+
%w[keyword].each do |f|
|
|
92
|
+
(pred, value) = get_pred_value(f, "DCAT") # value is a comma-separated string
|
|
93
|
+
next unless pred and value
|
|
94
|
+
|
|
95
|
+
keywords = value.split(",")
|
|
96
|
+
keywords.each do |kw|
|
|
97
|
+
kw.strip!
|
|
98
|
+
next if kw.empty?
|
|
99
|
+
|
|
100
|
+
g << [identifier, pred, kw]
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# DCT
|
|
105
|
+
%w[accessRights hasVersion conformsTo title description identifier license language creator].each do |f|
|
|
106
|
+
(pred, value) = get_pred_value(f, "DCT")
|
|
107
|
+
next unless pred and value
|
|
108
|
+
|
|
109
|
+
g << [identifier, pred, value]
|
|
110
|
+
end
|
|
111
|
+
%w[issued modified].each do |f|
|
|
112
|
+
warn "doing issued modified #{f}"
|
|
113
|
+
(pred, value) = get_pred_value(f, "DCT", "TIME")
|
|
114
|
+
next unless pred and value
|
|
115
|
+
|
|
116
|
+
g << [identifier, pred, value]
|
|
117
|
+
g << [identifier, BS.issued, value]
|
|
118
|
+
g << [identifier, BS.modified, value]
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# FOAF
|
|
122
|
+
%w[homepage].each do |f|
|
|
123
|
+
(pred, value) = get_pred_value(f, "FOAF")
|
|
124
|
+
next unless pred and value
|
|
125
|
+
|
|
126
|
+
g << [identifier, pred, value]
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# COMPLEX
|
|
130
|
+
|
|
131
|
+
# identifier
|
|
132
|
+
# contactPoint
|
|
133
|
+
if contactEmail or contactName
|
|
134
|
+
bnode = RDF::URI.new(identifier.to_s + "#contact")
|
|
135
|
+
g << [identifier, DCAT.contactPoint, bnode]
|
|
136
|
+
g << [bnode, RDF.type, RDF::URI.new("http://www.w3.org/2006/vcard/ns#Individual")]
|
|
137
|
+
g << [bnode, RDF::URI.new("http://www.w3.org/2006/vcard/ns#fn"), contactName] if contactName
|
|
138
|
+
g << [bnode, RDF::URI.new("http://www.w3.org/2006/vcard/ns#hasEmail"), contactEmail] if contactEmail
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# publisher
|
|
142
|
+
if publisher
|
|
143
|
+
bnode = RDF::Node.new
|
|
144
|
+
g << [identifier, RDF::Vocab::DC.publisher, bnode]
|
|
145
|
+
g << [bnode, RDF.type, FOAF.Agent]
|
|
146
|
+
g << [bnode, FOAF.name, publisher]
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# creator
|
|
150
|
+
if creator
|
|
151
|
+
g << [identifier, RDF::Vocab::DC.creator, RDF::URI.new(creator)]
|
|
152
|
+
g << [RDF::URI.new(creator), RDF.type, FOAF.Agent]
|
|
153
|
+
g << [RDF::URI.new(creator), FOAF.name, creatorName] if creatorName
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# accessRights
|
|
157
|
+
if accessRights
|
|
158
|
+
g << [identifier, RDF::Vocab::DC.accessRights, RDF::URI.new(accessRights)]
|
|
159
|
+
g << [RDF::URI.new(accessRights), RDF.type, RDF::Vocab::DC.RightsStatement]
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# dataService
|
|
163
|
+
if is_a? DCATDataService
|
|
164
|
+
warn inspect
|
|
165
|
+
warn "serializing data service #{endpointDescription} or #{endpointURL}"
|
|
166
|
+
if endpointDescription or endpointURL
|
|
167
|
+
warn "serializing ENDPOINTS"
|
|
168
|
+
bnode = RDF::Node.new
|
|
169
|
+
g << [identifier, DCAT.accessService, bnode]
|
|
170
|
+
g << [bnode, RDF.type, DCAT.dataService]
|
|
171
|
+
if endpointDescription
|
|
172
|
+
g << [bnode, DCAT.endpointDescription,
|
|
173
|
+
RDF::URI.new(endpointDescription)]
|
|
174
|
+
end
|
|
175
|
+
g << [bnode, DCAT.endpointURL, RDF::URI.new(endpointURL)] if endpointURL
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# mediaType or format https://www.iana.org/assignments/media-types/application/3gppHalForms+json
|
|
180
|
+
if is_a? DCATDistribution
|
|
181
|
+
if mediaType
|
|
182
|
+
# CHANGE THIS BACK WHEN FDP SHACL validation is correct
|
|
183
|
+
# type = "https://www.iana.org/assignments/media-types/" + self.mediaType
|
|
184
|
+
# type = RDF::URI.new(type)
|
|
185
|
+
type = mediaType
|
|
186
|
+
g << [identifier, DCAT.mediaType, type]
|
|
187
|
+
# CHANGE THIS BACK ALSO!
|
|
188
|
+
# self.g << [type, RDF.type, RDF::Vocab::DC.MediaType]
|
|
189
|
+
end
|
|
190
|
+
if self.format
|
|
191
|
+
type = RDF::URI.new(self.format)
|
|
192
|
+
g << [identifier, RDF::Vocab::DC.format, type]
|
|
193
|
+
g << [type, RDF.type, RDF::Vocab::DC.MediaTypeOrExtent]
|
|
194
|
+
end
|
|
195
|
+
# conformsTo
|
|
196
|
+
if conformsTo
|
|
197
|
+
schema = RDF::URI.new(conformsTo)
|
|
198
|
+
g << [identifier, RDF::Vocab::DC.conformsTo, schema]
|
|
199
|
+
g << [schema, RDF.type, RDF::Vocab::DC.Standard]
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# catalog dataset distribution
|
|
205
|
+
if is_a? DCATCatalog and !datasets.empty?
|
|
206
|
+
datasets.each do |d|
|
|
207
|
+
g << [identifier, DCAT.dataset, RDF::URI.new(d.identifier)]
|
|
208
|
+
end
|
|
209
|
+
elsif is_a? DCATCatalog and !accessServices.empty?
|
|
210
|
+
accessServices.each do |d|
|
|
211
|
+
g << [identifier, DCAT.service, RDF::URI.new(d.identifier)]
|
|
212
|
+
end
|
|
213
|
+
elsif is_a? DCATDataset and !distributions.empty?
|
|
214
|
+
distributions.each do |d|
|
|
215
|
+
g << [identifier, DCAT.distribution, RDF::URI.new(d.identifier)]
|
|
216
|
+
end
|
|
217
|
+
elsif is_a? DCATDistribution and !accessServices.empty?
|
|
218
|
+
accessServices.each do |d|
|
|
219
|
+
g << [identifier, DCAT.accessService, RDF::URI.new(d.identifier)]
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# theme
|
|
224
|
+
return unless theme
|
|
225
|
+
|
|
226
|
+
themes = theme.split(",").filter_map { |url| url.strip unless url.strip.empty? }
|
|
227
|
+
themes.each do |theme|
|
|
228
|
+
g << [identifier, DCAT.theme, RDF::URI.new(theme)]
|
|
229
|
+
g << [RDF::URI.new(theme), RDF.type, RDF::Vocab::SKOS.Concept]
|
|
230
|
+
g << [RDF::URI.new(theme), RDF::Vocab::SKOS.inScheme,
|
|
231
|
+
RDF::URI.new(identifier.to_s + "#conceptscheme")]
|
|
232
|
+
end
|
|
233
|
+
g << [RDF::URI.new(identifier.to_s + "#conceptscheme"), RDF.type,
|
|
234
|
+
RDF::Vocab::SKOS.ConceptScheme]
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def serialize(format: :turtle)
|
|
238
|
+
@g.dump(:turtle)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def publish
|
|
242
|
+
location = identifier.to_s.gsub(baseURI, serverURL)
|
|
243
|
+
begin
|
|
244
|
+
resp = RestClient.put("#{location}/meta/state", '{ "current": "PUBLISHED" }',
|
|
245
|
+
headers = { authorization: "Bearer #{$token}", content_type: "application/json" })
|
|
246
|
+
warn "publish response message"
|
|
247
|
+
warn resp.inspect
|
|
248
|
+
rescue StandardError
|
|
249
|
+
warn "ERROR in publishing"
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def get_pred_value(pred, vocab, datatype = nil)
|
|
254
|
+
# $stderr.puts "getting #{pred}, #{vocab}"
|
|
255
|
+
urire = Regexp.new("((http|https)://)(www.)?[a-zA-Z0-9@:%._\\+~#?&//=]{2,256}\\.[a-z]{2,8}\\b([-a-zA-Z0-9@:%._\\+~#?&//=]*)")
|
|
256
|
+
sym = "@" + pred
|
|
257
|
+
# $stderr.puts "getting #{pred}, #{sym}..."
|
|
258
|
+
case vocab
|
|
259
|
+
when "DCT"
|
|
260
|
+
pred = RDF::Vocab::DC[pred]
|
|
261
|
+
when "DCAT"
|
|
262
|
+
pred = DCAT[pred]
|
|
263
|
+
when "FOAF"
|
|
264
|
+
pred = FOAF[pred]
|
|
265
|
+
end
|
|
266
|
+
# $stderr.puts "got #{pred}, #{vocab}"
|
|
267
|
+
|
|
268
|
+
value = instance_variable_get(sym).to_s
|
|
269
|
+
thisvalue = value # temp compy
|
|
270
|
+
# $stderr.puts "got2 #{pred}, #{value}"
|
|
271
|
+
|
|
272
|
+
if datatype == "TIME"
|
|
273
|
+
now = Time.now.strftime("%Y-%m-%dT%H:%M:%S.%L")
|
|
274
|
+
value = RDF::Literal.new(thisvalue, datatype: RDF::URI("http://www.w3.org/2001/XMLSchema#dateTime"))
|
|
275
|
+
warn "time value1 #{value}"
|
|
276
|
+
unless value.valid?
|
|
277
|
+
thisvalue += "T12:00+01:00" # make a guess that they only provided the date
|
|
278
|
+
value = RDF::Literal.new(thisvalue, datatype: RDF::URI("http://www.w3.org/2001/XMLSchema#dateTime"))
|
|
279
|
+
warn "time value2 #{value}"
|
|
280
|
+
unless value.valid?
|
|
281
|
+
value = RDF::Literal.new(now, datatype: RDF::URI("http://www.w3.org/2001/XMLSchema#dateTime"))
|
|
282
|
+
warn "time value3 #{value}"
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
elsif urire.match(thisvalue)
|
|
286
|
+
value = RDF::URI.new(thisvalue)
|
|
287
|
+
end
|
|
288
|
+
return [nil, nil] if value.to_s.empty?
|
|
289
|
+
|
|
290
|
+
warn "returning #{pred}, #{value}"
|
|
291
|
+
[pred, value]
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# %w() array of strings
|
|
296
|
+
# %r() regular expression.
|
|
297
|
+
# %q() string
|
|
298
|
+
# %x() a shell command (returning the output string)
|
|
299
|
+
# %i() array of symbols (Ruby >= 2.0.0)
|
|
300
|
+
# %s() symbol
|
|
301
|
+
# %() (without letter) shortcut for %Q()
|
|
302
|
+
end
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rest-client"
|
|
4
|
+
require "json"
|
|
5
|
+
|
|
6
|
+
module FDP
|
|
7
|
+
##
|
|
8
|
+
# Client for interacting with a FAIR Data Point (FDP) reference implementation server.
|
|
9
|
+
#
|
|
10
|
+
# This class handles authentication (token-based) and provides methods for
|
|
11
|
+
# discovering and managing metadata schemas (SHACL shapes) and resource definitions.
|
|
12
|
+
#
|
|
13
|
+
# == Authentication
|
|
14
|
+
#
|
|
15
|
+
# The client obtains a JWT bearer token via the +/tokens+ endpoint on initialization.
|
|
16
|
+
# All subsequent requests use this token in the +Authorization+ header.
|
|
17
|
+
#
|
|
18
|
+
# == Usage example
|
|
19
|
+
#
|
|
20
|
+
# client = FDP::Client.new(
|
|
21
|
+
# base_url: "https://example.com/fdp",
|
|
22
|
+
# email: "user@example.com",
|
|
23
|
+
# password: "secret123"
|
|
24
|
+
# )
|
|
25
|
+
#
|
|
26
|
+
# schemas = client.retrieve_current_schemas
|
|
27
|
+
# resources = client.retrieve_current_resources
|
|
28
|
+
#
|
|
29
|
+
class Client
|
|
30
|
+
# @return [String] Base URL of the FAIR Data Point server (without trailing slash preferred)
|
|
31
|
+
attr_accessor :base_url
|
|
32
|
+
|
|
33
|
+
# @return [String] Email used for authentication
|
|
34
|
+
attr_accessor :email
|
|
35
|
+
|
|
36
|
+
# @return [String] Password used for authentication (stored only temporarily)
|
|
37
|
+
attr_accessor :password
|
|
38
|
+
|
|
39
|
+
# @return [String] JWT bearer token obtained after successful login
|
|
40
|
+
attr_accessor :token
|
|
41
|
+
|
|
42
|
+
# @return [Hash] Default headers used in all authenticated API requests
|
|
43
|
+
attr_accessor :headers
|
|
44
|
+
|
|
45
|
+
##
|
|
46
|
+
# Initializes a new FDP client and authenticates against the server.
|
|
47
|
+
#
|
|
48
|
+
# @param base_url [String] URL of the FAIR Data Point server (e.g. https://fdp.example.com)
|
|
49
|
+
# @param email [String] User email for authentication (default: albert.einstein@example.com)
|
|
50
|
+
# @param password [String] User password for authentication (default: password)
|
|
51
|
+
#
|
|
52
|
+
# @raise [SystemExit] if authentication fails or unexpected error occurs
|
|
53
|
+
#
|
|
54
|
+
def initialize(base_url:, email: "albert.einstein@example.com", password: "password")
|
|
55
|
+
@base_url = base_url
|
|
56
|
+
@email = email
|
|
57
|
+
@password = password
|
|
58
|
+
|
|
59
|
+
begin
|
|
60
|
+
response = RestClient.post(
|
|
61
|
+
"#{base_url}/tokens",
|
|
62
|
+
{ email: email, password: password }.to_json,
|
|
63
|
+
content_type: :json, accept: :json
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
token_data = JSON.parse(response.body)
|
|
67
|
+
@token = token_data["token"]
|
|
68
|
+
|
|
69
|
+
warn "Authorization: Bearer #{@token}"
|
|
70
|
+
rescue RestClient::ExceptionWithResponse => e
|
|
71
|
+
warn "Error getting token:"
|
|
72
|
+
warn "Status: #{e.response.code}"
|
|
73
|
+
warn "Body: #{e.response.body}"
|
|
74
|
+
abort
|
|
75
|
+
rescue StandardError => e
|
|
76
|
+
warn "Unexpected error: #{e.message}"
|
|
77
|
+
abort
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
@headers = {
|
|
81
|
+
Authorization: "Bearer #{@token}",
|
|
82
|
+
accept: :json,
|
|
83
|
+
content_type: :json
|
|
84
|
+
}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
##
|
|
88
|
+
# Fetches a simple name → {uuid, definition} lookup of currently defined metadata schemas.
|
|
89
|
+
#
|
|
90
|
+
# @return [Hash<String, Hash>] schema name → { 'uuid' => ..., 'definition' => ... }
|
|
91
|
+
# @return [Hash] empty hash when request fails
|
|
92
|
+
#
|
|
93
|
+
def list_current_schemas
|
|
94
|
+
begin
|
|
95
|
+
response = RestClient.get("#{base_url}/metadata-schemas", headers)
|
|
96
|
+
rescue RestClient::ExceptionWithResponse => e
|
|
97
|
+
warn "Error fetching schemas:"
|
|
98
|
+
warn "Status: #{e.response.code}"
|
|
99
|
+
warn "Body: #{e.response.body}"
|
|
100
|
+
return {}
|
|
101
|
+
rescue StandardError => e
|
|
102
|
+
warn "Unexpected error: #{e.message}"
|
|
103
|
+
return {}
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
j = JSON.parse(response.body)
|
|
107
|
+
uuids = {}
|
|
108
|
+
|
|
109
|
+
j.each do |entry|
|
|
110
|
+
uuids[entry["name"]] = {
|
|
111
|
+
"uuid" => entry["uuid"],
|
|
112
|
+
"definition" => entry["latest"]["definition"]
|
|
113
|
+
}
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
uuids
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
##
|
|
120
|
+
# Retrieves all currently defined metadata schemas as rich {FDP::Schema} objects.
|
|
121
|
+
#
|
|
122
|
+
# This method is usually preferred over #list_current_schemas when you need
|
|
123
|
+
# full metadata shape information (parents, prefix, target classes, etc.).
|
|
124
|
+
#
|
|
125
|
+
# @return [Array<FDP::Schema>] array of schema objects
|
|
126
|
+
# @return [Array] empty array when request fails
|
|
127
|
+
#
|
|
128
|
+
def retrieve_current_schemas
|
|
129
|
+
begin
|
|
130
|
+
response = RestClient.get("#{base_url}/metadata-schemas", headers)
|
|
131
|
+
rescue RestClient::ExceptionWithResponse => e
|
|
132
|
+
warn "Error fetching schemas:"
|
|
133
|
+
warn "Status: #{e.response.code}"
|
|
134
|
+
warn "Body: #{e.response.body}"
|
|
135
|
+
return []
|
|
136
|
+
rescue StandardError => e
|
|
137
|
+
warn "Unexpected error: #{e.message}"
|
|
138
|
+
return []
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
j = JSON.parse(response.body)
|
|
142
|
+
schemas = []
|
|
143
|
+
|
|
144
|
+
j.each do |entry|
|
|
145
|
+
latest = entry["latest"] || {}
|
|
146
|
+
|
|
147
|
+
schemas << FDP::Schema.new(
|
|
148
|
+
client: self,
|
|
149
|
+
uuid: entry["uuid"],
|
|
150
|
+
name: entry["name"],
|
|
151
|
+
label: latest["suggestedResourceName"],
|
|
152
|
+
description: latest["description"],
|
|
153
|
+
definition: latest["definition"],
|
|
154
|
+
prefix: latest["suggestedUrlPrefix"],
|
|
155
|
+
parents: latest["extendsSchemaUuids"] || [],
|
|
156
|
+
children: latest["childSchemaUuids"] || [],
|
|
157
|
+
version: latest["version"] || "1.0.0",
|
|
158
|
+
targetclasses: latest["targetClassUris"] || ["http://www.w3.org/ns/dcat#Resource"]
|
|
159
|
+
)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
schemas
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
##
|
|
166
|
+
# Fetches a simple name → uuid lookup of currently defined resource definitions.
|
|
167
|
+
#
|
|
168
|
+
# @return [Hash<String, Hash>] resource name → { 'uuid' => ... }
|
|
169
|
+
# @return [Hash] empty hash when request fails
|
|
170
|
+
#
|
|
171
|
+
def list_current_resources
|
|
172
|
+
begin
|
|
173
|
+
response = RestClient.get("#{base_url}/resource-definitions", headers)
|
|
174
|
+
rescue RestClient::ExceptionWithResponse => e
|
|
175
|
+
warn "Error fetching resources definitions:"
|
|
176
|
+
warn "Status: #{e.response.code}"
|
|
177
|
+
warn "Body: #{e.response.body}"
|
|
178
|
+
return {}
|
|
179
|
+
rescue StandardError => e
|
|
180
|
+
warn "Unexpected error: #{e.message}"
|
|
181
|
+
return {}
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
j = JSON.parse(response.body)
|
|
185
|
+
uuids = {}
|
|
186
|
+
|
|
187
|
+
j.each do |entry|
|
|
188
|
+
uuids[entry["name"]] = { "uuid" => entry["uuid"] }
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
uuids
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
##
|
|
195
|
+
# Retrieves all currently defined resource definitions as rich {FDP::Resource} objects.
|
|
196
|
+
#
|
|
197
|
+
# @return [Array<FDP::Resource>] array of resource definition objects
|
|
198
|
+
# @return [Array] empty array when request fails
|
|
199
|
+
#
|
|
200
|
+
def retrieve_current_resources
|
|
201
|
+
begin
|
|
202
|
+
response = RestClient.get("#{base_url}/resource-definitions", headers)
|
|
203
|
+
rescue RestClient::ExceptionWithResponse => e
|
|
204
|
+
warn "Error fetching resources definitions:"
|
|
205
|
+
warn "Status: #{e.response.code}"
|
|
206
|
+
warn "Body: #{e.response.body}"
|
|
207
|
+
return []
|
|
208
|
+
rescue StandardError => e
|
|
209
|
+
warn "Unexpected error: #{e.message}"
|
|
210
|
+
return []
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
j = JSON.parse(response.body)
|
|
214
|
+
resources = []
|
|
215
|
+
|
|
216
|
+
j.each do |entry|
|
|
217
|
+
resources << FDP::Resource.new(resourcejson: entry, client: self)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
resources
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|