contentstack 0.8.5 → 0.9.1

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: 130b38df3188d221dee98e6b81fd722182dec955c3a2135495e7287aafb30126
4
+ data.tar.gz: 4102aec44b79853e3922ceadb2f26948b0ab06f7cfa3efabfdc36abd79e032ee
5
5
  SHA512:
6
- metadata.gz: e423a49545eae6ce37d39857877a6fcbf2ba65cbba41a6fb3cd9ec7520d7a3d64cae7ba5d660b0b2726bd53050ea566559d392145bf00413a1c2d7055b345969
7
- data.tar.gz: e27142598452a529289a8d63e51387ad8f0d89295540fa146651bb623f5bda88962088a9d53cde1fd958f4c8674062bac95fdf5783fac7046a374ac8b445371e
6
+ metadata.gz: 3d54445ed056163eb5c89c205c83a7b89a2bc750aed8b4b9a97e023b70af5f0350b75fd20e5ccf275dc7c075a0cd3a0f87636133d6319e14d844278d3ae5a896
7
+ data.tar.gz: fbe828d45cac3276ae8ee494775673d849bd60e214c8139338232a8be6f7b0508a60b850a9fde73c14ff311236162fb7d9d8e737672a17dd5fbc9105fe0f55e5
@@ -2,30 +2,117 @@ name: Create Jira Ticket for Github Issue
2
2
 
3
3
  on:
4
4
  issues:
5
- types: [opened]
5
+ types: [opened, reopened]
6
6
 
7
7
  jobs:
8
8
  issue-jira:
9
9
  runs-on: ubuntu-latest
10
10
  steps:
11
+ - name: Create Jira Issue
12
+ id: create_jira
13
+ uses: actions/github-script@v9
14
+ with:
15
+ script: |
16
+ const baseUrl = process.env.JIRA_BASE_URL;
17
+ const userEmail = process.env.JIRA_USER_EMAIL;
18
+ const jiraToken = process.env.JIRA_API_TOKEN;
19
+ const jiraProject = process.env.JIRA_PROJECT;
20
+ const jiraIssueType = process.env.JIRA_ISSUE_TYPE;
21
+ const jiraFields = JSON.parse(process.env.ISSUES_JIRA_FIELDS);
22
+
23
+ let requestBody = JSON.stringify({
24
+ fields: {
25
+ ...jiraFields,
26
+ "project": {
27
+ "key": jiraProject
28
+ },
29
+ "issuetype": {
30
+ "name": jiraIssueType
31
+ },
32
+ "summary": "Github | Issue | ${{ github.event.repository.name }} | ${{ github.event.issue.title }}",
33
+ "description": {
34
+ "version": 1,
35
+ "type": "doc",
36
+ "content": [
37
+ {
38
+ "type": "paragraph",
39
+ "content": [
40
+ {
41
+ "type": "text",
42
+ "text": "Github Issue",
43
+ "marks": [
44
+ {
45
+ "type": "strong"
46
+ }
47
+ ]
48
+ },
49
+ {
50
+ "type": "text",
51
+ "text": ": "
52
+ },
53
+ {
54
+ "type": "text",
55
+ "text": "${{ github.event.issue.html_url }}",
56
+ "marks": [
57
+ {
58
+ "type": "link",
59
+ "attrs": {
60
+ "href": "${{ github.event.issue.html_url }}"
61
+ }
62
+ }
63
+ ]
64
+ }
65
+ ]
66
+ },
67
+ {
68
+ "type": "paragraph",
69
+ "content": [
70
+ {
71
+ "type": "text",
72
+ "text": "Description",
73
+ "marks": [
74
+ {
75
+ "type": "strong"
76
+ }
77
+ ]
78
+ },
79
+ {
80
+ "type": "text",
81
+ "text": ":"
82
+ }
83
+ ]
84
+ },
85
+ {
86
+ "type": "codeBlock",
87
+ "content": [
88
+ {
89
+ "type": "text",
90
+ "text": `${{ github.event.issue.body }}`
91
+ }
92
+ ]
93
+ }
94
+ ]
95
+ }
96
+ }
97
+ });
11
98
 
12
- - name: Login to Jira
13
- uses: atlassian/gajira-login@master
99
+ const response = await fetch(`${baseUrl}/rest/api/3/issue`, {
100
+ method: 'POST',
101
+ headers: {
102
+ 'Content-Type': 'application/json',
103
+ 'Authorization': `Basic ${btoa(userEmail + ":" + jiraToken)}`
104
+ },
105
+ body: requestBody
106
+ });
107
+ if (!response.ok) {
108
+ throw new Error(`JIRA API error! Status: ${response.status}`);
109
+ }
110
+ const data = await response.json();
111
+ console.log('Jira Issue Created:', data.key);
14
112
  env:
15
113
  JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
16
114
  JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
17
115
  JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
