contentstack 0.8.4 → 0.9.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: b496abfa2bddd87690df60b5271eedbbe2b3803e888ee9de01cd2dbdf28b3f4f
4
- data.tar.gz: e156a8022a32343d42ab8b28ebf48a9e48cbc31bf2d188eca936059443d703cc
3
+ metadata.gz: 9caea6c14fd2fce3388e9ca3f1fb9be0a1e9f00cfd8fb044ec2e86e82198710f
4
+ data.tar.gz: cf9d266384581fe938dc6951315ee0ed974b4fc5207ed620c81f7ef03d066e97
5
5
  SHA512:
6
- metadata.gz: 1aec5d7f59a1a56903a7d31d59671f0dc6daaf52dec83d8fe52b2c9653e40b1341b03cbf2f52f60761e26e4a0ffaa50387f5c8f529a6011afe35ead5ba17d434
7
- data.tar.gz: 6994156034d105442de60df1091d3b26aa7eae02c7ce9cfaf68315bfd868710626c3cfba08f3e69cc83dd4944695b8ad40cbb5761feac2fb52097f1a05bb3464
6
+ metadata.gz: d19ca8388e4a1b12afea51497706ca3e23b19e66a9cebbf9bc6a82b36190bc48fe444c1dfd1858ae090d007ab389df34d70f0f6adb5bcf9c2506e2844fc3bfaa
7
+ data.tar.gz: a5958d88bffbed5682d6a73c92583d1488b79aeb2a8ca5ebe82b43adfa3065eb1d496eb3bd790c37940c65b6c005d53aa90657cd648895b046acc00783197d99
@@ -0,0 +1,54 @@
1
+ name: Back-merge master to development
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+ pull-requests: write
12
+
13
+ jobs:
14
+ open-back-merge-pr:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - name: Checkout
18
+ uses: actions/checkout@v4
19
+ with:
20
+ fetch-depth: 0
21
+
22
+ - name: Open back-merge PR if needed
23
+ env:
24
+ GH_TOKEN: ${{ github.token }}
25
+ run: |
26
+ set -euo pipefail
27
+ BASE_BRANCH="development"
28
+ SOURCE_BRANCH="master"
29
+
30
+ git fetch origin "$BASE_BRANCH" "$SOURCE_BRANCH"
31
+
32
+ if ! git show-ref --verify --quiet "refs/remotes/origin/$BASE_BRANCH"; then
33
+ echo "Base branch '$BASE_BRANCH' does not exist on origin; skipping."
34
+ exit 0
35
+ fi
36
+
37
+ SOURCE_SHA=$(git rev-parse "origin/$SOURCE_BRANCH")
38
+ BASE_SHA=$(git rev-parse "origin/$BASE_BRANCH")
39
+
40
+ if [ "$SOURCE_SHA" = "$BASE_SHA" ]; then
41
+ echo "$SOURCE_BRANCH and $BASE_BRANCH are at the same commit; nothing to back-merge."
42
+ exit 0
43
+ fi
44
+
45
+ EXISTING=$(gh pr list --repo "${{ github.repository }}" --base "$BASE_BRANCH" --head "$SOURCE_BRANCH" --state open --json number --jq 'length')
46
+
47
+ if [ "$EXISTING" -gt 0 ]; then
48
+ echo "An open PR from $SOURCE_BRANCH to $BASE_BRANCH already exists; skipping."
49
+ exit 0
50
+ fi
51
+
52
+ gh pr create --repo "${{ github.repository }}" --base "$BASE_BRANCH" --head "$SOURCE_BRANCH" --title "chore: back-merge $SOURCE_BRANCH into $BASE_BRANCH" --body "Automated back-merge after changes landed on \\`$SOURCE_BRANCH\\`. Review and merge to keep \\`$BASE_BRANCH\\` in sync."
53
+
54
+ echo "Created back-merge PR $SOURCE_BRANCH -> $BASE_BRANCH."
@@ -0,0 +1,79 @@
1
+ name: Check Version Bump
2
+
3
+ on:
4
+ pull_request:
5
+
6
+ jobs:
7
+ version-bump:
8
+ name: Version & Changelog bump
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - name: Checkout
12
+ uses: actions/checkout@v4
13
+ with:
14
+ fetch-depth: 0
15
+
16
+ - name: Detect changed files and version bump
17
+ id: detect
18
+ run: |
19
+ if git rev-parse HEAD^2 >/dev/null 2>&1; then
20
+ FILES=$(git diff --name-only HEAD^1 HEAD^2)
21
+ else
22
+ FILES=$(git diff --name-only HEAD~1 HEAD)
23
+ fi
24
+ VERSION_FILES_CHANGED=false
25
+ echo "$FILES" | grep -qx 'lib/contentstack/version.rb' && VERSION_FILES_CHANGED=true
26
+ echo "$FILES" | grep -qx 'CHANGELOG.md' && VERSION_FILES_CHANGED=true
27
+ echo "version_files_changed=$VERSION_FILES_CHANGED" >> $GITHUB_OUTPUT
28
+ # Only lib/ counts as release-affecting; .github/ and spec/ do not
29
+ CODE_CHANGED=false
30
+ echo "$FILES" | grep -qE '^lib/' && CODE_CHANGED=true
31
+ echo "code_changed=$CODE_CHANGED" >> $GITHUB_OUTPUT
32
+
33
+ - name: Skip when only test/docs/.github changed
34
+ if: steps.detect.outputs.code_changed != 'true'
35
+ run: |
36
+ echo "No release-affecting files changed (e.g. only spec/docs/.github). Skipping version-bump check."
37
+ exit 0
38
+
39
+ - name: Fail when version bump was missed
40
+ if: steps.detect.outputs.code_changed == 'true' && steps.detect.outputs.version_files_changed != 'true'
41
+ run: |
42
+ echo "::error::This PR has code changes but no version bump. Please bump the version in lib/contentstack/version.rb and add an entry in CHANGELOG.md."
43
+ exit 1
44
+
45
+ - name: Check version bump
46
+ if: steps.detect.outputs.code_changed == 'true' && steps.detect.outputs.version_files_changed == 'true'
47
+ run: |
48
+ set -e
49
+ GEM_VERSION=$(sed -n 's/.*VERSION = "\(.*\)".*/\1/p' lib/contentstack/version.rb)
50
+ if [ -z "$GEM_VERSION" ]; then
51
+ echo "::error::Could not read version from lib/contentstack/version.rb"
52
+ exit 1
53
+ fi
54
+ git fetch --tags --force 2>/dev/null || true
55
+ LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || true)
56
+ if [ -z "$LATEST_TAG" ]; then
57
+ echo "No existing tags found. Skipping version-bump check (first release)."
58
+ exit 0
59
+ fi
60
+ LATEST_VERSION="${LATEST_TAG#v}"
61
+ LATEST_VERSION="${LATEST_VERSION%%-*}"
62
+ if [ "$(printf '%s\n' "$LATEST_VERSION" "$GEM_VERSION" | sort -V | tail -1)" != "$GEM_VERSION" ]; then
63
+ echo "::error::Version bump required: lib/contentstack/version.rb ($GEM_VERSION) is not greater than latest tag ($LATEST_TAG). Please bump Contentstack::VERSION."
64
+ exit 1
65
+ fi
66
+ if [ "$GEM_VERSION" = "$LATEST_VERSION" ]; then
67
+ echo "::error::Version bump required: lib/contentstack/version.rb ($GEM_VERSION) equals latest tag ($LATEST_TAG). Please bump Contentstack::VERSION."
68
+ exit 1
69
+ fi
70
+ CHANGELOG_VERSION=$(sed -nE 's/^## Version ([0-9]+\.[0-9]+\.[0-9]+).*/\1/p' CHANGELOG.md | head -1)
71
+ if [ -z "$CHANGELOG_VERSION" ]; then
72
+ echo "::error::Could not find a version entry in CHANGELOG.md (expected line like '## Version 1.0.0')."
73
+ exit 1
74
+ fi
75
+ if [ "$CHANGELOG_VERSION" != "$GEM_VERSION" ]; then
76
+ echo "::error::CHANGELOG version mismatch: CHANGELOG.md top version ($CHANGELOG_VERSION) does not match lib/contentstack/version.rb ($GEM_VERSION). Please add or update the CHANGELOG entry for $GEM_VERSION."
77
+ exit 1
78
+ fi
79
+ echo "Version bump check passed: lib/contentstack/version.rb and CHANGELOG.md are at $GEM_VERSION (latest tag: $LATEST_TAG)."
data/.gitignore CHANGED
@@ -4,9 +4,11 @@ test
4
4
  doc
