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 +4 -4
- data/.gitignore +2 -1
- data/CHANGELOG.md +12 -0
- data/Gemfile.lock +5 -4
- data/contentstack.gemspec +3 -1
- data/ext/download_regions/extconf.rb +29 -0
- data/lib/contentstack/client.rb +7 -39
- data/lib/contentstack/endpoint.rb +133 -0
- data/lib/contentstack/error.rb +8 -0
- data/lib/contentstack/region.rb +36 -8
- data/lib/contentstack/version.rb +1 -1
- data/lib/contentstack.rb +21 -6
- data/rakefile.rb +10 -1
- data/spec/endpoint_spec.rb +232 -0
- metadata +7 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9caea6c14fd2fce3388e9ca3f1fb9be0a1e9f00cfd8fb044ec2e86e82198710f
|
|
4
|
+
data.tar.gz: cf9d266384581fe938dc6951315ee0ed974b4fc5207ed620c81f7ef03d066e97
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d19ca8388e4a1b12afea51497706ca3e23b19e66a9cebbf9bc6a82b36190bc48fe444c1dfd1858ae090d007ab389df34d70f0f6adb5bcf9c2506e2844fc3bfaa
|
|
7
|
+
data.tar.gz: a5958d88bffbed5682d6a73c92583d1488b79aeb2a8ca5ebe82b43adfa3065eb1d496eb3bd790c37940c65b6c005d53aa90657cd648895b046acc00783197d99
|
data/.gitignore
CHANGED
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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")
|
data/lib/contentstack/client.rb
CHANGED
|
@@ -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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
data/lib/contentstack/error.rb
CHANGED
|
@@ -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
|
data/lib/contentstack/region.rb
CHANGED
|
@@ -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
|
data/lib/contentstack/version.rb
CHANGED
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
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.
|
|
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-
|
|
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
|