18
-
19
- - name: Create Jira Issue
20
- id: create_jira
21
- uses: atlassian/gajira-create@master
22
- with:
23
- project: ${{ secrets.JIRA_PROJECT }}
24
- issuetype: ${{ secrets.JIRA_ISSUE_TYPE }}
25
- summary: Github | Issue | ${{ github.event.repository.name }} | ${{ github.event.issue.title }}
26
- description: |
27
- *GitHub Issue:* ${{ github.event.issue.html_url }}
28
-
29
- *Description:*
30
- ${{ github.event.issue.body }}
31
- fields: "${{ secrets.ISSUES_JIRA_FIELDS }}"
116
+ JIRA_PROJECT: ${{ secrets.JIRA_PROJECT }}
117
+ JIRA_ISSUE_TYPE: ${{ secrets.JIRA_ISSUE_TYPE }}
118
+ ISSUES_JIRA_FIELDS: "${{ secrets.ISSUES_JIRA_FIELDS }}"
@@ -14,10 +14,10 @@ jobs:
14
14
 
15
15
  steps:
16
16
  - uses: actions/checkout@v3
17
- - name: Set up Ruby 2.7
17
+ - name: Set up Ruby
18
18
  uses: ruby/setup-ruby@v1
19
19
  with:
20
- ruby-version: '2.7'
20
+ ruby-version: '3.3'
21
21
 
22
22
  - name: Publish to RubyGems
23
23
  run: |
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,24 @@
1
1
  ## CHANGELOG
2
2
 
3
+ ## Version 0.9.1
4
+ ### Date: 29th-June-2026
5
+ ### Fix
6
+ - Snyk fixes
7
+
8
+ ------------------------------------------------
9
+
10
+ ## Version 0.9.0
11
+ ### Date: 15th-June-2026
12
+ ### Enhancement
13
+ - Introduced centralized endpoint resolution via `Contentstack::Endpoint.get_contentstack_endpoint(region, service)`, eliminating all hardcoded Contentstack hostnames from the SDK.
14
+ - Added `Contentstack.get_contentstack_endpoint` as a backward-compatible module-level proxy, aligned with the `ContentstackUtils` endpoint resolution API.
15
+ - Added `Contentstack::Service` class with `CDA`, `CMA`, and `PREVIEW` constants.
16
+ - Added `Contentstack::Region::GCP_EU` region constant.
17
+ - 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.
18
+ - Added `bundle exec rake refresh_regions` task to manually update region metadata from the registry.
19
+
20
+ ------------------------------------------------
21
+
3
22
  ## Version 0.8.5
4
23
  ### Date: 5th-June-2026
5
24
  ### 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.1)
5
5
  activesupport (>= 3.2)
6
6
  contentstack_utils (~> 1.2)
7
7
 
@@ -25,7 +25,7 @@ GEM
25
25
  public_suffix (>= 2.0.2, < 8.0)
26
26
  base64 (0.3.0)
27
27
  bigdecimal (4.1.2)
28
- concurrent-ruby (1.3.6)
28
+ concurrent-ruby (1.3.7)
29
29
  connection_pool (3.0.2)
30
30
  contentstack_utils (1.2.3)
31
31
  activesupport (>= 8.0)
@@ -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,9 @@ 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
- concurrent-ruby (1.3.6) sha256=6b56837e1e7e5292f9864f34b69c5a2cbc75c0cf5338f1ce9903d10fa762d5ab
119
+ concurrent-ruby (1.3.7) sha256=4412caec3a5ea2e5fdc52076724c071a81f2c0593d83b2ac8cbb8ca63b3151b0
120
120
  connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
121
- contentstack (0.8.5)
121
+ contentstack (0.9.1)
122
122
  contentstack_utils (1.2.3) sha256=cf2f5f996eb487559fd2d7d48a99262710f53dec62c84c6e325b9a598cd31ba7
123
123
  crack (1.0.1) sha256=ff4a10390cd31d66440b7524eb1841874db86201d5b70032028553130b6d4c7e
124
124
  diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
@@ -126,7 +126,7 @@ CHECKSUMS
126
126
  drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
127
127
  hashdiff (1.2.1) sha256=9c079dbc513dfc8833ab59c0c2d8f230fa28499cc5efb4b8dd276cf931457cd1
128
128
  i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5
129
- json (2.19.7) sha256=fe432c8639f6efff69f9d73b518a3705d9581ab93156f981ea72806e1e5bcc3e
129
+ json (2.19.8) sha256=6354310fd76ef69b87d5bd1f38b40d730613baf90b6803d2d0a48f618d32dfaa
130
130
  logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
131
131
  minitest (6.0.6) sha256=153ea36d1d987a62942382b61075745042a2b3123b1cd48f4c3675af9cc7d6f1
132
132
  nokogiri (1.19.3-aarch64-linux-gnu) sha256=46b89e5d7b9e844c2ee360794240c6ea2a4e6fa0c5892a4ed487db621224b639
@@ -156,4 +156,4 @@ CHECKSUMS
156
156
  yard (0.9.44) sha256=eb087e9b631ccd887b049f303d489963945452d5e2a7eb49a5a74a7cf6887f28
157
157
 
158
158
  BUNDLED WITH
159
- 4.0.11
159
+ 2.6.3
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.1"
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.1
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-29 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
@@ -183,7 +187,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
183
187
  - !ruby/object:Gem::Version
184
188
  version: '0'
185
189
  requirements: []
186
- rubygems_version: 3.1.6
190
+ rubygems_version: 3.5.22
187
191
  signing_key:
188
192
  specification_version: 4
189
193
  summary: Contentstack Ruby client for the Content Delivery API