ecoportal-api-graphql 1.3.12 → 1.3.13
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/.ai-assistance/projects/compat-layer-audit/COMPAT_AUDIT.md +244 -0
- data/.ai-assistance/projects/template-model-logic/CATEGORY_CATALOG.md +236 -0
- data/.ai-assistance/projects/template-model-logic/CLASSIFICATION_SPIKE.md +243 -0
- data/.ai-assistance/projects/template-model-logic/DESIGN_NOTE.md +154 -0
- data/.ai-assistance/scripts/bridge-inbox-check.sh +75 -0
- data/.ai-assistance/skills/project-self-docs/SKILL.md +181 -0
- data/.ai-assistance/skills/project-self-docs/scripts/self_docs_scan.py +378 -0
- data/.ai-assistance/standards-version.json +9 -9
- data/.claude/settings.json +150 -146
- data/.gitlab-ci.yml +45 -0
- data/CHANGELOG.md +26 -0
- data/CLAUDE.md +1 -0
- data/docs/self-docs/ARCHITECTURE.md +3 -3
- data/docs/self-docs/CHANGES.jsonl +5 -0
- data/docs/self-docs/COMPLIANCE.md +79 -0
- data/docs/self-docs/CONVENTIONS.md +3 -3
- data/docs/self-docs/INTEGRATIONS.md +17 -15
- data/docs/self-docs/OPERATIONS.md +76 -0
- data/docs/self-docs/OVERVIEW.md +16 -3
- data/docs/self-docs/STATUS.md +9 -5
- data/docs/self-docs/self-docs-index.json +20 -8
- data/docs/worklog.md +18 -0
- data/lib/ecoportal/api/common/graphql/client.rb +2 -0
- data/lib/ecoportal/api/common/graphql/http_client.rb +189 -177
- data/lib/ecoportal/api/graphql_version.rb +1 -1
- metadata +11 -1
data/docs/self-docs/OVERVIEW.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
schema_version: "1.
|
|
2
|
+
schema_version: "1.1"
|
|
3
3
|
repo: "ecoportal-api-graphql"
|
|
4
4
|
doc: overview
|
|
5
|
-
last_generated: "2026-07-
|
|
6
|
-
source_head: "
|
|
5
|
+
last_generated: "2026-07-09"
|
|
6
|
+
source_head: "3f7b877"
|
|
7
7
|
review_status: draft # draft | reviewed (only a human sets 'reviewed')
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -43,9 +43,22 @@ threading).
|
|
|
43
43
|
- Entry points: `require 'ecoportal/api-graphql'` -> `lib/ecoportal/api-graphql.rb` ->
|
|
44
44
|
`Ecoportal::API::GraphQL` (`lib/ecoportal/api/graphql.rb`)
|
|
45
45
|
|
|
46
|
+
## Discovery & access
|
|
47
|
+
|
|
48
|
+
Access model: **on-request, developer-scoped** -- this is a Ruby gem, not a self-serve product. It
|
|
49
|
+
reaches its users (the ecoPortal Integrations Team and internal script authors) by being declared as
|
|
50
|
+
a dependency in a consuming project's Gemfile/gemspec (e.g. `eco-helpers ~> 1.3, >= 1.3.4`), not by
|
|
51
|
+
a public listing or an end-user-facing install. Developers learn it exists through the team's
|
|
52
|
+
integration-gem stack documentation (`.ai-assistance/code/ecoPortal_architecture/11_integration_gems.md`),
|
|
53
|
+
the repo README, and this self-docs set once mirrored to the EP Projects Register. There is no
|
|
54
|
+
customer-facing discovery surface: end customers never interact with the gem directly; they see only
|
|
55
|
+
the ecoPortal platform it talks to.
|
|
56
|
+
|
|
46
57
|
## Related self-docs
|
|
47
58
|
|
|
48
59
|
- Architecture: `ARCHITECTURE.md`
|
|
49
60
|
- Conventions: `CONVENTIONS.md`
|
|
50
61
|
- Integrations: `INTEGRATIONS.md`
|
|
51
62
|
- Status: `STATUS.md`
|
|
63
|
+
- Compliance: `COMPLIANCE.md`
|
|
64
|
+
- Operations: `OPERATIONS.md`
|
data/docs/self-docs/STATUS.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
schema_version: "1.
|
|
2
|
+
schema_version: "1.1"
|
|
3
3
|
repo: "ecoportal-api-graphql"
|
|
4
4
|
doc: status
|
|
5
|
-
last_generated: "2026-07-
|
|
6
|
-
source_head: "
|
|
5
|
+
last_generated: "2026-07-09"
|
|
6
|
+
source_head: "3f7b877"
|
|
7
7
|
review_status: draft
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -65,5 +65,9 @@ intent. The #1-priority project is Template Automatic Build and Maintenance (see
|
|
|
65
65
|
|
|
66
66
|
## Recent changes
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
Upgraded the self-docs set to schema 1.1 (audit/compliance surface): added `COMPLIANCE.md` and
|
|
69
|
+
`OPERATIONS.md`, enriched `INTEGRATIONS.md` (data class + PII + permissions per tie) and `OVERVIEW.md`
|
|
70
|
+
(Discovery & access), and bumped all docs to `source_head` `3f7b877`. Everything remains
|
|
71
|
+
`review_status: draft` -- a human must set `reviewed` before any audit use. Prior state: first
|
|
72
|
+
generation at git head `2abd6fc` (EP Projects Register pilot). The machine-readable delta is in
|
|
73
|
+
`CHANGES.jsonl`.
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
|
-
"schema_version": "1.
|
|
3
|
-
"generated_at": "2026-07-
|
|
2
|
+
"schema_version": "1.1",
|
|
3
|
+
"generated_at": "2026-07-09T07:08:00Z",
|
|
4
4
|
"repo_name": "ecoportal-api-graphql",
|
|
5
5
|
"repo_type": "ruby-gem",
|
|
6
|
-
"head": "
|
|
6
|
+
"head": "3f7b877",
|
|
7
7
|
"docs": [
|
|
8
8
|
{
|
|
9
9
|
"file": "docs/self-docs/OVERVIEW.md",
|
|
10
|
-
"purpose": "What the project is, who it serves, current priority",
|
|
10
|
+
"purpose": "What the project is, who it serves, current priority, discovery & access",
|
|
11
11
|
"exists": true,
|
|
12
|
-
"content_hash": "
|
|
12
|
+
"content_hash": "c403012498ab9247"
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"file": "docs/self-docs/ARCHITECTURE.md",
|
|
@@ -25,15 +25,27 @@
|
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
"file": "docs/self-docs/INTEGRATIONS.md",
|
|
28
|
-
"purpose": "External services, upstream/downstream repos
|
|
28
|
+
"purpose": "External services + data + permissions, upstream/downstream repos",
|
|
29
29
|
"exists": true,
|
|
30
|
-
"content_hash": "
|
|
30
|
+
"content_hash": "d271ac76361e4638"
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
33
|
"file": "docs/self-docs/STATUS.md",
|
|
34
34
|
"purpose": "Active work, migration state, known risks, open questions",
|
|
35
35
|
"exists": true,
|
|
36
|
-
"content_hash": "
|
|
36
|
+
"content_hash": "1acf51a3e7822dfb"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"file": "docs/self-docs/COMPLIANCE.md",
|
|
40
|
+
"purpose": "ISO-27001/audit, data classes + PII, vendors, AI content, leak controls",
|
|
41
|
+
"exists": true,
|
|
42
|
+
"content_hash": "1e72c2356b4e3579"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"file": "docs/self-docs/OPERATIONS.md",
|
|
46
|
+
"purpose": "Infrastructure, public exposure + Cloudflare, DevOps audit, usage KPIs",
|
|
47
|
+
"exists": true,
|
|
48
|
+
"content_hash": "878b50d02339acb7"
|
|
37
49
|
}
|
|
38
50
|
]
|
|
39
51
|
}
|
data/docs/worklog.md
CHANGED
|
@@ -8,6 +8,24 @@ This file tracks cross-project session state and the overall repo status.
|
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
+
## ▶ SESSION 2026-07-10 — OD-001 decision-support delivered to ep-ai-standards (breadcrumb)
|
|
12
|
+
|
|
13
|
+
Produced a decision-support doc mapping this repo's REAL GraphQL mutation surface onto
|
|
14
|
+
ep-ai-standards' operation-class ladder for OD-001 (the GraphQL gateway PROD "low-risk"
|
|
15
|
+
auto-approval threshold / provisional Dial A). Evidence-based from live introspection
|
|
16
|
+
(`.ai-assistance/tmp/20260605T101224_live_ep_graphql_schema.graphql.json` -- 190 mutations)
|
|
17
|
+
+ code-specs + `lib/ecoportal/api/graphql/mutation/**`. Key findings: register-type axis is
|
|
18
|
+
real + in-schema (`Register.registerStatus: TESTING|LIVE`, `base/register.rb:7`); org-type
|
|
19
|
+
(sandpit/uat/demo/PROD) is NOT a schema field (infra/credential-level, `base/organization.rb`
|
|
20
|
+
exposes only id/name/companyName/logoUrl); several mutations do not map cleanly (payload-hidden
|
|
21
|
+
deletions in `updatePage`, bulk/cascade, cross-env UAT->PROD deploy, egress/email/credential
|
|
22
|
+
mints). Delivered to ep-ai-standards' bridge inbox as
|
|
23
|
+
`ecoportal-api-graphql-od001-mutation-mapping.md` (STATUS: PENDING) -- a PROPOSAL for the graphql
|
|
24
|
+
team + Oscar to validate via an ep-ai-standards MR. NO code changed in this repo. Does not change
|
|
25
|
+
OD-001's ruling. (OD-007 breadcrumb convention.)
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
11
29
|
## ▶ SESSION 2026-07-05 — 3 live act-gov crashes = ONE root cause (stale released gems); fixed-stack floors
|
|
12
30
|
|
|
13
31
|
Three live crashes on act-gov (`-toocs-coding` + org-structure sync), all the **same class**: the
|
|
@@ -1,177 +1,189 @@
|
|
|
1
|
-
module Ecoportal
|
|
2
|
-
module API
|
|
3
|
-
module Common
|
|
4
|
-
module GraphQL
|
|
5
|
-
class ResponseError < StandardError; end
|
|
6
|
-
|
|
7
|
-
class HttpClient < Common::Client
|
|
8
|
-
class << self
|
|
9
|
-
def base_url(host)
|
|
10
|
-
"#{protocol(host)}://#{host}"
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def protocol(host)
|
|
14
|
-
host.match(/^localhost|^127\.0\.0\.1/)? 'http' : 'https'
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
ENDPOINT_PATH = 'external/graphql'.freeze
|
|
19
|
-
READ_TIMEOUT = 90
|
|
20
|
-
WRITE_TIMEOUT = 90
|
|
21
|
-
|
|
22
|
-
attr_reader :host, :version
|
|
23
|
-
|
|
24
|
-
include Ecoportal::API::Common::GraphQL::AuthService
|
|
25
|
-
|
|
26
|
-
def initialize(
|
|
27
|
-
email: nil,
|
|
28
|
-
pass: nil,
|
|
29
|
-
org_id: nil,
|
|
30
|
-
api_key: nil,
|
|
31
|
-
version: 'graphql',
|
|
32
|
-
host: 'live.ecoportal.com',
|
|
33
|
-
logger: ::Logger.new(IO::NULL),
|
|
34
|
-
deep_logging: false,
|
|
35
|
-
no_schema: true
|
|
36
|
-
)
|
|
37
|
-
@org_id = org_id
|
|
38
|
-
@user_email = email
|
|
39
|
-
@user_pass = pass
|
|
40
|
-
@no_schema = no_schema
|
|
41
|
-
@version = version
|
|
42
|
-
|
|
43
|
-
super(
|
|
44
|
-
api_key: api_key,
|
|
45
|
-
host: host,
|
|
46
|
-
logger: logger,
|
|
47
|
-
deep_logging: deep_logging
|
|
48
|
-
)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# Creates a HTTP object adding the `X-ApiKey` or `X-ECOPORTAL-API-KEY` param to the header, depending on the API version.
|
|
52
|
-
# @note It configures HTTP so it only allows body data in json format.
|
|
53
|
-
# @return [HTTP] HTTP object.
|
|
54
|
-
def base_request
|
|
55
|
-
@base_request ||=
|
|
56
|
-
case @version
|
|
57
|
-
when NilClass, 'http'
|
|
58
|
-
HTTP
|
|
59
|
-
when 'v1', 'v0'
|
|
60
|
-
HTTP.headers('X-ApiKey' => key_token)
|
|
61
|
-
when 'v2', 'v3'
|
|
62
|
-
HTTP.headers('X-ECOPORTAL-API-KEY' => key_token)
|
|
63
|
-
when 'graphql'
|
|
64
|
-
HTTP.headers('Authorization' => "Bearer #{session_token(host: host)}")
|
|
65
|
-
end.then do |request|
|
|
66
|
-
request ||= HTTP
|
|
67
|
-
request.accept(:json).timeout(
|
|
68
|
-
read: READ_TIMEOUT,
|
|
69
|
-
write: WRITE_TIMEOUT
|
|
70
|
-
)
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def org_id
|
|
75
|
-
@org_id || fetch_env_required('ORGANIZATION_ID')
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
# Executes a GraphQL query or mutation over HTTP.
|
|
79
|
-
# @param query_string [String] full GraphQL query document.
|
|
80
|
-
# @param variables [Hash] variable values to pass to the operation.
|
|
81
|
-
# @param operation_name [String, nil] when the document has multiple operations.
|
|
82
|
-
# @return [Hash] parsed response body (string-keyed, includes 'data' and optionally 'errors').
|
|
83
|
-
# @raise [Ecoportal::API::Common::Client::Error::UnexpectedServerError] on non-2xx HTTP status.
|
|
84
|
-
# @raise [Ecoportal::API::Common::GraphQL::ResponseError] when the response body contains GraphQL errors.
|
|
85
|
-
def execute(query_string, variables: {}, operation_name: nil)
|
|
86
|
-
body = { query: query_string, variables: variables }
|
|
87
|
-
body[:operationName] = operation_name if operation_name
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
@
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
def
|
|
134
|
-
@
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
def
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
end
|
|
1
|
+
module Ecoportal
|
|
2
|
+
module API
|
|
3
|
+
module Common
|
|
4
|
+
module GraphQL
|
|
5
|
+
class ResponseError < StandardError; end
|
|
6
|
+
|
|
7
|
+
class HttpClient < Common::Client
|
|
8
|
+
class << self
|
|
9
|
+
def base_url(host)
|
|
10
|
+
"#{protocol(host)}://#{host}"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def protocol(host)
|
|
14
|
+
host.match(/^localhost|^127\.0\.0\.1/)? 'http' : 'https'
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
ENDPOINT_PATH = 'external/graphql'.freeze
|
|
19
|
+
READ_TIMEOUT = 90
|
|
20
|
+
WRITE_TIMEOUT = 90
|
|
21
|
+
|
|
22
|
+
attr_reader :host, :version
|
|
23
|
+
|
|
24
|
+
include Ecoportal::API::Common::GraphQL::AuthService
|
|
25
|
+
|
|
26
|
+
def initialize(
|
|
27
|
+
email: nil,
|
|
28
|
+
pass: nil,
|
|
29
|
+
org_id: nil,
|
|
30
|
+
api_key: nil,
|
|
31
|
+
version: 'graphql',
|
|
32
|
+
host: 'live.ecoportal.com',
|
|
33
|
+
logger: ::Logger.new(IO::NULL),
|
|
34
|
+
deep_logging: false,
|
|
35
|
+
no_schema: true
|
|
36
|
+
)
|
|
37
|
+
@org_id = org_id
|
|
38
|
+
@user_email = email
|
|
39
|
+
@user_pass = pass
|
|
40
|
+
@no_schema = no_schema
|
|
41
|
+
@version = version
|
|
42
|
+
|
|
43
|
+
super(
|
|
44
|
+
api_key: api_key,
|
|
45
|
+
host: host,
|
|
46
|
+
logger: logger,
|
|
47
|
+
deep_logging: deep_logging
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Creates a HTTP object adding the `X-ApiKey` or `X-ECOPORTAL-API-KEY` param to the header, depending on the API version.
|
|
52
|
+
# @note It configures HTTP so it only allows body data in json format.
|
|
53
|
+
# @return [HTTP] HTTP object.
|
|
54
|
+
def base_request
|
|
55
|
+
@base_request ||=
|
|
56
|
+
case @version
|
|
57
|
+
when NilClass, 'http'
|
|
58
|
+
HTTP
|
|
59
|
+
when 'v1', 'v0'
|
|
60
|
+
HTTP.headers('X-ApiKey' => key_token)
|
|
61
|
+
when 'v2', 'v3'
|
|
62
|
+
HTTP.headers('X-ECOPORTAL-API-KEY' => key_token)
|
|
63
|
+
when 'graphql'
|
|
64
|
+
HTTP.headers('Authorization' => "Bearer #{session_token(host: host)}")
|
|
65
|
+
end.then do |request|
|
|
66
|
+
request ||= HTTP
|
|
67
|
+
request.accept(:json).timeout(
|
|
68
|
+
read: READ_TIMEOUT,
|
|
69
|
+
write: WRITE_TIMEOUT
|
|
70
|
+
)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def org_id
|
|
75
|
+
@org_id || fetch_env_required('ORGANIZATION_ID')
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Executes a GraphQL query or mutation over HTTP.
|
|
79
|
+
# @param query_string [String] full GraphQL query document.
|
|
80
|
+
# @param variables [Hash] variable values to pass to the operation.
|
|
81
|
+
# @param operation_name [String, nil] when the document has multiple operations.
|
|
82
|
+
# @return [Hash] parsed response body (string-keyed, includes 'data' and optionally 'errors').
|
|
83
|
+
# @raise [Ecoportal::API::Common::Client::Error::UnexpectedServerError] on non-2xx HTTP status.
|
|
84
|
+
# @raise [Ecoportal::API::Common::GraphQL::ResponseError] when the response body contains GraphQL errors.
|
|
85
|
+
def execute(query_string, variables: {}, operation_name: nil)
|
|
86
|
+
body = { query: query_string, variables: variables }
|
|
87
|
+
body[:operationName] = operation_name if operation_name
|
|
88
|
+
|
|
89
|
+
# Route through the inherited `Common::Client` pipeline
|
|
90
|
+
# (`instrument` -> `with_retry` { `rate_throttling` }) so GraphQL requests
|
|
91
|
+
# inherit the SAME battle-tested contingencies as the REST clients:
|
|
92
|
+
# rate-limit backoff (429 / Cloudflare 1015), 5xx + connection-error retries,
|
|
93
|
+
# timeouts, and instrumentation. Do NOT re-issue the request raw here — that
|
|
94
|
+
# bypasses all of the above and is what let live runs die on the first 429.
|
|
95
|
+
response = instrument('POST', ENDPOINT_PATH, body) do
|
|
96
|
+
request { |http| http.post(url_for(''), json: body) }
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
handle_http_response(response)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Full URl builder of the request
|
|
103
|
+
# @param path [String] the tail that completes the url of the request.
|
|
104
|
+
# @return [String] the final url.
|
|
105
|
+
def url_for(path)
|
|
106
|
+
"#{api_url}#{path}"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
private
|
|
110
|
+
|
|
111
|
+
def key_token
|
|
112
|
+
return if http?
|
|
113
|
+
return session_token(host: host) if graphql?
|
|
114
|
+
return @api_key if api_key?
|
|
115
|
+
return ENV['ORG_INT_KEY'] if v0?
|
|
116
|
+
|
|
117
|
+
puts 'Api-key missing!'
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def api_url
|
|
121
|
+
base_url.then do |url|
|
|
122
|
+
next "#{url}/api/#{version}" if v1_0? || v2_3?
|
|
123
|
+
next "#{url}/api/#{org_id}/#{ENDPOINT_PATH}" if graphql?
|
|
124
|
+
|
|
125
|
+
url
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def base_url
|
|
130
|
+
@base_url ||= self.class.base_url(host)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def protocol
|
|
134
|
+
@protocol ||= self.class.protocol(host)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def http?
|
|
138
|
+
return true if @version.nil?
|
|
139
|
+
|
|
140
|
+
@version == 'http'
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def api_key?
|
|
144
|
+
@api_key && !@api_key.match(/\A\W*\z/)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def v1_0?
|
|
148
|
+
v1? || v0?
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def v1?
|
|
152
|
+
@version == 'v1'
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def v0?
|
|
156
|
+
@version == 'v0'
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def v2_3?
|
|
160
|
+
return true if @version == 'v2'
|
|
161
|
+
return true if @version == 'v3'
|
|
162
|
+
|
|
163
|
+
false
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def graphql?
|
|
167
|
+
@version == 'graphql'
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# @param response [Ecoportal::API::Common::Response] the wrapped response
|
|
171
|
+
# produced by the inherited pipeline (body already JSON-parsed).
|
|
172
|
+
def handle_http_response(response)
|
|
173
|
+
unless response.success?
|
|
174
|
+
raise Ecoportal::API::Common::Client::Error::UnexpectedServerError.new(
|
|
175
|
+
response.src_body, code: response.status.code
|
|
176
|
+
)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
parsed = response.body
|
|
180
|
+
errors = parsed && parsed['errors']
|
|
181
|
+
raise ResponseError, errors.map { |e| e['message'] }.join('; ') if errors&.any?
|
|
182
|
+
|
|
183
|
+
parsed
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ecoportal-api-graphql
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.3.
|
|
4
|
+
version: 1.3.13
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Oscar Segura
|
|
@@ -262,6 +262,7 @@ files:
|
|
|
262
262
|
- ".ai-assistance/projects/api-v2-to-graphql-migration/UPSTREAM.md"
|
|
263
263
|
- ".ai-assistance/projects/api-v2-to-graphql-migration/notes/csv-template-pipeline-design.md"
|
|
264
264
|
- ".ai-assistance/projects/api-v2-to-graphql-migration/notes/cutover-usecase-gap-audit.md"
|
|
265
|
+
- ".ai-assistance/projects/compat-layer-audit/COMPAT_AUDIT.md"
|
|
265
266
|
- ".ai-assistance/projects/dynamic-model-generation/INTENT.md"
|
|
266
267
|
- ".ai-assistance/projects/eco-helpers-compat/INTENT.md"
|
|
267
268
|
- ".ai-assistance/projects/eco-helpers-compat/MIGRATION_GUIDE.md"
|
|
@@ -291,9 +292,13 @@ files:
|
|
|
291
292
|
- ".ai-assistance/projects/template-maintenance/DESIGN.md"
|
|
292
293
|
- ".ai-assistance/projects/template-maintenance/PHASE0-FINDINGS.md"
|
|
293
294
|
- ".ai-assistance/projects/template-maintenance/README.md"
|
|
295
|
+
- ".ai-assistance/projects/template-model-logic/CATEGORY_CATALOG.md"
|
|
296
|
+
- ".ai-assistance/projects/template-model-logic/CLASSIFICATION_SPIKE.md"
|
|
297
|
+
- ".ai-assistance/projects/template-model-logic/DESIGN_NOTE.md"
|
|
294
298
|
- ".ai-assistance/projects/workflow-space/TODO.md"
|
|
295
299
|
- ".ai-assistance/reinstall-claude-desktop-windows.md"
|
|
296
300
|
- ".ai-assistance/scripts/CLAUDE.md"
|
|
301
|
+
- ".ai-assistance/scripts/bridge-inbox-check.sh"
|
|
297
302
|
- ".ai-assistance/scripts/bridge-init.sh"
|
|
298
303
|
- ".ai-assistance/scripts/bridge-status.sh"
|
|
299
304
|
- ".ai-assistance/scripts/capabilities-check.ts"
|
|
@@ -323,6 +328,8 @@ files:
|
|
|
323
328
|
- ".ai-assistance/skills/graphql-schema-analysis/SKILL.md"
|
|
324
329
|
- ".ai-assistance/skills/procedural-memory/SKILL.md"
|
|
325
330
|
- ".ai-assistance/skills/project-cycle/SKILL.md"
|
|
331
|
+
- ".ai-assistance/skills/project-self-docs/SKILL.md"
|
|
332
|
+
- ".ai-assistance/skills/project-self-docs/scripts/self_docs_scan.py"
|
|
326
333
|
- ".ai-assistance/skills/refactor/SKILL.md"
|
|
327
334
|
- ".ai-assistance/skills/rubocop/SKILL.md"
|
|
328
335
|
- ".ai-assistance/skills/ruby-scripting/SKILL.md"
|
|
@@ -334,6 +341,7 @@ files:
|
|
|
334
341
|
- ".env.example"
|
|
335
342
|
- ".gitattributes"
|
|
336
343
|
- ".gitignore"
|
|
344
|
+
- ".gitlab-ci.yml"
|
|
337
345
|
- ".markdownlint.json"
|
|
338
346
|
- ".rspec"
|
|
339
347
|
- ".rubocop.yml"
|
|
@@ -350,8 +358,10 @@ files:
|
|
|
350
358
|
- bin/setup.ps1
|
|
351
359
|
- docs/self-docs/ARCHITECTURE.md
|
|
352
360
|
- docs/self-docs/CHANGES.jsonl
|
|
361
|
+
- docs/self-docs/COMPLIANCE.md
|
|
353
362
|
- docs/self-docs/CONVENTIONS.md
|
|
354
363
|
- docs/self-docs/INTEGRATIONS.md
|
|
364
|
+
- docs/self-docs/OPERATIONS.md
|
|
355
365
|
- docs/self-docs/OVERVIEW.md
|
|
356
366
|
- docs/self-docs/STATUS.md
|
|
357
367
|
- docs/self-docs/self-docs-index.json
|