contentstack 0.8.5 → 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: c608b1247b89cb28b9519fbf9e0814879aab951953ca8a82902df4dcf4a38b7b
4
- data.tar.gz: 0cf04ec05622ffbbb963fb94794da5a80eec6d66fda8609747d2d11312e4808f
3
+ metadata.gz: 9caea6c14fd2fce3388e9ca3f1fb9be0a1e9f00cfd8fb044ec2e86e82198710f
4
+ data.tar.gz: cf9d266384581fe938dc6951315ee0ed974b4fc5207ed620c81f7ef03d066e97
5
5
  SHA512:
6
- metadata.gz: e423a49545eae6ce37d39857877a6fcbf2ba65cbba41a6fb3cd9ec7520d7a3d64cae7ba5d660b0b2726bd53050ea566559d392145bf00413a1c2d7055b345969
7
- data.tar.gz: e27142598452a529289a8d63e51387ad8f0d89295540fa146651bb623f5bda88962088a9d53cde1fd958f4c8674062bac95fdf5783fac7046a374ac8b445371e
6
+ metadata.gz: d19ca8388e4a1b12afea51497706ca3e23b19e66a9cebbf9bc6a82b36190bc48fe444c1dfd1858ae090d007ab389df34d70f0f6adb5bcf9c2506e2844fc3bfaa
7
+ data.tar.gz: a5958d88bffbed5682d6a73c92583d1488b79aeb2a8ca5ebe82b43adfa3065eb1d496eb3bd790c37940c65b6c005d53aa90657cd648895b046acc00783197d99
data/.gitignore CHANGED
@@ -10,4 +10,5 @@ spec/.env.test
10
10
  .bundle/
11
11
  **/rspec_results.html
12
12
  vendor/
13
- .dccache
13
+ .dccache
14
+ lib/data/regions.json
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
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
+
3
15
  ## Version 0.8.5
4
16
  ### Date: 5th-June-2026
5
17
  ### Deprecated
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- contentstack (0.8.5)
4
+ contentstack (0.9.0)
5
5
  activesupport (>= 3.2)
6
6
  contentstack_utils (~> 1.2)
7
7
 
@@ -39,7 +39,7 @@ GEM
39
39
  hashdiff (1.2.1)
40
40
  i18n (1.14.8)
41
41
  concurrent-ruby (~> 1.0)
42
- json (2.19.7)
42
+ json (2.19.8)
43
43
  logger (1.7.0)
44
44
  minitest (6.0.6)
45
45
  drb (~> 2.0)
@@ -116,9 +116,10 @@ CHECKSUMS
116
116
  addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af
117
117
  base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
118
118
  bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd
119
+ bundler (4.0.11) sha256=5bcec0fb78302e48d02ee46f10ee6e6942be647ba5b44a6d1ddfda9a240ce785
119
120
  concurrent-ruby (1.3.6) sha256=6b56837e1e7e5292f9864f34b69c5a2cbc75c0cf5338f1ce9903d10fa762d5ab
120
121
  connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
121
- contentstack (0.8.5)
122
+ contentstack (0.9.0)
122
123
  contentstack_utils (1.2.3) sha256=cf2f5f996eb487559fd2d7d48a99262710f53dec62c84c6e325b9a598cd31ba7
123
124
  crack (1.0.1) sha256=ff4a10390cd31d66440b7524eb1841874db86201d5b70032028553130b6d4c7e
124
125
  diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
@@ -126,7 +127,7 @@ CHECKSUMS
126
127
  drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
127
128
  hashdiff (1.2.1) sha256=9c079dbc513dfc8833ab59c0c2d8f230fa28499cc5efb4b8dd276cf931457cd1
128
129
  i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5
129
- json (2.19.7) sha256=fe432c8639f6efff69f9d73b518a3705d9581ab93156f981ea72806e1e5bcc3e
130
+ json (2.19.8) sha256=6354310fd76ef69b87d5bd1f38b40d730613baf90b6803d2d0a48f618d32dfaa
130
131
  logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
131
132
  minitest (6.0.6) sha256=153ea36d1d987a62942382b61075745042a2b3123b1cd48f4c3675af9cc7d6f1
132
133
  nokogiri (1.19.3-aarch64-linux-gnu) sha256=46b89e5d7b9e844c2ee360794240c6ea2a4e6fa0c5892a4ed487db621224b639
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
@@ -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.5"
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
@@ -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
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.5
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-06-01 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,7 +98,8 @@ 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"
@@ -122,12 +123,14 @@ files:
122
123
  - README.md
123
124
  - SECURITY.md
124
125
  - contentstack.gemspec
126
+ - ext/download_regions/extconf.rb
125
127
  - lib/contentstack.rb
126
128
  - lib/contentstack/api.rb
127
129
  - lib/contentstack/asset.rb
128
130
  - lib/contentstack/asset_collection.rb
129
131
  - lib/contentstack/client.rb
130
132
  - lib/contentstack/content_type.rb
133
+ - lib/contentstack/endpoint.rb
131
134
  - lib/contentstack/entry.rb
132
135
  - lib/contentstack/entry_collection.rb
133
136
  - lib/contentstack/error.rb
@@ -148,6 +151,7 @@ files:
148
151
  - spec/asset_spec.rb
149
152
  - spec/content_type_spec.rb
150
153
  - spec/contentstack_spec.rb
154
+ - spec/endpoint_spec.rb
151
155
  - spec/entry_collection_spec.rb
152
156
  - spec/entry_spec.rb
153
157
  - spec/fixtures/asset.json