5
5
  spec-integration
6
6
  coverage
7
+ spec/.env.test
7
8
  \.yardoc
8
9
  .DS_Store
9
10
  .bundle/
10
11
  **/rspec_results.html
11
12
  vendor/
12
- .dccache
13
+ .dccache
14
+ lib/data/regions.json
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  ## CHANGELOG
2
2
 
3
+ ## Version 0.9.0
4
+ ### Date: 15th-June-2026
5
+ ### Enhancement
6
+ - Introduced centralized endpoint resolution via `Contentstack::Endpoint.get_contentstack_endpoint(region, service)`, eliminating all hardcoded Contentstack hostnames from the SDK.
7
+ - Added `Contentstack.get_contentstack_endpoint` as a backward-compatible module-level proxy, aligned with the `ContentstackUtils` endpoint resolution API.
8
+ - Added `Contentstack::Service` class with `CDA`, `CMA`, and `PREVIEW` constants.
9
+ - Added `Contentstack::Region::GCP_EU` region constant.
10
+ - Endpoint URLs are driven by a local `lib/data/regions.json` file with automatic runtime fallback to the Contentstack registry when the file is absent.
11
+ - Added `bundle exec rake refresh_regions` task to manually update region metadata from the registry.
12
+
13
+ ------------------------------------------------
14
+
15
+ ## Version 0.8.5
16
+ ### Date: 5th-June-2026
17
+ ### Deprecated
18
+ - `Query#include_draft` is deprecated. The Content Delivery API returns published content only; the `include_draft` query parameter has no effect. Use Live Preview with the Preview Service to preview unpublished entries, or the Content Management API to work with draft content.
19
+
3
20
  ## Version 0.8.4
4
21
  ### Date: 15th-April-2026
5
22
  ### Security and Compatibility
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- contentstack (0.8.4)
4
+ contentstack (0.9.0)
5
5
  activesupport (>= 3.2)
6
6
  contentstack_utils (~> 1.2)
7
7
 
@@ -24,7 +24,7 @@ GEM
24
24
  addressable (2.9.0)
25
25
  public_suffix (>= 2.0.2, < 8.0)
26
26
  base64 (0.3.0)
27
- bigdecimal (4.1.1)
27
+ bigdecimal (4.1.2)
28
28
  concurrent-ruby (1.3.6)
29
29
  connection_pool (3.0.2)
30
30
  contentstack_utils (1.2.3)
@@ -39,12 +39,26 @@ GEM
39
39
  hashdiff (1.2.1)
40
40
  i18n (1.14.8)
41
41
  concurrent-ruby (~> 1.0)
42
- json (2.19.3)
42
+ json (2.19.8)
43
43
  logger (1.7.0)
44
- minitest (6.0.4)
44
+ minitest (6.0.6)
45
45
  drb (~> 2.0)
46
46
  prism (~> 1.5)
47
- nokogiri (1.19.2-arm64-darwin)
47
+ nokogiri (1.19.3-aarch64-linux-gnu)
48
+ racc (~> 1.4)
49
+ nokogiri (1.19.3-aarch64-linux-musl)
50
+ racc (~> 1.4)
51
+ nokogiri (1.19.3-arm-linux-gnu)
52
+ racc (~> 1.4)
53
+ nokogiri (1.19.3-arm-linux-musl)
54
+ racc (~> 1.4)
55
+ nokogiri (1.19.3-arm64-darwin)
56
+ racc (~> 1.4)
57
+ nokogiri (1.19.3-x86_64-darwin)
58
+ racc (~> 1.4)
59
+ nokogiri (1.19.3-x86_64-linux-gnu)
60
+ racc (~> 1.4)
61
+ nokogiri (1.19.3-x86_64-linux-musl)
48
62
  racc (~> 1.4)
49
63
  prism (1.9.0)
50
64
  public_suffix (7.0.5)
@@ -77,12 +91,17 @@ GEM
77
91
  addressable (>= 2.8.0)
78
92
  crack (>= 0.3.2)
79
93
  hashdiff (>= 0.4.0, < 2.0.0)
80
- yard (0.9.41)
94
+ yard (0.9.44)
81
95
 
82
96
  PLATFORMS
83
- arm64-darwin-22
84
- arm64-darwin-24
85
- arm64-darwin-25
97
+ aarch64-linux-gnu
98
+ aarch64-linux-musl
99
+ arm-linux-gnu
100
+ arm-linux-musl
101
+ arm64-darwin
102
+ x86_64-darwin
103
+ x86_64-linux-gnu
104
+ x86_64-linux-musl
86
105
 
87
106
  DEPENDENCIES
88
107
  contentstack!
@@ -92,5 +111,50 @@ DEPENDENCIES
92
111
  webmock (~> 3.26.0)
93
112
  yard (~> 0.9.38)
94
113
 
