relaton-api 3.0.2 → 3.1.0
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 +4 -4
- data/lib/relaton/api/app.rb +0 -1
- data/lib/relaton/api/routes.rb +0 -7
- data/lib/relaton/api/store/file_system.rb +2 -50
- data/lib/relaton/api/version.rb +1 -1
- metadata +2 -3
- data/lib/relaton/api/routes/api_v1.rb +0 -98
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c27bb3c8f9b659ae7c0c760f664b9eb0f8af9de851fe2de5db3bcc09df884820
|
|
4
|
+
data.tar.gz: f79f884937d238bc12e33ba10bbc93a4897ce27da3a3f918406d24321e6bda4b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9b1ddaba4ec35ba34c10c39f103cdd8f3094b25f248e0fe3d3a0b2647fca9bb1ac3d97e7c09fa76a30d79054820403dfb12d61f59bb739bbb6a8a56e07b16a3c
|
|
7
|
+
data.tar.gz: faaf4afa99370b0ae3eaa5d6d2b48761265f6e19da516a4f849f05b410ad6ed47655ce8ac77ecfd67f9543c20d8931461e17308c187975873acb5ba87549d94d
|
data/lib/relaton/api/app.rb
CHANGED
data/lib/relaton/api/routes.rb
CHANGED
|
@@ -4,13 +4,6 @@ module Relaton
|
|
|
4
4
|
module Api
|
|
5
5
|
# Route namespace; children declared here.
|
|
6
6
|
module Routes
|
|
7
|
-
# The /api/v1 contract surface (same paths as api.relaton.org).
|
|
8
|
-
module ApiV1
|
|
9
|
-
autoload :Document, "relaton/api/routes/api_v1"
|
|
10
|
-
autoload :Search, "relaton/api/routes/api_v1"
|
|
11
|
-
autoload :Version, "relaton/api/routes/api_v1"
|
|
12
|
-
end
|
|
13
|
-
|
|
14
7
|
autoload :Version, "relaton/api/routes/version"
|
|
15
8
|
autoload :Documents, "relaton/api/routes/documents"
|
|
16
9
|
autoload :Flavors, "relaton/api/routes/flavors"
|
|
@@ -49,37 +49,8 @@ module Relaton
|
|
|
49
49
|
matches.find { |d| d.year == year.to_i } || best
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
-
#
|
|
53
|
-
# exact-value filters,
|
|
54
|
-
# shape, and facets computed over the whole filtered set.
|
|
55
|
-
def search(q: nil, flavor: nil, doctype: nil, year: nil,
|
|
56
|
-
status: nil, page: 0, size: DEFAULT_SIZE)
|
|
57
|
-
size = DEFAULT_SIZE if size.nil? || size < 1
|
|
58
|
-
size = MAX_SIZE if size > MAX_SIZE
|
|
59
|
-
page = 0 if page.nil? || page.negative?
|
|
60
|
-
|
|
61
|
-
filtered = @docs.select do |d|
|
|
62
|
-
(flavor.nil? || d.stream == flavor) &&
|
|
63
|
-
(doctype.nil? || d.doctype == doctype) &&
|
|
64
|
-
(year.nil? || d.year == year.to_i) &&
|
|
65
|
-
(status.nil? || d.status == status) &&
|
|
66
|
-
(q.nil? || matches_query?(d, q))
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
facets = {
|
|
70
|
-
"flavor" => facet(filtered, :stream),
|
|
71
|
-
"doctype" => facet(filtered, :doctype),
|
|
72
|
-
"status" => facet(filtered, :status),
|
|
73
|
-
}
|
|
74
|
-
items = filtered.sort_by { |d| [d.title.to_s, d.docid.to_s] }
|
|
75
|
-
.drop(page * size)
|
|
76
|
-
.first(size)
|
|
77
|
-
.map { |d| item(d) }
|
|
78
|
-
{ "total" => filtered.size, "page" => page, "size" => size,
|
|
79
|
-
"items" => items, "facets" => facets }
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# v3 collection page: the same filters as #search, docid-ordered,
|
|
52
|
+
# v3 collection page: substring over titles and identifiers,
|
|
53
|
+
# exact-value filters, docid-ordered,
|
|
83
54
|
# paged by cursor (the docid the previous page ended on) instead of
|
|
84
55
|
# page numbers. -> { items:, next_cursor: }
|
|
85
56
|
def search_cursor(cursor: nil, limit: DEFAULT_SIZE, q: nil, flavor: nil,
|
|
@@ -131,25 +102,6 @@ module Relaton
|
|
|
131
102
|
doc.ids.any? { |i| i.downcase.include?(needle) }
|
|
132
103
|
end
|
|
133
104
|
|
|
134
|
-
def facet(docs, attr)
|
|
135
|
-
docs.group_by { |d| d.send(attr) }
|
|
136
|
-
.reject { |value, _| value.nil? }
|
|
137
|
-
.map { |value, ds| { "value" => value, "count" => ds.size } }
|
|
138
|
-
.sort_by { |f| -f["count"] }
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
def item(doc)
|
|
142
|
-
{
|
|
143
|
-
"docid" => doc.docid,
|
|
144
|
-
"flavor" => doc.stream,
|
|
145
|
-
"year" => doc.year,
|
|
146
|
-
"doctype" => doc.doctype,
|
|
147
|
-
"status" => doc.status,
|
|
148
|
-
"title" => doc.title,
|
|
149
|
-
"api" => "/api/v1/document?code=#{::URI.encode_www_form_component(doc.docid.to_s)}",
|
|
150
|
-
}
|
|
151
|
-
end
|
|
152
|
-
|
|
153
105
|
def collection_item(doc)
|
|
154
106
|
{
|
|
155
107
|
"key" => doc.docid,
|
data/lib/relaton/api/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: relaton-api
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.0
|
|
4
|
+
version: 3.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-11-
|
|
11
|
+
date: 2026-11-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rack
|
|
@@ -64,7 +64,6 @@ files:
|
|
|
64
64
|
- lib/relaton/api/app.rb
|
|
65
65
|
- lib/relaton/api/config.rb
|
|
66
66
|
- lib/relaton/api/routes.rb
|
|
67
|
-
- lib/relaton/api/routes/api_v1.rb
|
|
68
67
|
- lib/relaton/api/routes/documents.rb
|
|
69
68
|
- lib/relaton/api/routes/flavors.rb
|
|
70
69
|
- lib/relaton/api/routes/version.rb
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "json"
|
|
4
|
-
|
|
5
|
-
module Relaton
|
|
6
|
-
module Api
|
|
7
|
-
module Routes
|
|
8
|
-
# The /api/v1 contract surface — the same paths and shapes the
|
|
9
|
-
# Workers stack serves at api.relaton.org, so the shared contract
|
|
10
|
-
# fixtures pass against this stack unchanged.
|
|
11
|
-
module ApiV1
|
|
12
|
-
PROBLEM = { "content-type" => "application/problem+json" }.freeze
|
|
13
|
-
|
|
14
|
-
def self.parse_query(env)
|
|
15
|
-
query = env["QUERY_STRING"].to_s
|
|
16
|
-
::URI.decode_www_form(query).to_h
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# GET /api/v1/document?code=...&year=&all_parts=&keep_year=
|
|
20
|
-
# -> text/xml bibdata (the model round-trip of the stored YAML)
|
|
21
|
-
class Document
|
|
22
|
-
PATH = %r{\A/api/v1/document\z}.freeze
|
|
23
|
-
|
|
24
|
-
def matches?(path) = PATH.match?(path)
|
|
25
|
-
|
|
26
|
-
def call(env, app)
|
|
27
|
-
params = ApiV1.parse_query(env)
|
|
28
|
-
code = params["code"].to_s.strip
|
|
29
|
-
return bad_request("code parameter is required") if code.empty?
|
|
30
|
-
|
|
31
|
-
entry = app.store.resolve(
|
|
32
|
-
code,
|
|
33
|
-
year: params["year"]&.to_i,
|
|
34
|
-
all_parts: params["all_parts"] == "true",
|
|
35
|
-
)
|
|
36
|
-
return not_found unless entry
|
|
37
|
-
|
|
38
|
-
[200, { "content-type" => "text/xml" },
|
|
39
|
-
[app.store.document_xml(entry)]]
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
private
|
|
43
|
-
|
|
44
|
-
def bad_request(detail)
|
|
45
|
-
[400, PROBLEM,
|
|
46
|
-
[{ type: "about:blank", title: "Bad Request",
|
|
47
|
-
detail: detail }.to_json]]
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def not_found
|
|
51
|
-
[404, { "content-type" => "text/plain" },
|
|
52
|
-
["Resource doesn't exist."]]
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
# GET /api/v1/search?q=&flavor=&doctype=&year=&status=&page=&size=
|
|
57
|
-
# -> { total, page, size, items[], facets{} }
|
|
58
|
-
class Search
|
|
59
|
-
PATH = %r{\A/api/v1/search\z}.freeze
|
|
60
|
-
|
|
61
|
-
def matches?(path) = PATH.match?(path)
|
|
62
|
-
|
|
63
|
-
def call(env, app)
|
|
64
|
-
p = ApiV1.parse_query(env)
|
|
65
|
-
result = app.store.search(
|
|
66
|
-
q: presence(p["q"]), flavor: presence(p["flavor"]),
|
|
67
|
-
doctype: presence(p["doctype"]), year: p["year"]&.to_i,
|
|
68
|
-
status: presence(p["status"]), page: p["page"]&.to_i,
|
|
69
|
-
size: p["size"]&.to_i,
|
|
70
|
-
)
|
|
71
|
-
[200, { "content-type" => "application/json" },
|
|
72
|
-
[JSON.generate(result)]]
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
private
|
|
76
|
-
|
|
77
|
-
def presence(value)
|
|
78
|
-
v = value.to_s.strip
|
|
79
|
-
v.empty? ? nil : v
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
# GET /api/v1/version -> text/plain release banner
|
|
84
|
-
class Version
|
|
85
|
-
PATH = %r{\A/api/v1/version\z}.freeze
|
|
86
|
-
|
|
87
|
-
def matches?(path) = PATH.match?(path)
|
|
88
|
-
|
|
89
|
-
def call(_env, app)
|
|
90
|
-
[200, { "content-type" => "text/plain" },
|
|
91
|
-
["Release: #{app.config.release}, " \
|
|
92
|
-
"Relaton version: #{app.config.model_version}"]]
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
end
|