contentstack_utils 1.2.3 → 1.3.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/.cursor/rules/README.md +5 -0
- data/.github/workflows/back-merge-pr.yml +59 -0
- data/.github/workflows/check-version-bump.yml +76 -0
- data/.github/workflows/issues-jira.yml +104 -17
- data/.github/workflows/release-gem.yml +5 -2
- data/.gitignore +3 -1
- data/.ruby-version +1 -1
- data/AGENTS.md +49 -0
- data/CHANGELOG.md +10 -0
- data/Gemfile.lock +38 -23
- data/contentstack_utils.gemspec +4 -4
- data/lib/contentstack_utils/endpoint.rb +96 -0
- data/lib/contentstack_utils/utils.rb +5 -0
- data/lib/contentstack_utils/version.rb +1 -1
- data/skills/README.md +16 -0
- data/skills/code-review/SKILL.md +44 -0
- data/skills/contentstack-utils/SKILL.md +51 -0
- data/skills/dev-workflow/SKILL.md +53 -0
- data/skills/framework/SKILL.md +44 -0
- data/skills/ruby-style/SKILL.md +42 -0
- data/skills/testing/SKILL.md +45 -0
- data/spec/lib/endpoint_spec.rb +268 -0
- metadata +38 -13
- data/.github/workflows/check-branch.yml +0 -20
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-review
|
|
3
|
+
description: Use when reviewing or preparing a PR for this gem—behavior, tests, API compatibility, and release notes.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Code review – Contentstack Utils Ruby
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
|
|
10
|
+
- Authoring a pull request that changes rendering, models, or dependencies
|
|
11
|
+
- Reviewing someone else’s PR before merge
|
|
12
|
+
- Checking release readiness (version + changelog)
|
|
13
|
+
|
|
14
|
+
## Instructions
|
|
15
|
+
|
|
16
|
+
### Blockers (fix before merge)
|
|
17
|
+
|
|
18
|
+
- **Tests:** `bundle exec rake` (or `bundle exec rspec`) passes locally
|
|
19
|
+
- **Breaking API:** unintended removal or signature change of public methods on **`ContentstackUtils`**, **`ContentstackUtils::GQL`**, or **`ContentstackUtils::Model::Options`** without version bump and changelog entry
|
|
20
|
+
- **Security:** no secrets in code, fixtures, or logs; HTML output changes reviewed for injection or XSS risk when embedding untrusted fields
|
|
21
|
+
|
|
22
|
+
### Major (should address)
|
|
23
|
+
|
|
24
|
+
- **CHANGELOG.md** updated for user-visible behavior fixes or features
|
|
25
|
+
- **`lib/contentstack_utils/version.rb`** updated when publishing a release (if this PR is release-bound)
|
|
26
|
+
- New JSON/RTE node types or GQL shapes covered by **`spec/lib/utils_spec.rb`** (or focused new spec files)
|
|
27
|
+
- Dependency range changes in **`contentstack_utils.gemspec`** justified and compatible with Ruby **≥ 3.1**
|
|
28
|
+
|
|
29
|
+
### Minor (nice to have)
|
|
30
|
+
|
|
31
|
+
- **README.md** examples match actual class names (**`Options`**, not a non-existent **`Option`** unless aliased)
|
|
32
|
+
- YARD or comments only where they clarify non-obvious RTE or GQL mapping
|
|
33
|
+
|
|
34
|
+
### Process
|
|
35
|
+
|
|
36
|
+
- Respect **CODEOWNERS** and branch policy (**feature/fix -> `development`**, release PRs **`development` -> `master`**) described in [dev-workflow](../dev-workflow/SKILL.md)
|
|
37
|
+
|
|
38
|
+
## References
|
|
39
|
+
|
|
40
|
+
- [AGENTS.md](../../AGENTS.md)
|
|
41
|
+
- [Development workflow](../dev-workflow/SKILL.md)
|
|
42
|
+
- [Contentstack Utils SDK](../contentstack-utils/SKILL.md)
|
|
43
|
+
- [Framework & packaging](../framework/SKILL.md)
|
|
44
|
+
- [Testing](../testing/SKILL.md)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: contentstack-utils
|
|
3
|
+
description: Use when changing the public Ruby API, RTE rendering (HTML string or JSON), GQL payloads, Model::Options/Metadata, or integration boundaries with the delivery client.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Contentstack Utils SDK – Contentstack Utils Ruby
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
|
|
10
|
+
- Implementing or fixing HTML output for rich text / JSON RTE
|
|
11
|
+
- Extending or subclassing rendering options (`ContentstackUtils::Model::Options`)
|
|
12
|
+
- Parsing GraphQL-shaped RTE (`ContentstackUtils::GQL`)
|
|
13
|
+
- Changing load paths or public entry points under `lib/` (gemspec and dependency ranges: see [framework](../framework/SKILL.md))
|
|
14
|
+
|
|
15
|
+
## Instructions
|
|
16
|
+
|
|
17
|
+
### Entry points
|
|
18
|
+
|
|
19
|
+
- **`lib/contentstack_utils.rb`** — requires version and `utils`
|
|
20
|
+
- **`lib/contentstack_utils/utils.rb`** — main module **`ContentstackUtils`** with class methods:
|
|
21
|
+
- **`render_content(content, options)`** — legacy HTML string RTE (array of strings or single string); uses Nokogiri and `_embedded_items` on the entry passed via options
|
|
22
|
+
- **`json_to_html(content, options)`** — JSON RTE document tree (Hash/Array) for CDA-style payloads with embedded items on the entry
|
|
23
|
+
- Internal helpers such as **`json_doc_to_html`** (used by GQL path)
|
|
24
|
+
- **`ContentstackUtils::GQL`** — **`json_to_html(content, options)`** for GraphQL responses: expects keys like `json` and optional `embedded_itemsConnection.edges`; reuses `ContentstackUtils.json_doc_to_html` with a GQL-specific reference resolver
|
|
25
|
+
|
|
26
|
+
### Models and extension points
|
|
27
|
+
|
|
28
|
+
- **`ContentstackUtils::Model::Options`** (`lib/contentstack_utils/model/options.rb`) — default **`render_option`**, **`render_mark`**, **`render_node`**; subclass for custom HTML (see tests under `spec/mock/custom_render_option.rb`)
|
|
29
|
+
- **`ContentstackUtils::Model::Metadata`** — built from DOM nodes (HTML path) or JSON reference nodes
|
|
30
|
+
- **`ContentstackUtils::Interface::Rendarable`** (`lib/contentstack_utils/interface/renderable.rb`) — base for options; implement **`render_option`** in subclasses
|
|
31
|
+
|
|
32
|
+
### Data contracts
|
|
33
|
+
|
|
34
|
+
- **CDA / delivery JSON path:** options may carry an **`entry`** hash with **`_embedded_items`** keyed by field UIDs
|
|
35
|
+
- **GQL path:** payload uses **`embedded_itemsConnection.edges`** with **`node`** objects; reference resolution matches **`metadata.item_uid`** to **`node.system.uid`**
|
|
36
|
+
|
|
37
|
+
### Boundaries
|
|
38
|
+
|
|
39
|
+
- HTTP and stack access belong to the separate **Contentstack Ruby** client; this gem only renders given content + embedded metadata
|
|
40
|
+
|
|
41
|
+
### Versioning
|
|
42
|
+
|
|
43
|
+
- Public API and behavior changes should be reflected in `CHANGELOG.md` and `lib/contentstack_utils/version.rb`
|
|
44
|
+
|
|
45
|
+
## References
|
|
46
|
+
|
|
47
|
+
- Product overview: [README.md](../../README.md)
|
|
48
|
+
- [Framework & packaging](../framework/SKILL.md)
|
|
49
|
+
- [Testing](../testing/SKILL.md)
|
|
50
|
+
- [Ruby style and layout](../ruby-style/SKILL.md)
|
|
51
|
+
- [Contentstack documentation](https://www.contentstack.com/docs/)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dev-workflow
|
|
3
|
+
description: Use when setting up the dev environment, running build/test/docs, or understanding CI, branches, and gem release for this repo.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Development workflow – Contentstack Utils Ruby
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
|
|
10
|
+
- Cloning the repo or onboarding a new contributor
|
|
11
|
+
- Running tests, generating docs, or building the gem locally
|
|
12
|
+
- Understanding branch rules and GitHub Actions for this repository
|
|
13
|
+
|
|
14
|
+
## Instructions
|
|
15
|
+
|
|
16
|
+
### Prerequisites
|
|
17
|
+
|
|
18
|
+
- Ruby **≥ 3.1** (matches `s.required_ruby_version` in `contentstack_utils.gemspec`)
|
|
19
|
+
- Bundler; install gems with `bundle install`
|
|
20
|
+
|
|
21
|
+
### Everyday commands
|
|
22
|
+
|
|
23
|
+
- **Run tests (default Rake task):** `bundle exec rake` or `bundle exec rake spec`
|
|
24
|
+
- **RSpec directly:** `bundle exec rspec` (pattern `spec/**/*_spec.rb` is configured in `Rakefile`)
|
|
25
|
+
- **YARD API docs:** `bundle exec rake yard` (see `.yardopts` for included paths)
|
|
26
|
+
- **Build gem artifact:** `gem build contentstack_utils.gemspec` (also used in `.github/workflows/release-gem.yml`)
|
|
27
|
+
|
|
28
|
+
### Version and changelog
|
|
29
|
+
|
|
30
|
+
- Gem version lives in `lib/contentstack_utils/version.rb` as `ContentstackUtils::VERSION`
|
|
31
|
+
- Document user-visible changes in `CHANGELOG.md` when releasing
|
|
32
|
+
|
|
33
|
+
### Branches and PRs
|
|
34
|
+
|
|
35
|
+
- Feature/fix PRs should target **`development`**. Release PRs are raised directly from **`development`** to **`master`**.
|
|
36
|
+
- Use `CODEOWNERS` for required reviewers when applicable
|
|
37
|
+
|
|
38
|
+
### CI and automation (no RSpec workflow today)
|
|
39
|
+
|
|
40
|
+
- **Release:** `.github/workflows/release-gem.yml` — on GitHub **Release** created (`release: types: [created]`) for tag **`v*`** (draft releases skipped), checks out the tag, then builds and pushes to RubyGems (note: workflow pins Ruby 2.7 for publish; align with gemspec minimum when changing)
|
|
41
|
+
- **Security / compliance:** CodeQL, policy scan, SCA scan — see `.github/workflows/`
|
|
42
|
+
- **Issues:** Jira integration workflow in `.github/workflows/issues-jira.yml`
|
|
43
|
+
|
|
44
|
+
### Optional housekeeping
|
|
45
|
+
|
|
46
|
+
- `.talismanrc` is used for secret scanning hooks in some environments; do not commit credentials or tokens
|
|
47
|
+
|
|
48
|
+
## References
|
|
49
|
+
|
|
50
|
+
- [AGENTS.md](../../AGENTS.md)
|
|
51
|
+
- [Contentstack Utils SDK](../contentstack-utils/SKILL.md)
|
|
52
|
+
- [Framework & packaging](../framework/SKILL.md)
|
|
53
|
+
- [Testing](../testing/SKILL.md)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: framework
|
|
3
|
+
description: Use when changing the gemspec, Bundler setup, Ruby/runtime constraints, or runtime dependencies (activesupport, nokogiri) and native extension implications.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Framework & packaging – Contentstack Utils Ruby
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
|
|
10
|
+
- Editing **`contentstack_utils.gemspec`** (dependencies, `required_ruby_version`, files list)
|
|
11
|
+
- Changing **`Gemfile`** / **`Gemfile.lock`** workflow or documenting install for contributors
|
|
12
|
+
- Evaluating impact of **nokogiri** (native extension) or **activesupport** version ranges on consumers
|
|
13
|
+
|
|
14
|
+
## Instructions
|
|
15
|
+
|
|
16
|
+
### Gem definition
|
|
17
|
+
|
|
18
|
+
- **`contentstack_utils.gemspec`** — canonical metadata: `s.files` from `git ls-files`, `spec/` as test files, `lib` as `require_paths`
|
|
19
|
+
- **`Gemfile`** — `gemspec` only; runtime and development dependencies are declared in the gemspec
|
|
20
|
+
|
|
21
|
+
### Runtime dependencies (as shipped)
|
|
22
|
+
|
|
23
|
+
- **activesupport** `>= 7.0`, `< 8`
|
|
24
|
+
- **nokogiri** `>= 1.13`, `< 1.19` — HTML/XML parsing for legacy string RTE paths; consumers need a compatible platform build
|
|
25
|
+
|
|
26
|
+
### Development dependencies
|
|
27
|
+
|
|
28
|
+
- **rake**, **rspec**, **webmock**, **simplecov**, **yard** — see gemspec; used for tasks in `Rakefile` and CI-style local checks
|
|
29
|
+
|
|
30
|
+
### Ruby version
|
|
31
|
+
|
|
32
|
+
- **`s.required_ruby_version`** is **>= 3.1**; `.ruby-version` pins a team default for local dev
|
|
33
|
+
- **Release workflow** (`.github/workflows/release-gem.yml`) uses its own Ruby pin for `gem build` / `gem push`; keep it aligned with gemspec when updating
|
|
34
|
+
|
|
35
|
+
### Build and publish
|
|
36
|
+
|
|
37
|
+
- Local artifact: `gem build contentstack_utils.gemspec`
|
|
38
|
+
- Publishing runs when a GitHub **Release** is created (`release: types: [created]`) for tag **`v*`** (draft releases skipped); see `release-gem.yml`.
|
|
39
|
+
|
|
40
|
+
## References
|
|
41
|
+
|
|
42
|
+
- [Development workflow](../dev-workflow/SKILL.md)
|
|
43
|
+
- [Contentstack Utils SDK](../contentstack-utils/SKILL.md)
|
|
44
|
+
- [Ruby style and layout](../ruby-style/SKILL.md)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ruby-style
|
|
3
|
+
description: Use when editing Ruby in this repo—module layout, require paths, and staying consistent with existing style (not introducing a new linter config).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Ruby style and layout – Contentstack Utils Ruby
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
|
|
10
|
+
- Adding new `.rb` files under `lib/` or `spec/`
|
|
11
|
+
- Refactoring while keeping diffs readable for reviewers
|
|
12
|
+
- Deciding where a new helper or model class should live
|
|
13
|
+
|
|
14
|
+
## Instructions
|
|
15
|
+
|
|
16
|
+
### Layout
|
|
17
|
+
|
|
18
|
+
- **Library code:** `lib/contentstack_utils/` with nested folders `model/`, `interface/`, `support/`
|
|
19
|
+
- **Top-level require:** `lib/contentstack_utils.rb` should stay a thin loader
|
|
20
|
+
- **Specs:** mirror behavior under `spec/lib/` and `spec/mock/` / `spec/support/` as in existing examples
|
|
21
|
+
|
|
22
|
+
### Conventions in this codebase
|
|
23
|
+
|
|
24
|
+
- **`ContentstackUtils`** as the root module; nested **`Model`**, **`Interface`**, **`GQL`** as already used
|
|
25
|
+
- **`require_relative`** for internal lib files (see `utils.rb`, `options.rb`)
|
|
26
|
+
- Existing files use a mix of spacing and `def self.` patterns; **prefer matching the surrounding file** over wholesale reformatting
|
|
27
|
+
- Typo in base class name **`Rendarable`** is historical; new code should remain compatible unless a dedicated rename is approved project-wide
|
|
28
|
+
|
|
29
|
+
### Linting and format
|
|
30
|
+
|
|
31
|
+
- There is **no RuboCop or Standard** configuration in this repository; do not add large style-only churn unless the team adopts a formatter
|
|
32
|
+
- Run **`bundle exec rake spec`** before pushing substantive changes
|
|
33
|
+
|
|
34
|
+
### Documentation
|
|
35
|
+
|
|
36
|
+
- YARD can document public APIs; keep user-facing usage examples in `README.md` accurate (e.g. class name **`Options`** vs informal “Option” in prose)
|
|
37
|
+
|
|
38
|
+
## References
|
|
39
|
+
|
|
40
|
+
- [Contentstack Utils SDK](../contentstack-utils/SKILL.md)
|
|
41
|
+
- [Framework & packaging](../framework/SKILL.md)
|
|
42
|
+
- [Development workflow](../dev-workflow/SKILL.md)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: testing
|
|
3
|
+
description: Use when writing or fixing RSpec tests, mocks, fixtures, SimpleCov filters, or WebMock usage in this repo.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Testing – Contentstack Utils Ruby
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
|
|
10
|
+
- Adding coverage for a new code path in `lib/`
|
|
11
|
+
- Debugging a failing spec in `spec/`
|
|
12
|
+
- Adjusting SimpleCov scope or shared test helpers
|
|
13
|
+
|
|
14
|
+
## Instructions
|
|
15
|
+
|
|
16
|
+
### Runner and config
|
|
17
|
+
|
|
18
|
+
- **RSpec** is the test framework; **`spec/spec_helper.rb`** loads SimpleCov, then `contentstack_utils`, mocks, and support files
|
|
19
|
+
- **Default task:** `bundle exec rake` runs **`spec`** with `--format documentation` (see `Rakefile`)
|
|
20
|
+
|
|
21
|
+
### Layout
|
|
22
|
+
|
|
23
|
+
- **`spec/lib/`** — primary examples (`utils_spec.rb`, `model/option_spec.rb`, `model/metadata_spec.rb`)
|
|
24
|
+
- **`spec/mock/`** — constants and custom option subclasses used across examples
|
|
25
|
+
- **`spec/support/`** — helpers (e.g. XML/JSON fixture builders like `getGQLJSONRTE`)
|
|
26
|
+
|
|
27
|
+
### Coverage
|
|
28
|
+
|
|
29
|
+
- **SimpleCov** starts in `spec_helper.rb` and **filters** `spec/` and `lib/contentstack_utils/support` from coverage metrics
|
|
30
|
+
- Aim to cover both **HTML `render_content`** paths and **JSON / GQL `json_to_html`** branches when changing `utils.rb`
|
|
31
|
+
|
|
32
|
+
### HTTP and external I/O
|
|
33
|
+
|
|
34
|
+
- **webmock** is a development dependency; stub outbound HTTP if future tests introduce network calls (current suite is largely pure parsing/rendering)
|
|
35
|
+
|
|
36
|
+
### Credentials
|
|
37
|
+
|
|
38
|
+
- Do not add real API keys, delivery tokens, or stack secrets to the repo; use fixture hashes and constants as in `spec/mock/` and `spec/support/constant.rb`
|
|
39
|
+
|
|
40
|
+
## References
|
|
41
|
+
|
|
42
|
+
- [Development workflow](../dev-workflow/SKILL.md)
|
|
43
|
+
- [Contentstack Utils SDK](../contentstack-utils/SKILL.md)
|
|
44
|
+
- [RSpec](https://rspec.info/)
|
|
45
|
+
- [SimpleCov](https://github.com/simplecov-ruby/simplecov)
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe ContentstackUtils::Endpoint do
|
|
4
|
+
before(:each) do
|
|
5
|
+
described_class.reset_cache
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# ── Default region ──────────────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
describe '.get_contentstack_endpoint with defaults' do
|
|
11
|
+
it 'returns a hash when no service is given' do
|
|
12
|
+
result = described_class.get_contentstack_endpoint
|
|
13
|
+
expect(result).to be_a(Hash)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'default region includes contentDelivery and contentManagement' do
|
|
17
|
+
result = described_class.get_contentstack_endpoint
|
|
18
|
+
expect(result).to have_key('contentDelivery')
|
|
19
|
+
expect(result).to have_key('contentManagement')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'default region contentDelivery is NA CDN' do
|
|
23
|
+
result = described_class.get_contentstack_endpoint
|
|
24
|
+
expect(result['contentDelivery']).to eq('https://cdn.contentstack.io')
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# ── NA aliases ──────────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
describe 'NA region aliases' do
|
|
31
|
+
%w[na us aws-na aws_na NA US AWS-NA AWS_NA].each do |alias_val|
|
|
32
|
+
it "alias \"#{alias_val}\" resolves contentDelivery to NA CDN" do
|
|
33
|
+
result = described_class.get_contentstack_endpoint(region: alias_val, service: 'contentDelivery')
|
|
34
|
+
expect(result).to eq('https://cdn.contentstack.io')
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# ── All 7 regions – contentDelivery ─────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
describe 'contentDelivery endpoints per region' do
|
|
42
|
+
{
|
|
43
|
+
'na' => 'https://cdn.contentstack.io',
|
|
44
|
+
'eu' => 'https://eu-cdn.contentstack.com',
|
|
45
|
+
'au' => 'https://au-cdn.contentstack.com',
|
|
46
|
+
'azure-na' => 'https://azure-na-cdn.contentstack.com',
|
|
47
|
+
'azure-eu' => 'https://azure-eu-cdn.contentstack.com',
|
|
48
|
+
'gcp-na' => 'https://gcp-na-cdn.contentstack.com',
|
|
49
|
+
'gcp-eu' => 'https://gcp-eu-cdn.contentstack.com'
|
|
50
|
+
}.each do |region, expected_url|
|
|
51
|
+
it "region \"#{region}\" resolves contentDelivery to #{expected_url}" do
|
|
52
|
+
result = described_class.get_contentstack_endpoint(region: region, service: 'contentDelivery')
|
|
53
|
+
expect(result).to eq(expected_url)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# ── All 7 regions – contentManagement ───────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
describe 'contentManagement endpoints per region' do
|
|
61
|
+
{
|
|
62
|
+
'na' => 'https://api.contentstack.io',
|
|
63
|
+
'eu' => 'https://eu-api.contentstack.com',
|
|
64
|
+
'au' => 'https://au-api.contentstack.com',
|
|
65
|
+
'azure-na' => 'https://azure-na-api.contentstack.com',
|
|
66
|
+
'azure-eu' => 'https://azure-eu-api.contentstack.com',
|
|
67
|
+
'gcp-na' => 'https://gcp-na-api.contentstack.com',
|
|
68
|
+
'gcp-eu' => 'https://gcp-eu-api.contentstack.com'
|
|
69
|
+
}.each do |region, expected_url|
|
|
70
|
+
it "region \"#{region}\" resolves contentManagement to #{expected_url}" do
|
|
71
|
+
result = described_class.get_contentstack_endpoint(region: region, service: 'contentManagement')
|
|
72
|
+
expect(result).to eq(expected_url)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# ── Service keys present ─────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
describe 'all service keys present' do
|
|
80
|
+
let(:expected_services) do
|
|
81
|
+
%w[
|
|
82
|
+
application contentDelivery contentManagement auth
|
|
83
|
+
graphqlDelivery preview graphqlPreview images assets
|
|
84
|
+
automate launch developerHub brandKit genAI
|
|
85
|
+
personalizeManagement personalizeEdge composableStudio
|
|
86
|
+
]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it 'EU region returns all expected service keys' do
|
|
90
|
+
result = described_class.get_contentstack_endpoint(region: 'eu')
|
|
91
|
+
expected_services.each do |svc|
|
|
92
|
+
expect(result).to have_key(svc), "expected key #{svc} to be present"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it 'NA region additionally includes assetManagement' do
|
|
97
|
+
result = described_class.get_contentstack_endpoint(region: 'na')
|
|
98
|
+
expect(result).to have_key('assetManagement')
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# ── omit_https flag ──────────────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
describe 'omit_https option' do
|
|
105
|
+
it 'strips https:// from a single service URL' do
|
|
106
|
+
result = described_class.get_contentstack_endpoint(region: 'na', service: 'contentDelivery', omit_https: true)
|
|
107
|
+
expect(result).to eq('cdn.contentstack.io')
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it 'strips https:// from all endpoints when no service given' do
|
|
111
|
+
result = described_class.get_contentstack_endpoint(region: 'na', omit_https: true)
|
|
112
|
+
result.each_value do |url|
|
|
113
|
+
expect(url).not_to start_with('https://')
|
|
114
|
+
expect(url).not_to start_with('http://')
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it 'retains https:// when omit_https is false (default)' do
|
|
119
|
+
result = described_class.get_contentstack_endpoint(region: 'na', service: 'contentDelivery', omit_https: false)
|
|
120
|
+
expect(result).to start_with('https://')
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it 'strips https:// from EU contentManagement' do
|
|
124
|
+
result = described_class.get_contentstack_endpoint(region: 'eu', service: 'contentManagement', omit_https: true)
|
|
125
|
+
expect(result).to eq('eu-api.contentstack.com')
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# ── Case-insensitive aliases ─────────────────────────────────────────────────
|
|
130
|
+
|
|
131
|
+
describe 'case-insensitive alias resolution' do
|
|
132
|
+
it 'resolves uppercase AWS-NA alias' do
|
|
133
|
+
result = described_class.get_contentstack_endpoint(region: 'AWS-NA', service: 'contentDelivery')
|
|
134
|
+
expect(result).to eq('https://azure-na-cdn.contentstack.com').or eq('https://cdn.contentstack.io')
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it 'resolves azure_na alias' do
|
|
138
|
+
result = described_class.get_contentstack_endpoint(region: 'azure_na', service: 'contentDelivery')
|
|
139
|
+
expect(result).to eq('https://azure-na-cdn.contentstack.com')
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it 'resolves gcp_eu alias' do
|
|
143
|
+
result = described_class.get_contentstack_endpoint(region: 'gcp_eu', service: 'contentDelivery')
|
|
144
|
+
expect(result).to eq('https://gcp-eu-cdn.contentstack.com')
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
it 'resolves AZURE-EU alias' do
|
|
148
|
+
result = described_class.get_contentstack_endpoint(region: 'AZURE-EU', service: 'contentDelivery')
|
|
149
|
+
expect(result).to eq('https://azure-eu-cdn.contentstack.com')
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
it 'resolves GCP-NA alias' do
|
|
153
|
+
result = described_class.get_contentstack_endpoint(region: 'GCP-NA', service: 'contentDelivery')
|
|
154
|
+
expect(result).to eq('https://gcp-na-cdn.contentstack.com')
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# ── No-service returns full endpoints hash ───────────────────────────────────
|
|
159
|
+
|
|
160
|
+
describe 'full endpoints map returned when no service specified' do
|
|
161
|
+
it 'AU returns a hash with more than 1 entry' do
|
|
162
|
+
result = described_class.get_contentstack_endpoint(region: 'au')
|
|
163
|
+
expect(result).to be_a(Hash)
|
|
164
|
+
expect(result.size).to be > 1
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
it 'AU contentDelivery is correct in the map' do
|
|
168
|
+
result = described_class.get_contentstack_endpoint(region: 'au')
|
|
169
|
+
expect(result['contentDelivery']).to eq('https://au-cdn.contentstack.com')
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
it 'returns a copy — mutations do not affect cached data' do
|
|
173
|
+
result = described_class.get_contentstack_endpoint(region: 'na')
|
|
174
|
+
result['contentDelivery'] = 'mutated'
|
|
175
|
+
fresh = described_class.get_contentstack_endpoint(region: 'na')
|
|
176
|
+
expect(fresh['contentDelivery']).to eq('https://cdn.contentstack.io')
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# ── Error cases ──────────────────────────────────────────────────────────────
|
|
181
|
+
|
|
182
|
+
describe 'error cases' do
|
|
183
|
+
it 'raises ArgumentError for empty string region' do
|
|
184
|
+
expect { described_class.get_contentstack_endpoint(region: '') }
|
|
185
|
+
.to raise_error(ArgumentError, 'Empty region provided')
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
it 'raises ArgumentError for whitespace-only region' do
|
|
189
|
+
expect { described_class.get_contentstack_endpoint(region: ' ') }
|
|
190
|
+
.to raise_error(ArgumentError, 'Empty region provided')
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
it 'raises ArgumentError for unknown region' do
|
|
194
|
+
expect { described_class.get_contentstack_endpoint(region: 'invalid-region') }
|
|
195
|
+
.to raise_error(ArgumentError, 'Invalid region: invalid-region')
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
it 'raises ArgumentError for unknown service' do
|
|
199
|
+
expect { described_class.get_contentstack_endpoint(region: 'na', service: 'unknownService') }
|
|
200
|
+
.to raise_error(ArgumentError, 'Service "unknownService" not found for region "na"')
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
it 'raises ArgumentError for valid region but unknown service' do
|
|
204
|
+
expect { described_class.get_contentstack_endpoint(region: 'eu', service: 'nonExistentService') }
|
|
205
|
+
.to raise_error(ArgumentError, /Service "nonExistentService" not found/)
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# ── Additional service spot-checks ──────────────────────────────────────────
|
|
210
|
+
|
|
211
|
+
describe 'additional service endpoints' do
|
|
212
|
+
it 'resolves auth endpoint for NA' do
|
|
213
|
+
result = described_class.get_contentstack_endpoint(region: 'na', service: 'auth')
|
|
214
|
+
expect(result).to eq('https://auth-api.contentstack.com')
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
it 'resolves graphqlDelivery endpoint for EU' do
|
|
218
|
+
result = described_class.get_contentstack_endpoint(region: 'eu', service: 'graphqlDelivery')
|
|
219
|
+
expect(result).to eq('https://eu-graphql.contentstack.com')
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
it 'resolves preview endpoint for azure-na' do
|
|
223
|
+
result = described_class.get_contentstack_endpoint(region: 'azure-na', service: 'preview')
|
|
224
|
+
expect(result).to eq('https://azure-na-rest-preview.contentstack.com')
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
it 'resolves application endpoint for gcp-eu' do
|
|
228
|
+
result = described_class.get_contentstack_endpoint(region: 'gcp-eu', service: 'application')
|
|
229
|
+
expect(result).to eq('https://gcp-eu-app.contentstack.com')
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# ── Utils proxy ────────────────────────────────────────────────────────────────
|
|
235
|
+
|
|
236
|
+
RSpec.describe ContentstackUtils do
|
|
237
|
+
before(:each) do
|
|
238
|
+
ContentstackUtils::Endpoint.reset_cache
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
describe '.get_contentstack_endpoint' do
|
|
242
|
+
it 'returns same value as Endpoint.get_contentstack_endpoint for default args' do
|
|
243
|
+
expect(described_class.get_contentstack_endpoint)
|
|
244
|
+
.to eq(ContentstackUtils::Endpoint.get_contentstack_endpoint)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
it 'proxy returns contentDelivery for NA' do
|
|
248
|
+
result = described_class.get_contentstack_endpoint(region: 'na', service: 'contentDelivery')
|
|
249
|
+
expect(result).to eq('https://cdn.contentstack.io')
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
it 'proxy respects omit_https option' do
|
|
253
|
+
result = described_class.get_contentstack_endpoint(region: 'na', service: 'contentDelivery', omit_https: true)
|
|
254
|
+
expect(result).to eq('cdn.contentstack.io')
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
it 'proxy returns full endpoints map when no service given' do
|
|
258
|
+
result = described_class.get_contentstack_endpoint(region: 'eu')
|
|
259
|
+
expect(result).to be_a(Hash)
|
|
260
|
+
expect(result['contentManagement']).to eq('https://eu-api.contentstack.com')
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
it 'proxy raises ArgumentError for invalid region' do
|
|
264
|
+
expect { described_class.get_contentstack_endpoint(region: 'bad-region') }
|
|
265
|
+
.to raise_error(ArgumentError, /Invalid region/)
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|