114
+ CHECKSUMS
115
+ activesupport (8.1.3) sha256=21a5e0dfbd4c3ddd9e1317ec6a4d782fa226e7867dc70b0743acda81a1dca20e
116
+ addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af
117
+ base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
118
+ bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd
119
+ bundler (4.0.11) sha256=5bcec0fb78302e48d02ee46f10ee6e6942be647ba5b44a6d1ddfda9a240ce785
120
+ concurrent-ruby (1.3.6) sha256=6b56837e1e7e5292f9864f34b69c5a2cbc75c0cf5338f1ce9903d10fa762d5ab
121
+ connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
122
+ contentstack (0.9.0)
123
+ contentstack_utils (1.2.3) sha256=cf2f5f996eb487559fd2d7d48a99262710f53dec62c84c6e325b9a598cd31ba7
124
+ crack (1.0.1) sha256=ff4a10390cd31d66440b7524eb1841874db86201d5b70032028553130b6d4c7e
125
+ diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
126
+ docile (1.4.1) sha256=96159be799bfa73cdb721b840e9802126e4e03dfc26863db73647204c727f21e
127
+ drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
128
+ hashdiff (1.2.1) sha256=9c079dbc513dfc8833ab59c0c2d8f230fa28499cc5efb4b8dd276cf931457cd1
129
+ i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5
130
+ json (2.19.8) sha256=6354310fd76ef69b87d5bd1f38b40d730613baf90b6803d2d0a48f618d32dfaa
131
+ logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
132
+ minitest (6.0.6) sha256=153ea36d1d987a62942382b61075745042a2b3123b1cd48f4c3675af9cc7d6f1
133
+ nokogiri (1.19.3-aarch64-linux-gnu) sha256=46b89e5d7b9e844c2ee360794240c6ea2a4e6fa0c5892a4ed487db621224b639
134
+ nokogiri (1.19.3-aarch64-linux-musl) sha256=8392dfdcd21be7a94dbbe9ccc138dea01b97b24cb2dc02a114ca98bfb1d9a0b7
135
+ nokogiri (1.19.3-arm-linux-gnu) sha256=3919d5ffc334ad778a4a9eb88fda7dcb8b1fb58c8a52ac640c6dcd2f038e774f
136
+ nokogiri (1.19.3-arm-linux-musl) sha256=9ce1cb6346bb9c67b1550eb537aa183ead91e4b6eadb2f36ade02d8dd2a79fb6
137
+ nokogiri (1.19.3-arm64-darwin) sha256=71b9bd424b1b7abc18b05052a1a3cfd3627abdca62be280854cc411791357e42
138
+ nokogiri (1.19.3-x86_64-darwin) sha256=77f3fba57d46c53ab31e62fc6c28f705109d1bf6264356c76f132b2be5728d4d
139
+ nokogiri (1.19.3-x86_64-linux-gnu) sha256=2f5078620fe12e83669b5b17311b32532a8153d02eee7ad06948b926d6080976
140
+ nokogiri (1.19.3-x86_64-linux-musl) sha256=248c906d2166eca5efb56d52fdee5f9a1f51d69a72e2b64fdac647b4ce39ea3f
141
+ prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
142
+ public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623
143
+ racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
144
+ rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
145
+ rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
146
+ rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
147
+ rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
148
+ rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47
149
+ rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
150
+ securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
151
+ simplecov (0.22.0) sha256=fe2622c7834ff23b98066bb0a854284b2729a569ac659f82621fc22ef36213a5
152
+ simplecov-html (0.13.2) sha256=bd0b8e54e7c2d7685927e8d6286466359b6f16b18cb0df47b508e8d73c777246
153
+ simplecov_json_formatter (0.1.4) sha256=529418fbe8de1713ac2b2d612aa3daa56d316975d307244399fa4838c601b428
154
+ tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b
155
+ uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
156
+ webmock (3.26.2) sha256=774556f2ea6371846cca68c01769b2eac0d134492d21f6d0ab5dd643965a4c90
157
+ yard (0.9.44) sha256=eb087e9b631ccd887b049f303d489963945452d5e2a7eb49a5a74a7cf6887f28
158
+
95
159
  BUNDLED WITH
96
- 2.3.13
160
+ 4.0.11
data/contentstack.gemspec CHANGED
@@ -18,7 +18,9 @@ Gem::Specification.new do |s|
18
18
  s.summary = %q{Contentstack Ruby client for the Content Delivery API}
19
19
  s.description = %q{Contentstack Ruby client for the Content Delivery API}
20
20
 
21
- s.files = `git ls-files`.split("\n")
21
+ s.files = `git ls-files`.split("\n") +
22
+ Dir['ext/**/*'].select { |f| File.file?(f) }
23
+ s.extensions = ['ext/download_regions/extconf.rb']
22
24
  s.require_paths = ["lib"]
23
25
 
24
26
  s.add_dependency 'activesupport', '>= 3.2'
@@ -0,0 +1,29 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+ require 'json'
4
+ require 'fileutils'
5
+
6
+ REGISTRY_URL = 'https://artifacts.contentstack.com/regions.json'
7
+
8
+ gem_root = File.expand_path('../..', __dir__)
9
+ data_dir = File.join(gem_root, 'lib', 'data')
10
+ dest_file = File.join(data_dir, 'regions.json')
11
+
12
+ FileUtils.mkdir_p(data_dir)
13
+
14
+ begin
15
+ uri = URI.parse(REGISTRY_URL)
16
+ response = Net::HTTP.get_response(uri)
17
+ if response.is_a?(Net::HTTPSuccess)
18
+ File.write(dest_file, JSON.pretty_generate(JSON.parse(response.body)))
19
+ $stdout.puts "[Contentstack] regions.json downloaded successfully."
20
+ else
21
+ $stdout.puts "[Contentstack] Warning: Could not download regions.json (HTTP #{response.code}). Runtime fallback will be used."
22
+ end
23
+ rescue => e
24
+ $stdout.puts "[Contentstack] Warning: Could not download regions.json — #{e.message}. Runtime fallback will be used."
25
+ end
26
+
27
+ # RubyGems requires a Makefile to exist after extconf.rb runs.
28
+ # We create a no-op one since this extension has no C code to compile.
29
+ File.write('Makefile', "all:\n\ninstall:\n\nclean:\n\n")
@@ -2,6 +2,7 @@ require 'contentstack/api'
2
2
  require 'contentstack/content_type'
3
3
  require 'contentstack/asset_collection'
4
4
  require 'contentstack/sync_result'
5
+ require 'contentstack/endpoint'
5
6
  require 'util'
6
7
  require 'contentstack/error'
7
8
  module Contentstack
@@ -80,47 +81,14 @@ module Contentstack
80
81
  end
81
82
 
82
83
  private
83
- def get_default_region_hosts(region='us')
84
- host = "#{Contentstack::Host::PROTOCOL}#{Contentstack::Host::DEFAULT_HOST}" #set default host if region is nil
85
- case region
86
- when "us"
87
- host = "#{Contentstack::Host::PROTOCOL}#{Contentstack::Host::DEFAULT_HOST}"
88
- when "eu"
89
- host = "#{Contentstack::Host::PROTOCOL}eu-cdn.#{Contentstack::Host::HOST}"
90
- when "azure-na"
91
- host = "#{Contentstack::Host::PROTOCOL}azure-na-cdn.#{Contentstack::Host::HOST}"
92
- when "azure-eu"
93
- host = "#{Contentstack::Host::PROTOCOL}azure-eu-cdn.#{Contentstack::Host::HOST}"
94
- when "gcp-na"
95
- host = "#{Contentstack::Host::PROTOCOL}gcp-na-cdn.#{Contentstack::Host::HOST}"
96
- end
97
- host
98
- end
99
84
 
