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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 13b35ffffbfa3349f95961b56e7dae9e74d55a116554a76c1175f3f74e347183
4
- data.tar.gz: ea78a952280a73728fbd27cff80af8e3eebf163e45b8428ece6171a588471b55
3
+ metadata.gz: 3246a177690e1019834ab1e177da12f75aee9a7a75cdaa6caecaa3e7a721b227
4
+ data.tar.gz: e2194852b615ef5e61ddea0726e8b279543c6cbad326991a6238adf791e8d87c
5
5
  SHA512:
6
- metadata.gz: 538a97d2151764c9c84026ecccf32380ca82393158ac56d85af050aceab459ca47eb23a7828fb48bb5ed9fbe8289b11d2a5583bd8bd8b96f40ee20002247d5d8
7
- data.tar.gz: 6edff89d3e29935de3227db81ef9126d6d7ce46cf0c39d58343114522d88205de9d969d99f760e6bd8602bc71f85fd80310b8730684769c31ec9aa21fa8f0413
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, "User-Agent" => "asciichem-resolver"))
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
- body = fetch.get("#{BASE}?cas_rn=#{CGI.escape(value.to_s)}")
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",
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AsciiChem
4
- VERSION = "0.31.13"
4
+ VERSION = "0.31.15"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asciichem
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.31.13
4
+ version: 0.31.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.