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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e562f633d044fd756c25fc089ee2416511162bf2de89f67659ca17c23e167040
4
- data.tar.gz: a51fa14ea291598cd2d05e4abc6ade1ce09d0d4f764953873285a04171cda797
3
+ metadata.gz: c27bb3c8f9b659ae7c0c760f664b9eb0f8af9de851fe2de5db3bcc09df884820
4
+ data.tar.gz: f79f884937d238bc12e33ba10bbc93a4897ce27da3a3f918406d24321e6bda4b
5
5
  SHA512:
6
- metadata.gz: 85e1eac5109e2f9a4234e49d65ef81711eec2abb99ee3edac952c256f1ff38ad82ef03858707b96c5d02d7ef5051ec7e3e5bc232af66b6416c8e6ae39205348d
7
- data.tar.gz: 52be47941620eb5072e68e6b533db99d24a5081ef0232d0a8bfe45320f2279077a4a3aa79ef3e05303637a0064bb05befc57baf56eb937e9b86d3c0073370f6b
6
+ metadata.gz: 9b1ddaba4ec35ba34c10c39f103cdd8f3094b25f248e0fe3d3a0b2647fca9bb1ac3d97e7c09fa76a30d79054820403dfb12d61f59bb739bbb6a8a56e07b16a3c
7
+ data.tar.gz: faaf4afa99370b0ae3eaa5d6d2b48761265f6e19da516a4f849f05b410ad6ed47655ce8ac77ecfd67f9543c20d8931461e17308c187975873acb5ba87549d94d
@@ -8,7 +8,6 @@ module Relaton
8
8
  # autoloaded class plus one entry here (open/closed).
9
9
  class App
10
10
  ROUTES = [
11
- Routes::ApiV1::Document, Routes::ApiV1::Search, Routes::ApiV1::Version,
12
11
  Routes::Version, Routes::Flavors, Routes::Documents,
13
12
  ].freeze
14
13
 
@@ -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
- # Full-contract search: substring over titles and identifiers,
53
- # exact-value filters, one page of items in the Workers response
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,
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Relaton
4
4
  module Api
5
- VERSION = "3.0.2"
5
+ VERSION = "3.1.0"
6
6
  end
7
7
  end
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.2
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-01 00:00:00.000000000 Z
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