100
85
  def get_host_by_region(region, options)
101
- if options[:host].nil? && region.present?
102
- host = get_default_region_hosts(region)
103
- elsif options[:host].present? && region.present?
104
- custom_host = options[:host]
105
- case region
106
- when "us"
107
- host = "#{Contentstack::Host::PROTOCOL}cdn.#{custom_host}"
108
- when "eu"
109
- host = "#{Contentstack::Host::PROTOCOL}eu-cdn.#{custom_host}"
110
- when "azure-na"
111
- host = "#{Contentstack::Host::PROTOCOL}azure-na-cdn.#{custom_host}"
112
- when "azure-eu"
113
- host = "#{Contentstack::Host::PROTOCOL}azure-eu-cdn.#{custom_host}"
114
- when "gcp-na"
115
- host = "#{Contentstack::Host::PROTOCOL}gcp-na-cdn.#{custom_host}"
116
- end
117
- elsif options[:host].present? && region.empty?
118
- custom_host = options[:host]
119
- host = "#{Contentstack::Host::PROTOCOL}cdn.#{custom_host}"
120
- else
121
- host = "#{Contentstack::Host::PROTOCOL}#{Contentstack::Host::DEFAULT_HOST}" #set default host if region and host is empty
122
- end
123
- host
86
+ custom_host = options[:host]
87
+ Contentstack::Endpoint.get_contentstack_endpoint(
88
+ region.present? ? region : Contentstack::Region::US,
89
+ Contentstack::Service::CDA,
90
+ custom_host.present? ? custom_host : nil
91
+ )
124
92
  end
125
93
 
126
94
  end
@@ -0,0 +1,133 @@
1
+ require 'json'
2
+ require 'net/http'
3
+ require 'uri'
4
+ require 'fileutils'
5
+ require 'contentstack/error'
6
+
7
+ module Contentstack
8
+ # Centralised endpoint resolver. Reads region metadata from the local
9
+ # regions.json (downloaded from https://artifacts.contentstack.com/regions.json
10
+ # at gem install time) and falls back to a live fetch when the file is absent.
11
+ #
12
+ # Delegates to ContentstackUtils.get_contentstack_endpoint when that gem
13
+ # ships the method (contentstack-utils-ruby PR #41).
14
+ class Endpoint
15
+ REGISTRY_URL = 'https://artifacts.contentstack.com/regions.json'
16
+ DATA_FILE_PATH = File.join(File.dirname(File.dirname(__FILE__)), 'data', 'regions.json')
17
+
18
+ # Maps the SDK's short service keys to the camelCase keys used in regions.json,
19
+ # preserving backward compatibility for callers using Service::CDA / Service::CMA.
20
+ SERVICE_MAP = {
21
+ 'cda' => 'contentDelivery',
22
+ 'cma' => 'contentManagement',
23
+ 'preview' => 'preview'
24
+ }.freeze
25
+
26
+ DEFAULT_SERVICE = 'contentDelivery'
27
+
28
+ # Resolve a Contentstack service URL for the given region and service.
29
+ #
30
+ # Contentstack::Endpoint.get_contentstack_endpoint('eu')
31
+ # # => "https://eu-cdn.contentstack.com"
32
+ #
33
+ # Contentstack::Endpoint.get_contentstack_endpoint('us', 'contentManagement')
34
+ # # => "https://api.contentstack.io"
35
+ #
36
+ # Contentstack::Endpoint.get_contentstack_endpoint('eu', 'cda') # short alias
37
+ # # => "https://eu-cdn.contentstack.com"
38
+ #
39
+ # When +custom_host+ is supplied the region CDN prefix is derived from
40
+ # regions.json and prepended to the custom domain.
41
+ def self.get_contentstack_endpoint(region, service = DEFAULT_SERVICE, custom_host = nil)
42
+ region_key = region.to_s.downcase
43
+ service_key = SERVICE_MAP.fetch(service.to_s, service.to_s)
44
+
45
+ if custom_host.nil? || custom_host.to_s.empty?
46
+ if defined?(ContentstackUtils) && ContentstackUtils.respond_to?(:get_contentstack_endpoint)
47
+ return ContentstackUtils.get_contentstack_endpoint(region_key, service_key)
48
+ end
49
+ resolve_standard(region_key, service_key)
50
+ else
51
+ resolve_custom_host(region_key, service_key, custom_host)
52
+ end
53
+ end
54
+
55
+ # Download the latest regions.json from https://artifacts.contentstack.com/regions.json
56
+ # and persist it locally. Called automatically by ext/download_regions/extconf.rb
57
+ # during bundle install / bundle update, and by `bundle exec rake refresh_regions`.
58
+ def self.refresh_regions
59
+ data = fetch_from_registry
60
+ FileUtils.mkdir_p(File.dirname(DATA_FILE_PATH))
61
+ File.write(DATA_FILE_PATH, JSON.pretty_generate(data))
62
+ data
63
+ end
64
+
65
+ private
66
+
67
+ def self.resolve_standard(region_key, service_key)
68
+ region_data = find_region(region_key)
69
+ unless region_data
70
+ raise Contentstack::Error.new(
71
+ Contentstack::ErrorMessages.region_invalid(region_key, all_region_ids)
72
+ )
73
+ end
74
+ unless region_data['endpoints'].key?(service_key)
75
+ raise Contentstack::Error.new(
76
+ Contentstack::ErrorMessages.service_invalid(service_key, region_data['endpoints'].keys)
77
+ )
78
+ end
79
+ region_data['endpoints'][service_key]
80
+ end
81
+
82
+ def self.resolve_custom_host(region_key, service_key, custom_host)
83
+ region_data = find_region(region_key)
84
+ if region_data && region_data['endpoints'].key?(service_key)
85
+ standard_url = region_data['endpoints'][service_key]
86
+ prefix = URI.parse(standard_url).host.split('.').first
87
+ "https://#{prefix}.#{custom_host}"
88
+ else
89
+ "https://cdn.#{custom_host}"
90
+ end
91
+ end
92
+
93
+ # Find a region by its canonical id or any of its declared aliases.
94
+ def self.find_region(region_key)
95
+ load_regions['regions'].find do |r|
96
+ r['id'] == region_key ||
97
+ r['alias'].any? { |a| a.downcase == region_key }
98
+ end
99
+ end
100
+
101
+ def self.all_region_ids
102
+ load_regions['regions'].map { |r| r['id'] }
103
+ end
104
+
105
+ def self.load_regions
106
+ if File.exist?(DATA_FILE_PATH)
107
+ JSON.parse(File.read(DATA_FILE_PATH))
108
+ else
109
+ warn '[Contentstack] regions.json not found locally — fetching from registry...'
110
+ data = fetch_from_registry
111
+ begin
112
+ FileUtils.mkdir_p(File.dirname(DATA_FILE_PATH))
113
+ File.write(DATA_FILE_PATH, JSON.pretty_generate(data))
114
+ rescue => e
115
+ warn "[Contentstack] Could not cache regions.json: #{e.message}"
116
+ end
117
+ data
118
+ end
119
+ end
120
+
121
+ def self.fetch_from_registry
122
+ uri = URI.parse(REGISTRY_URL)
123
+ response = Net::HTTP.get_response(uri)
124
+ unless response.is_a?(Net::HTTPSuccess)
125
+ raise Contentstack::Error.new(
126
+ "Failed to fetch region metadata from registry (HTTP #{response.code}). " \
127
+ 'Ensure network access and try again.'
128
+ )
129
+ end
130
+ JSON.parse(response.body)
131
+ end
132
+ end
133
+ end
@@ -17,6 +17,14 @@ module Contentstack
17
17
  def self.request_error(error)
