asciichem 0.31.13 → 0.31.15
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/CHANGELOG.md +22 -0
- data/README.adoc +7 -0
- data/lib/asciichem/resolver/adapter.rb +4 -3
- data/lib/asciichem/resolver/common_chemistry.rb +35 -2
- data/lib/asciichem/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3246a177690e1019834ab1e177da12f75aee9a7a75cdaa6caecaa3e7a721b227
|
|
4
|
+
data.tar.gz: e2194852b615ef5e61ddea0726e8b279543c6cbad326991a6238adf791e8d87c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9fb5e64c26d57111cb9a742388228e225a99da2965d5ac7793cf8ec2366367a3c3b4ed24c14aab1c173f03d324309bd0f34919c52d29a0ee569cb34de88d1012
|
|
7
|
+
data.tar.gz: cc604167f949bac08b2f47c1dd6ec69e76d1b85ec9d2158ed7d0004a1c0f60c368ec5c1c5a6544fec68f6cc9f71ffec197d1fc2eb165c7da3ea6062e039db7f1
|
data/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,28 @@
|
|
|
3
3
|
All notable changes to AsciiChem are documented here.
|
|
4
4
|
This project follows [Semantic Versioning](https://semver.org/).
|
|
5
5
|
|
|
6
|
+
## [0.31.15] - 2026-11-12
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
- Common Chemistry speaks its real key contract (CAS's access
|
|
10
|
+
email): `GET /direct-api/detail` with the token as the
|
|
11
|
+
`X-API-KEY` header plus `x-origin` and `accept` headers — not
|
|
12
|
+
the `apikey` query parameter 0.31.14 guessed. The key resolves
|
|
13
|
+
from `COMMON_CHEMISTRY_API_KEY` or the token's stored location
|
|
14
|
+
(`~/.config/cas-common-chemistry-api.key`); explicit nil means
|
|
15
|
+
no key. Verified live: formaldehyde resolves from production.
|
|
16
|
+
- The fetch contract grows `get(url, headers = {})`; redirects
|
|
17
|
+
keep the headers.
|
|
18
|
+
|
|
19
|
+
## [0.31.14] - 2026-11-12
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- The Common Chemistry adapter accepts a user-provided API key:
|
|
23
|
+
`CommonChemistry.new(api_key: ...)` or the
|
|
24
|
+
`COMMON_CHEMISTRY_API_KEY` environment variable (sent as the
|
|
25
|
+
`apikey` query parameter). CAS key-gated the API — without a key
|
|
26
|
+
live lookups answer 401 and the resolver skips the source.
|
|
27
|
+
|
|
6
28
|
## [0.31.13] - 2026-11-11
|
|
7
29
|
|
|
8
30
|
### Fixed
|
data/README.adoc
CHANGED
|
@@ -103,6 +103,13 @@ AsciiChem::Resolver.resolve(value: "50-78-2", convention: "cas",
|
|
|
103
103
|
sources: [:pubchem, :opsin])
|
|
104
104
|
----
|
|
105
105
|
|
|
106
|
+
CAS key-gated the Common Chemistry API: live lookups there need a
|
|
107
|
+
personal token (request one at cas.org/services/commonchemistry-api,
|
|
108
|
+
valid 12 months). Export it as `COMMON_CHEMISTRY_API_KEY` or store
|
|
109
|
+
it at `~/.config/cas-common-chemistry-api.key` — the adapter sends
|
|
110
|
+
it as the `X-API-KEY` header. Without a key, that source answers 401
|
|
111
|
+
and is skipped — PubChem and OPSIN keep resolving.
|
|
112
|
+
|
|
106
113
|
=== Citations: dataset bibitems with provenance
|
|
107
114
|
|
|
108
115
|
[source,ruby]
|
|
@@ -78,18 +78,19 @@ module AsciiChem
|
|
|
78
78
|
|
|
79
79
|
# Minimal stdlib HTTP transport. No runtime gem dependencies.
|
|
80
80
|
class NetFetch
|
|
81
|
-
def get(url, limit = 3)
|
|
81
|
+
def get(url, headers = {}, limit = 3)
|
|
82
82
|
raise Error, "too many redirects" if limit.zero?
|
|
83
83
|
|
|
84
84
|
uri = URI(url)
|
|
85
|
+
merged = { "User-Agent" => "asciichem-resolver" }.merge(headers)
|
|
85
86
|
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true,
|
|
86
87
|
open_timeout: 5, read_timeout: 10) do |http|
|
|
87
|
-
http.request(Net::HTTP::Get.new(uri.request_uri,
|
|
88
|
+
http.request(Net::HTTP::Get.new(uri.request_uri, merged))
|
|
88
89
|
end
|
|
89
90
|
|
|
90
91
|
case response
|
|
91
92
|
when Net::HTTPSuccess then response.body
|
|
92
|
-
when Net::HTTPRedirection then get(URI.join(uri, response["location"]).to_s, limit - 1)
|
|
93
|
+
when Net::HTTPRedirection then get(URI.join(uri, response["location"]).to_s, headers, limit - 1)
|
|
93
94
|
when Net::HTTPNotFound then nil
|
|
94
95
|
else
|
|
95
96
|
raise Error, "HTTP #{response.code}: #{response.message}"
|
|
@@ -14,14 +14,42 @@ module AsciiChem
|
|
|
14
14
|
# that must stay NC-free limit sources with the
|
|
15
15
|
# `asciichem-sources` attribute (metanorma-plugin-asciichem) or
|
|
16
16
|
# `Resolver.resolve(sources:)`.
|
|
17
|
+
#
|
|
18
|
+
# API access: CAS key-gated the API (401 without a key; request
|
|
19
|
+
# one at cas.org/services/commonchemistry-api). The access email
|
|
20
|
+
# defines the contract: GET /direct-api/detail with the token as
|
|
21
|
+
# the X-API-KEY header plus x-origin and accept headers. The key
|
|
22
|
+
# comes per instance (`api_key:`), from
|
|
23
|
+
# COMMON_CHEMISTRY_API_KEY, or from
|
|
24
|
+
# ~/.config/cas-common-chemistry-api.key (the token's stored
|
|
25
|
+
# location). Tokens are personal and expire after 12 months
|
|
26
|
+
# (CAS emails a renewal notice). Without a key, live lookups
|
|
27
|
+
# answer 401 and the resolver skips this source; seeded caches
|
|
28
|
+
# keep working.
|
|
17
29
|
class CommonChemistry < Adapter
|
|
18
30
|
source_name :common_chemistry
|
|
19
31
|
supports "cas"
|
|
20
32
|
|
|
21
|
-
BASE = "https://commonchemistry.cas.org/api/detail"
|
|
33
|
+
BASE = "https://commonchemistry.cas.org/direct-api/detail"
|
|
34
|
+
ORIGIN = "https://commonchemistry.cas.org/api-overview"
|
|
35
|
+
KEY_FILE = File.expand_path("~/.config/cas-common-chemistry-api.key")
|
|
36
|
+
|
|
37
|
+
attr_reader :api_key
|
|
38
|
+
|
|
39
|
+
# Explicit nil means no key; the env/file lookup runs only when
|
|
40
|
+
# the argument is omitted.
|
|
41
|
+
def initialize(api_key: default_api_key)
|
|
42
|
+
super()
|
|
43
|
+
@api_key = api_key
|
|
44
|
+
end
|
|
22
45
|
|
|
23
46
|
def fetch_record(value:, convention:, fetch:)
|
|
24
|
-
|
|
47
|
+
headers = {
|
|
48
|
+
"Accept" => "application/json",
|
|
49
|
+
"x-origin" => ORIGIN
|
|
50
|
+
}
|
|
51
|
+
headers["X-API-KEY"] = api_key if api_key
|
|
52
|
+
body = fetch.get("#{BASE}?cas_rn=#{CGI.escape(value.to_s)}", headers)
|
|
25
53
|
return nil unless body
|
|
26
54
|
|
|
27
55
|
data = JSON.parse(body)
|
|
@@ -38,6 +66,11 @@ module AsciiChem
|
|
|
38
66
|
|
|
39
67
|
private
|
|
40
68
|
|
|
69
|
+
def default_api_key
|
|
70
|
+
ENV.fetch("COMMON_CHEMISTRY_API_KEY", nil) ||
|
|
71
|
+
(File.read(KEY_FILE).strip if File.readable?(KEY_FILE))
|
|
72
|
+
end
|
|
73
|
+
|
|
41
74
|
def substance_from(data)
|
|
42
75
|
provenance = default_provenance
|
|
43
76
|
identifiers = [Identifier.new(value: data["rn"], convention: "cas",
|
data/lib/asciichem/version.rb
CHANGED