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