18
18
  "The request encountered an issue due to #{error}. Review the details and try again."
19
19
  end
20
+
21
+ def self.region_invalid(region, supported)
22
+ "Unknown region '#{region}'. Supported regions: #{supported.join(', ')}."
23
+ end
24
+
25
+ def self.service_invalid(service, supported)
26
+ "Unknown service '#{service}'. Supported services: #{supported.join(', ')}."
27
+ end
20
28
  end
21
29
 
22
30
  class Error < StandardError
@@ -565,16 +565,19 @@ module Contentstack
565
565
  self
566
566
  end
567
567
 
568
- # Include objects in 'Draft' mode in response
569
- #
570
- # Example
571
- #
572
- # @query = @stack.content_type('product').query
573
- # @query.include_draft
574
- #
575
- # @return [Contentstack::Query]
576
- def include_draft(flag=true)
577
- @query[:include_draft] = flag
568
+ # @deprecated since 0.8.5 The Content Delivery API returns published content only.
569
+ # Unpublished or draft entries are not available through CDA queries. Use Live Preview
570
+ # with the Preview Service, or the Content Management API, to access unpublished content.
571
+ #
572
+ # @return [Contentstack::Query]
573
+ def include_draft(_flag=true)
574
+ warn(
575
+ "Contentstack: Query#include_draft is deprecated and has no effect on the Content " \
576
+ "Delivery API, which returns published content only. To preview unpublished entries, " \
577
+ "use Live Preview with the Preview Service. To manage or fetch draft entries, use " \
578
+ "the Content Management API.",
579
+ uplevel: 1
580
+ )
578
581
  self
579
582
  end
580
583
 
@@ -1,15 +1,43 @@
1
1
  module Contentstack
2
2
  class Region
3
- EU='eu'
4
- US='us'
5
- AZURE_NA='azure-na'
6
- AZURE_EU='azure-eu'
7
- GCP_NA='gcp-na'
3
+ EU = 'eu'
4
+ US = 'us' # alias for the 'na' region in regions.json
5
+ AZURE_NA = 'azure-na'
6
+ AZURE_EU = 'azure-eu'
7
+ GCP_NA = 'gcp-na'
8
+ GCP_EU = 'gcp-eu'
9
+ AU = 'au'
10
+ end
11
+
12
+ class Service
13
+ # Full camelCase keys matching regions.json
14
+ CONTENT_DELIVERY = 'contentDelivery'
15
+ CONTENT_MANAGEMENT = 'contentManagement'
16
+ PREVIEW = 'preview'
17
+ AUTH = 'auth'
18
+ GRAPHQL_DELIVERY = 'graphqlDelivery'
19
+ GRAPHQL_PREVIEW = 'graphqlPreview'
20
+ IMAGES = 'images'
21
+ ASSETS = 'assets'
22
+ AUTOMATE = 'automate'
23
+ LAUNCH = 'launch'
24
+ DEVELOPER_HUB = 'developerHub'
25
+ BRAND_KIT = 'brandKit'
26
+ GEN_AI = 'genAI'
27
+ PERSONALIZE_MGMT = 'personalizeManagement'
28
+ PERSONALIZE_EDGE = 'personalizeEdge'
29
+ COMPOSABLE_STUDIO = 'composableStudio'
30
+ ASSET_MANAGEMENT = 'assetManagement'
31
+ APPLICATION = 'application'
32
+
33
+ # Short aliases kept for backward compatibility
34
+ CDA = CONTENT_DELIVERY
35
+ CMA = CONTENT_MANAGEMENT
8
36
  end
9
37
 
10
38
  class Host
11
- PROTOCOL='https://'
12
- DEFAULT_HOST='cdn.contentstack.io'
13
- HOST='contentstack.com'
39
+ PROTOCOL = 'https://'
40
+ DEFAULT_HOST = 'cdn.contentstack.io'
41
+ HOST = 'contentstack.com'
14
42
  end
15
43
  end
@@ -1,3 +1,3 @@
1
1
  module Contentstack
2
- VERSION = "0.8.4"
2
+ VERSION = "0.9.0"
3
3
  end
data/lib/contentstack.rb CHANGED
@@ -3,6 +3,7 @@ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
3
3
  require "contentstack/version"
4
4
  require "contentstack/client"
5
5
  require "contentstack/region"
6
+ require "contentstack/endpoint"
6
7
  require "contentstack_utils"
7
8
 
8
9
  # == Contentstack - Ruby SDK
@@ -23,10 +24,24 @@ require "contentstack_utils"
23
24
  # ==== Query entries
24
25
  # @stack.content_type('blog').query.regex('title', '.*hello.*').fetch
25
26
  module Contentstack
26
- def self.render_content(content, options)
27
- ContentstackUtils.render_content(content, options)
28
- end
29
- def self.json_to_html(content, options)
30
- ContentstackUtils.json_to_html(content, options)
31
- end
27
+ def self.render_content(content, options)
28
+ ContentstackUtils.render_content(content, options)
29
+ end
30
+
31
+ def self.json_to_html(content, options)
32
+ ContentstackUtils.json_to_html(content, options)
33
+ end
34
+
35
+ # Backward-compatible proxy for endpoint resolution.
36
+ # Delegates to ContentstackUtils.get_contentstack_endpoint when available,
37
+ # otherwise resolves via Contentstack::Endpoint.
38
+ #
39
+ # Contentstack.get_contentstack_endpoint('eu')
40
+ # # => "https://eu-cdn.contentstack.com"
41
+ #
42
+ # Contentstack.get_contentstack_endpoint('us', 'cma')
43
+ # # => "https://api.contentstack.io"
44
+ def self.get_contentstack_endpoint(region, service = Contentstack::Service::CDA)
45
+ Contentstack::Endpoint.get_contentstack_endpoint(region, service)
46
+ end
32
47
  end
data/rakefile.rb CHANGED
@@ -1,4 +1,13 @@
1
1
  require 'yard'
2
2
  YARD::Rake::YardocTask.new do |t|
3
- t.files = ["README.rdoc", 'lib/contentstack/*.rb', 'lib/contentstack.rb'] # optional
3
+ t.files = ["README.rdoc", 'lib/contentstack/*.rb', 'lib/contentstack.rb']
4
+ end
5
+
6
+ desc 'Download the latest region metadata from the Contentstack registry and update lib/data/regions.json'
7
+ task :refresh_regions do
8
+ require_relative 'lib/contentstack/endpoint'
9
+ require_relative 'lib/contentstack/error'
10
+ puts 'Fetching latest region metadata from registry...'
11
+ Contentstack::Endpoint.refresh_regions
12
+ puts "regions.json updated at: #{Contentstack::Endpoint::DATA_FILE_PATH}"
4
13
  end
@@ -32,7 +32,7 @@ description: Use when authoring or reviewing a pull request for contentstack-rub
32
32
 
33
33
  ### Process notes
34
34
 
35
- - **`master`** is protected by **`.github/workflows/check-branch.yml`**; follow team flow (**`staging`** **`master`**) for production promotion.
35
+ - Follow direct release flow **`development` -> `master`** (no `staging` handoff in the release path).
36
36
  - Run **`bundle exec rspec`** locally; CI may not run the full suite on every PR in this repository.
37
37
 
38
38
  ## References
@@ -28,7 +28,7 @@ description: Use when setting up the repo, running tests or docs, choosing branc
28
28
 
29
29
  ### Branches and PRs
30
30
 
31
- - Default integration branch is typically **`development`** (confirm on GitHub). **`master`** merges are restricted: `.github/workflows/check-branch.yml` blocks PRs into `master` unless the head branch is **`staging`**—follow org process for promotion.
31
+ - Default integration branch is typically **`development`** (confirm on GitHub). Release PRs go directly **`development` -> `master`**; `staging` is not part of the release promotion flow.
32
32
  - Keep PRs focused; mention breaking API or Ruby version requirement changes in the description.
33
33
 
34
34
  ### Before you push
@@ -30,7 +30,7 @@ description: Use when writing or fixing RSpec examples, WebMock stubs, JSON fixt
30
30
 
31
31
  ### Helpers
32
32
 
33
- - **`create_client`** and **`create_preview_client`** in **`spec_helper`** build clients using **`ENV['API_KEY']`**, **`ENV['DELIVERY_TOKEN']`**, **`ENV['ENVIRONMENT']`** tests should not rely on real credentials; stubs supply responses.
33
+ - **`create_client`** and **`create_preview_client`** in **`spec_helper`** build clients using **`ENV['API_KEY']`**, **`ENV['DELIVERY_TOKEN']`**, **`ENV['ENVIRONMENT']`**. Copy **`spec/.env.test.example`** to **`spec/.env.test`** (gitignored) for local runs without exporting env vars; CLI/env values already set take precedence. Tests use WebMock stubs and do not require live API calls.
34
34
 
35
35
  ### Coverage
36
36
 
@@ -0,0 +1,10 @@
1
+ # Copy to spec/.env.test and fill in your stack credentials.
2
+ # spec/.env.test is gitignored — do not commit real tokens.
3
+ #
4
+ # bundle exec rspec
5
+
6
+ API_KEY=your_stack_api_key
7
+ DELIVERY_TOKEN=your_delivery_token
8
+ ENVIRONMENT=development
9
+ # Optional: only needed for custom CDN host tests
10
+ # HOST=cdn.contentstack.io
@@ -0,0 +1,232 @@
1
+ require 'spec_helper'
2
+ require_relative '../lib/contentstack'
3
+
4
+ describe Contentstack::Endpoint do
5
+ let(:regions_data) do
6
+ JSON.parse(File.read(Contentstack::Endpoint::DATA_FILE_PATH))
7
+ end
8
+
9
+ # ---------------------------------------------------------------------------
10
+ # contentDelivery (CDA) endpoints — default service
11
+ # ---------------------------------------------------------------------------
12
+ describe '.get_contentstack_endpoint - contentDelivery (default)' do
13
+ {
14
+ 'us' => 'https://cdn.contentstack.io',
15
+ 'na' => 'https://cdn.contentstack.io',
16
+ 'eu' => 'https://eu-cdn.contentstack.com',
17
+ 'au' => 'https://au-cdn.contentstack.com',
18
+ 'azure-na' => 'https://azure-na-cdn.contentstack.com',
19
+ 'azure-eu' => 'https://azure-eu-cdn.contentstack.com',
20
+ 'gcp-na' => 'https://gcp-na-cdn.contentstack.com',
21
+ 'gcp-eu' => 'https://gcp-eu-cdn.contentstack.com'
22
+ }.each do |region, expected|
23
+ it "resolves #{region} => #{expected}" do
24
+ expect(described_class.get_contentstack_endpoint(region)).to eq expected
25
+ end
26
+ end
27
+ end
28
+
29
+ # ---------------------------------------------------------------------------
30
+ # Short alias 'cda' backward-compat via SERVICE_MAP
31
+ # ---------------------------------------------------------------------------
32
+ describe '.get_contentstack_endpoint - short alias cda' do
33
+ it "maps 'cda' to contentDelivery" do
34
+ expect(described_class.get_contentstack_endpoint('eu', 'cda'))
35
+ .to eq 'https://eu-cdn.contentstack.com'
36
+ end
37
+
38
+ it "maps 'cma' to contentManagement" do
39
+ expect(described_class.get_contentstack_endpoint('eu', 'cma'))
40
+ .to eq 'https://eu-api.contentstack.com'
41
+ end
42
+ end
43
+
44
+ # ---------------------------------------------------------------------------
45
+ # contentManagement (CMA) endpoints
46
+ # ---------------------------------------------------------------------------
47
+ describe '.get_contentstack_endpoint - contentManagement' do
48
+ {
49
+ 'us' => 'https://api.contentstack.io',
50
+ 'eu' => 'https://eu-api.contentstack.com',
51
+ 'au' => 'https://au-api.contentstack.com',
52
+ 'azure-na' => 'https://azure-na-api.contentstack.com',
53
+ 'azure-eu' => 'https://azure-eu-api.contentstack.com',
54
+ 'gcp-na' => 'https://gcp-na-api.contentstack.com',
55
+ 'gcp-eu' => 'https://gcp-eu-api.contentstack.com'
56
+ }.each do |region, expected|
57
+ it "resolves #{region} CMA => #{expected}" do
58
+ expect(described_class.get_contentstack_endpoint(region, 'contentManagement')).to eq expected
59
+ end
60
+ end
61
+ end
62
+
63
+ # ---------------------------------------------------------------------------
64
+ # All other services for a representative region (EU)
65
+ # ---------------------------------------------------------------------------
66
+ describe '.get_contentstack_endpoint - all services for EU' do
67
+ {
68
+ 'auth' => 'https://eu-auth-api.contentstack.com',
69
+ 'graphqlDelivery' => 'https://eu-graphql.contentstack.com',
70
+ 'preview' => 'https://eu-rest-preview.contentstack.com',
71
+ 'graphqlPreview' => 'https://eu-graphql-preview.contentstack.com',
72
+ 'images' => 'https://eu-images.contentstack.com',
73
+ 'assets' => 'https://eu-assets.contentstack.com',
74
+ 'automate' => 'https://eu-prod-automations-api.contentstack.com',
75
+ 'launch' => 'https://eu-launch-api.contentstack.com',
76
+ 'developerHub' => 'https://eu-developerhub-api.contentstack.com',
77
+ 'brandKit' => 'https://eu-brand-kits-api.contentstack.com',
78
+ 'genAI' => 'https://eu-ai.contentstack.com/brand-kits',
79
+ 'personalizeManagement'=> 'https://eu-personalize-api.contentstack.com',
80
+ 'personalizeEdge' => 'https://eu-personalize-edge.contentstack.com',
81
+ 'composableStudio' => 'https://eu-composable-studio-api.contentstack.com',
82
+ 'application' => 'https://eu-app.contentstack.com'
83
+ }.each do |service, expected|
84
+ it "resolves EU #{service} => #{expected}" do
85
+ expect(described_class.get_contentstack_endpoint('eu', service)).to eq expected
86
+ end
87
+ end
88
+ end
89
+
90
+ # ---------------------------------------------------------------------------
91
+ # Region::Service constants
92
+ # ---------------------------------------------------------------------------
93
+ describe 'Contentstack::Service constants' do
94
+ it 'CDA aliases to contentDelivery' do expect(Contentstack::Service::CDA).to eq 'contentDelivery' end
95
+ it 'CMA aliases to contentManagement' do expect(Contentstack::Service::CMA).to eq 'contentManagement' end
96
+ it 'defines PREVIEW' do expect(Contentstack::Service::PREVIEW).to eq 'preview' end
97
+ it 'defines AUTH' do expect(Contentstack::Service::AUTH).to eq 'auth' end
98
+ it 'defines GRAPHQL_DELIVERY' do expect(Contentstack::Service::GRAPHQL_DELIVERY).to eq 'graphqlDelivery' end
99
+ it 'defines IMAGES' do expect(Contentstack::Service::IMAGES).to eq 'images' end
100
+ it 'defines ASSETS' do expect(Contentstack::Service::ASSETS).to eq 'assets' end
101
+ it 'defines DEVELOPER_HUB' do expect(Contentstack::Service::DEVELOPER_HUB).to eq 'developerHub' end
102
+ it 'defines GEN_AI' do expect(Contentstack::Service::GEN_AI).to eq 'genAI' end
103
+ end
104
+
105
+ # ---------------------------------------------------------------------------
106
+ # Region constants
107
+ # ---------------------------------------------------------------------------
108
+ describe 'Contentstack::Region constants' do
109
+ it 'includes AU' do expect(Contentstack::Region::AU).to eq 'au' end
110
+ it 'includes GCP_EU' do expect(Contentstack::Region::GCP_EU).to eq 'gcp-eu' end
111
+ end
112
+
113
+ # ---------------------------------------------------------------------------
114
+ # Alias resolution (aliases come from regions.json itself)
115
+ # ---------------------------------------------------------------------------
116
+ describe '.get_contentstack_endpoint - region aliases from regions.json' do
117
+ it "resolves 'aws-na' (alias) to the NA CDN" do
118
+ expect(described_class.get_contentstack_endpoint('aws-na')).to eq 'https://cdn.contentstack.io'
119
+ end
120
+
121
+ it "resolves 'aws-eu' (alias) to the EU CDN" do
122
+ expect(described_class.get_contentstack_endpoint('aws-eu')).to eq 'https://eu-cdn.contentstack.com'
123
+ end
124
+
125
+ it "resolves 'aws-au' (alias) to the AU CDN" do
126
+ expect(described_class.get_contentstack_endpoint('aws-au')).to eq 'https://au-cdn.contentstack.com'
127
+ end
128
+ end
129
+
130
+ # ---------------------------------------------------------------------------
131
+ # Custom host resolution
132
+ # ---------------------------------------------------------------------------
133
+ describe '.get_contentstack_endpoint - custom host' do
134
+ it 'prepends eu-cdn prefix for eu + custom host' do
135
+ expect(described_class.get_contentstack_endpoint('eu', 'contentDelivery', 'example.com'))
136
+ .to eq 'https://eu-cdn.example.com'
137
+ end
138
+
139
+ it 'prepends azure-na-cdn prefix for azure-na + custom host' do
140
+ expect(described_class.get_contentstack_endpoint('azure-na', 'contentDelivery', 'example.com'))
141
+ .to eq 'https://azure-na-cdn.example.com'
142
+ end
143
+
144
+ it 'prepends au-cdn prefix for au + custom host' do
145
+ expect(described_class.get_contentstack_endpoint('au', 'contentDelivery', 'example.com'))
146
+ .to eq 'https://au-cdn.example.com'
147
+ end
148
+
149
+ it 'falls back to cdn. prefix for an unknown region + custom host' do
150
+ expect(described_class.get_contentstack_endpoint('unknown', 'contentDelivery', 'example.com'))
151
+ .to eq 'https://cdn.example.com'
152
+ end
153
+ end
154
+
155
+ # ---------------------------------------------------------------------------
156
+ # Error handling
157
+ # ---------------------------------------------------------------------------
158
+ describe '.get_contentstack_endpoint - error handling' do
159
+ it 'raises Contentstack::Error for an unknown region (no custom host)' do
160
+ expect { described_class.get_contentstack_endpoint('mars') }
161
+ .to raise_error(Contentstack::Error, /Unknown region 'mars'/)
162
+ end
163
+
164
+ it 'raises Contentstack::Error for an unknown service' do
165
+ expect { described_class.get_contentstack_endpoint('us', 'invalidService') }
166
+ .to raise_error(Contentstack::Error, /Unknown service 'invalidService'/)
167
+ end
168
+ end
169
+
170
+ # ---------------------------------------------------------------------------
171
+ # Runtime fallback when regions.json is absent
172
+ # ---------------------------------------------------------------------------
173
+ describe '.get_contentstack_endpoint - runtime fallback' do
174
+ it 'fetches from registry when regions.json is absent' do
175
+ stub_request(:get, Contentstack::Endpoint::REGISTRY_URL)
176
+ .to_return(status: 200, body: regions_data.to_json, headers: {})
177
+
178
+ allow(File).to receive(:exist?).and_call_original
179
+ allow(File).to receive(:exist?).with(Contentstack::Endpoint::DATA_FILE_PATH).and_return(false)
180
+ allow(File).to receive(:write).and_call_original
181
+
182
+ result = described_class.get_contentstack_endpoint('eu')
183
+ expect(result).to eq 'https://eu-cdn.contentstack.com'
184
+ end
185
+ end
186
+
187
+ # ---------------------------------------------------------------------------
188
+ # refresh_regions
189
+ # ---------------------------------------------------------------------------
190
+ describe '.refresh_regions' do
191
+ it 'writes updated region data to DATA_FILE_PATH' do
192
+ stub_request(:get, Contentstack::Endpoint::REGISTRY_URL)
193
+ .to_return(status: 200, body: regions_data.to_json, headers: {})
194
+
195
+ allow(FileUtils).to receive(:mkdir_p)
196
+ expect(File).to receive(:write).with(
197
+ Contentstack::Endpoint::DATA_FILE_PATH,
198
+ JSON.pretty_generate(regions_data)
199
+ )
200
+
201
+ result = described_class.refresh_regions
202
+ expect(result).to eq regions_data
203
+ end
204
+
205
+ it 'raises Contentstack::Error when registry returns non-200' do
206
+ stub_request(:get, Contentstack::Endpoint::REGISTRY_URL)
207
+ .to_return(status: 503, body: 'Service Unavailable')
208
+
209
+ expect { described_class.refresh_regions }
210
+ .to raise_error(Contentstack::Error, /HTTP 503/)
211
+ end
212
+ end
213
+
214
+ # ---------------------------------------------------------------------------
215
+ # Module-level proxy: Contentstack.get_contentstack_endpoint
216
+ # ---------------------------------------------------------------------------
217
+ describe 'Contentstack.get_contentstack_endpoint' do
218
+ it 'returns the NA CDN URL for us' do
219
+ expect(Contentstack.get_contentstack_endpoint('us')).to eq 'https://cdn.contentstack.io'
220
+ end
221
+
222
+ it 'returns the EU CMA URL' do
223
+ expect(Contentstack.get_contentstack_endpoint('eu', 'contentManagement'))
224
+ .to eq 'https://eu-api.contentstack.com'
225
+ end
226
+
227
+ it 'accepts Service::CDA constant' do
228
+ expect(Contentstack.get_contentstack_endpoint('au', Contentstack::Service::CDA))
229
+ .to eq 'https://au-cdn.contentstack.com'
230
+ end
231
+ end
232
+ end
data/spec/query_spec.rb CHANGED
@@ -165,8 +165,13 @@ describe Contentstack::Query do
165
165
  expect(data.first.fields[:locale]).not_to be nil
166
166
  end
167
167
 
168
- it "should get data using `include_draft` method" do
169
- data = category_query.include_draft.fetch
168
+ it "warns when `include_draft` is called and does not send include_draft to the API" do
169
+ query = category_query
170
+ expect {
171
+ query.include_draft
172
+ }.to output(/Query#include_draft is deprecated/).to_stderr
173
+ expect(query.query).not_to have_key(:include_draft)
174
+ data = query.fetch
170
175
  expect(data.length).to eq 5
171
176
  end
172
177
 
data/spec/spec_helper.rb CHANGED
@@ -16,6 +16,8 @@
16
16
  require 'webmock/rspec'
17
17
  WebMock.disable_net_connect!(allow_localhost: true)
18
18
 
19
+ require_relative 'support/load_test_env'
20
+
19
21
  require 'simplecov'
20
22
  SimpleCov.start
21
23
 
@@ -0,0 +1,23 @@
1
+ # Loads optional local test credentials from spec/.env.test (gitignored).
2
+ # Existing ENV values are not overwritten, so CLI exports still take precedence.
3
+ module ContentstackTestEnv
4
+ ENV_FILE = File.expand_path("../.env.test", __dir__).freeze
5
+
6
+ def self.load!
7
+ return unless File.file?(ENV_FILE)
8
+
9
+ File.foreach(ENV_FILE) do |line|
10
+ line = line.strip
11
+ next if line.empty? || line.start_with?("#")
12
+
13
+ key, value = line.split("=", 2)
14
+ next if key.nil? || value.nil?
15
+
16
+ key = key.strip
17
+ value = value.strip.delete_prefix('"').delete_suffix('"')
18
+ ENV[key] = value unless ENV.key?(key) && !ENV[key].to_s.empty?
19
+ end
20
+ end
21
+ end
22
+
23
+ ContentstackTestEnv.load!
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: contentstack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.4
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Contentstack
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-04-15 00:00:00.000000000 Z
11
+ date: 2026-06-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -98,11 +98,13 @@ description: Contentstack Ruby client for the Content Delivery API
98
98
  email:
99
99
  - support@contentstack.com
100
100
  executables: []
101
- extensions: []
101
+ extensions:
102
+ - ext/download_regions/extconf.rb
102
103
  extra_rdoc_files: []
103
104
  files:
104
105
  - ".cursor/rules/README.md"
105
- - ".github/workflows/check-branch.yml"
106
+ - ".github/workflows/back-merge-pr.yml"
107
+ - ".github/workflows/check-version-bump.yml"
106
108
  - ".github/workflows/codeql-analysis.yml"
107
109
  - ".github/workflows/issues-jira.yml"
108
110
  - ".github/workflows/policy-scan.yml"
@@ -121,12 +123,14 @@ files:
121
123
  - README.md
122
124
  - SECURITY.md
123
125
  - contentstack.gemspec
126
+ - ext/download_regions/extconf.rb
124
127
  - lib/contentstack.rb
125
128
  - lib/contentstack/api.rb
126
129
  - lib/contentstack/asset.rb
127
130
  - lib/contentstack/asset_collection.rb
128
131
  - lib/contentstack/client.rb
129
132
  - lib/contentstack/content_type.rb
133
+ - lib/contentstack/endpoint.rb
130
134
  - lib/contentstack/entry.rb
131
135
  - lib/contentstack/entry_collection.rb
132
136
  - lib/contentstack/error.rb
@@ -142,10 +146,12 @@ files:
142
146
  - skills/framework/SKILL.md
143
147
  - skills/ruby-style/SKILL.md
144
148
  - skills/testing/SKILL.md
149
+ - spec/.env.test.example
145
150
  - spec/asset_collection_spec.rb
146
151
  - spec/asset_spec.rb
147
152
  - spec/content_type_spec.rb
148
153
  - spec/contentstack_spec.rb
154
+ - spec/endpoint_spec.rb
149
155
  - spec/entry_collection_spec.rb
150
156
  - spec/entry_spec.rb
151
157
  - spec/fixtures/asset.json
@@ -160,6 +166,7 @@ files:
160
166
  - spec/fixtures/sync_init.json
161
167
  - spec/query_spec.rb
162
168
  - spec/spec_helper.rb
169
+ - spec/support/load_test_env.rb
163
170
  - spec/sync_spec.rb
164
171
  homepage: https://github.com/contentstack/contentstack-ruby
165
172
  licenses:
@@ -1,20 +0,0 @@
1
- name: 'Check Branch'
2
-
3
- on:
4
- pull_request:
5
-
6
- jobs:
7
- check_branch:
8
- runs-on: ubuntu-latest
9
- steps:
10
- - name: Comment PR
11
- if: github.base_ref == 'master' && github.head_ref != 'staging'
12
- uses: thollander/actions-comment-pull-request@v2
13
- with:
14
- message: |
15
- We regret to inform you that you are currently not able to merge your changes into the master branch due to restrictions applied by our SRE team. To proceed with merging your changes, we kindly request that you create a pull request from the next branch. Our team will then review the changes and work with you to ensure a successful merge into the master branch.
16
- - name: Check branch
17
- if: github.base_ref == 'master' && github.head_ref != 'staging'
18
- run: |
19
- echo "ERROR: We regret to inform you that you are currently not able to merge your changes into the master branch due to restrictions applied by our SRE team. To proceed with merging your changes, we kindly request that you create a pull request from the next branch. Our team will then review the changes and work with you to ensure a successful merge into the master branch."
20
- exit 1