Skip to content

Commit 2d44a08

Browse files
committed
addition of secondary property update after subscription test cases
1 parent 2688437 commit 2d44a08

4 files changed

Lines changed: 558 additions & 0 deletions

File tree

sdm/src/test/java/integration/com/sap/cds/sdm/IntegrationTest_Subscription.java

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,4 +448,96 @@ void testCreateSubscription_NoExistingRepo_RepoOnboarded() throws Exception {
448448
assertTrue(
449449
verifyResult.containsIgnoreCase("FOUND"), "Check output should confirm repo was found");
450450
}
451+
452+
// ───────────────────────────────────────────────────────────────────────────
453+
// Test 6 — Register custom CMIS Secondary Types in the subscribed repository
454+
// and verify they are queryable.
455+
//
456+
// After test 5 the consumer is subscribed and SUBSCRIPTION_REPO_EXTERNAL_ID
457+
// is onboarded. We POST two secondary-type definitions (read from JSON
458+
// resources) via the CMIS browser-binding's createType action, then query
459+
// each one back via cmisselector=typeDefinition to confirm registration.
460+
//
461+
// The register-type helper treats "already exists" responses as success so
462+
// re-runs of this test against the same repo are idempotent.
463+
// ───────────────────────────────────────────────────────────────────────────
464+
@Test
465+
@Order(6)
466+
void testSubscribedTenant_RegisterSecondaryTypes_VerifyAvailable() throws Exception {
467+
System.out.println(
468+
"Test (6) : Register CMIS secondary types in subscribed repo and verify they are queryable");
469+
470+
final String typeManageScript =
471+
"src/test/java/integration/com/sap/cds/sdm/utils/sdm-type-manage.sh";
472+
473+
final String[][] secondaryTypes = {
474+
{"abc:bo", "src/test/resources/secondary-types/abc-bo-type.json"},
475+
{"Working:DocumentInfo", "src/test/resources/secondary-types/documentinfo-type.json"}
476+
};
477+
478+
// Pre-condition: subscription must be active and the repo must be onboarded
479+
// (left in place by test 5 / @BeforeAll).
480+
assertNotNull(cmisEnv, "cmisEnv is null — CMIS token was not fetched in @BeforeAll");
481+
System.out.println(" Verifying subscription repo is present before registering types...");
482+
ShellScriptRunner.Result preCheck = repoCheck(SUBSCRIPTION_REPO_EXTERNAL_ID);
483+
assertEquals(
484+
0,
485+
preCheck.getExitCode(),
486+
"Pre-condition: repo '"
487+
+ SUBSCRIPTION_REPO_EXTERNAL_ID
488+
+ "' must exist before type registration");
489+
490+
for (String[] entry : secondaryTypes) {
491+
String typeId = entry[0];
492+
String typeFile = entry[1];
493+
494+
// Step 1: Register the secondary type
495+
System.out.println(" Registering secondary type '" + typeId + "' from " + typeFile + "...");
496+
int registerExit =
497+
ShellScriptRunner.run(
498+
cmisEnv,
499+
typeManageScript,
500+
"register-type",
501+
"--externalId",
502+
SUBSCRIPTION_REPO_EXTERNAL_ID,
503+
"--typeFile",
504+
typeFile,
505+
"--subdomain",
506+
consumerSubdomain);
507+
assertEquals(
508+
0,
509+
registerExit,
510+
"register-type for '"
511+
+ typeId
512+
+ "' should succeed (exit 0 = created or already-exists, idempotent)");
513+
514+
// Step 2: Verify the type is queryable
515+
System.out.println(" Verifying secondary type '" + typeId + "' is queryable...");
516+
ShellScriptRunner.Result getResult =
517+
ShellScriptRunner.runAndCaptureAll(
518+
cmisEnv,
519+
typeManageScript,
520+
"get-type",
521+
"--externalId",
522+
SUBSCRIPTION_REPO_EXTERNAL_ID,
523+
"--typeId",
524+
typeId,
525+
"--subdomain",
526+
consumerSubdomain);
527+
assertEquals(
528+
0,
529+
getResult.getExitCode(),
530+
"get-type for '" + typeId + "' should succeed (HTTP 200 + body contains the typeId)");
531+
assertTrue(
532+
getResult.containsIgnoreCase("FOUND"),
533+
"get-type output for '" + typeId + "' should contain 'FOUND'");
534+
}
535+
536+
System.out.println(
537+
" ✅ All "
538+
+ secondaryTypes.length
539+
+ " secondary types registered and verified in '"
540+
+ SUBSCRIPTION_REPO_EXTERNAL_ID
541+
+ "'.");
542+
}
451543
}
Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
# ---------------------------------------------------------------------------
5+
# sdm-type-manage.sh — Manage CMIS Secondary Types in an SDM repository.
6+
#
7+
# Usage:
8+
# ./sdm-type-manage.sh register-type --externalId <repo-ext-id> --typeFile <path-to-type.json> [--subdomain <consumer-subdomain>]
9+
# ./sdm-type-manage.sh get-type --externalId <repo-ext-id> --typeId <type-id> [--subdomain <consumer-subdomain>]
10+
#
11+
# Exit codes:
12+
# register-type: 0 = created OR already exists (idempotent),
13+
# 1 = repository not found,
14+
# 2 = error (auth, network, or non-recoverable HTTP)
15+
# get-type: 0 = type exists in repository (HTTP 200, body contains the typeId),
16+
# 1 = type NOT found (HTTP 404 or HTTP 200 but body lacks typeId),
17+
# 2 = error
18+
#
19+
# Required config in credentials.properties:
20+
# CMIS_URL, authUrl, cmisClientID, cmisClientSecret, username, password
21+
#
22+
# When --subdomain is provided:
23+
# - The OAuth token is obtained via client_credentials against the consumer's UAA URL
24+
# (provider subdomain in authUrl is replaced by the given consumer subdomain).
25+
# - This matches the auth pattern used by sdm-repo-manage.sh for consumer-scoped operations.
26+
#
27+
# When the env var CMIS_ACCESS_TOKEN is set, the OAuth fetch is skipped — the test
28+
# harness pre-fetches the token once in @BeforeAll and threads it through here.
29+
# ---------------------------------------------------------------------------
30+
31+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
32+
CONFIG_FILE="${SCRIPT_DIR}/../../../../../../../resources/credentials.properties"
33+
34+
# --- Load key=value pairs from properties file ---
35+
load_props() {
36+
local key val
37+
while IFS= read -r line || [[ -n "$line" ]]; do
38+
[[ "$line" =~ ^[[:space:]]*$ || "$line" =~ ^[[:space:]]*# ]] && continue
39+
key="${line%%=*}"
40+
val="${line#*=}"
41+
key="${key//[[:space:]]/}"
42+
[[ -z "$key" ]] && continue
43+
printf -v "$key" '%s' "$val"
44+
done < "$1"
45+
}
46+
47+
if [[ ! -f "$CONFIG_FILE" ]]; then
48+
echo "ERROR: Config file not found at $CONFIG_FILE"
49+
exit 2
50+
fi
51+
load_props "$CONFIG_FILE"
52+
CMIS_URL="${CMIS_URL%/}/"
53+
54+
# --- Parse command ---
55+
if [[ $# -lt 1 ]]; then
56+
echo "Usage: $0 {register-type|get-type} [options]"
57+
exit 2
58+
fi
59+
60+
ACTION="$1"
61+
shift
62+
63+
EXTERNAL_ID=""
64+
TYPE_FILE=""
65+
TYPE_ID=""
66+
SUBDOMAIN=""
67+
68+
while [[ $# -gt 0 ]]; do
69+
case "$1" in
70+
--externalId) EXTERNAL_ID="$2"; shift 2 ;;
71+
--typeFile) TYPE_FILE="$2"; shift 2 ;;
72+
--typeId) TYPE_ID="$2"; shift 2 ;;
73+
--subdomain) SUBDOMAIN="$2"; shift 2 ;;
74+
*) echo "Unknown argument: $1"; exit 2 ;;
75+
esac
76+
done
77+
78+
# --- Validate required config ---
79+
for var in CMIS_URL authUrl cmisClientID cmisClientSecret; do
80+
if [[ -z "${!var:-}" ]]; then
81+
echo "ERROR: $var is not set in $CONFIG_FILE"
82+
exit 2
83+
fi
84+
done
85+
86+
# --- Resolve token URL (replace provider subdomain with consumer if --subdomain given) ---
87+
RESOLVED_TOKEN_URL="$authUrl"
88+
if [[ -n "$SUBDOMAIN" ]]; then
89+
PROVIDER_SUBDOMAIN=$(echo "$authUrl" | sed -n 's|.*://\([^.]*\)\..*|\1|p')
90+
RESOLVED_TOKEN_URL="${authUrl/$PROVIDER_SUBDOMAIN/$SUBDOMAIN}"
91+
echo "Using consumer subdomain: $SUBDOMAIN (token URL: $RESOLVED_TOKEN_URL)"
92+
fi
93+
94+
# --- Obtain OAuth2 access token (or reuse pre-fetched one) ---
95+
get_token() {
96+
if [[ -n "${CMIS_ACCESS_TOKEN:-}" ]]; then
97+
ACCESS_TOKEN="$CMIS_ACCESS_TOKEN"
98+
return
99+
fi
100+
101+
local TOKEN_RESPONSE
102+
if [[ -n "$SUBDOMAIN" ]]; then
103+
TOKEN_RESPONSE=$(curl -s -X POST "${RESOLVED_TOKEN_URL}/oauth/token" \
104+
--data-urlencode "grant_type=client_credentials" \
105+
--data-urlencode "client_id=${cmisClientID}" \
106+
--data-urlencode "client_secret=${cmisClientSecret}")
107+
else
108+
TOKEN_RESPONSE=$(curl -s -X POST "${RESOLVED_TOKEN_URL}/oauth/token" \
109+
--data-urlencode "grant_type=password" \
110+
--data-urlencode "client_id=${cmisClientID}" \
111+
--data-urlencode "client_secret=${cmisClientSecret}" \
112+
--data-urlencode "username=${username}" \
113+
--data-urlencode "password=${password}")
114+
fi
115+
116+
ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" \
117+
| grep -o '"access_token":"[^"]*"' \
118+
| sed 's/"access_token":"//;s/"$//' || true)
119+
120+
if [[ -z "$ACCESS_TOKEN" ]]; then
121+
echo "ERROR: Failed to obtain access token."
122+
echo "Token response: $TOKEN_RESPONSE"
123+
exit 2
124+
fi
125+
}
126+
127+
# --- Resolve internal CMIS repo ID from externalId ---
128+
# The repo's internal ID is needed for browser-binding endpoints (browser/{repoId}).
129+
resolve_repo_id() {
130+
if [[ -z "$EXTERNAL_ID" ]]; then
131+
echo "ERROR: --externalId is required"
132+
exit 2
133+
fi
134+
135+
local LIST_RESPONSE LIST_HTTP_CODE LIST_BODY
136+
LIST_RESPONSE=$(curl -s -w "\n%{http_code}" \
137+
-X GET "${CMIS_URL}rest/v2/repositories" \
138+
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
139+
-H "Content-Type: application/json")
140+
LIST_HTTP_CODE=$(echo "$LIST_RESPONSE" | tail -n1)
141+
LIST_BODY=$(echo "$LIST_RESPONSE" | sed '$d')
142+
143+
if [[ "$LIST_HTTP_CODE" != "200" ]]; then
144+
echo "ERROR: Failed to list repositories (HTTP ${LIST_HTTP_CODE})."
145+
echo "$LIST_BODY"
146+
exit 2
147+
fi
148+
149+
REPO_ID=$(echo "$LIST_BODY" | python3 -c "
150+
import sys, json
151+
data = json.load(sys.stdin)
152+
repos = data.get('repoAndConnectionInfos', data.get('repositories', []))
153+
for r in repos:
154+
repo = r.get('repository', r)
155+
if repo.get('externalId') == '${EXTERNAL_ID}':
156+
print(repo.get('id', ''))
157+
break
158+
" 2>/dev/null || true)
159+
160+
if [[ -z "$REPO_ID" ]]; then
161+
echo "NOT_FOUND: No repository with externalId '${EXTERNAL_ID}'."
162+
exit 1
163+
fi
164+
}
165+
166+
# ===========================================================================
167+
# ACTION: register-type — POST a CMIS type definition to the browser binding
168+
# ===========================================================================
169+
# CMIS browser binding accepts createType via:
170+
# POST {CMIS_URL}browser/{repoId}
171+
# form fields: cmisaction=createType, type=<JSON type definition>
172+
# ===========================================================================
173+
action_register_type() {
174+
if [[ -z "$TYPE_FILE" ]]; then
175+
echo "ERROR: --typeFile is required for register-type"
176+
exit 2
177+
fi
178+
if [[ ! -f "$TYPE_FILE" ]]; then
179+
echo "ERROR: Type file not found: $TYPE_FILE"
180+
exit 2
181+
fi
182+
183+
get_token
184+
resolve_repo_id
185+
echo "Registering CMIS secondary type from '${TYPE_FILE}' in repository '${EXTERNAL_ID}' (id: ${REPO_ID})..."
186+
187+
# Read type JSON; the CMIS browser binding accepts the type definition as
188+
# the value of the 'type' form field. Whitespace inside is fine.
189+
local TYPE_JSON
190+
TYPE_JSON=$(cat "$TYPE_FILE")
191+
192+
# Extract typeId for logging / idempotency check.
193+
local INCOMING_TYPE_ID
194+
INCOMING_TYPE_ID=$(echo "$TYPE_JSON" | python3 -c "
195+
import sys, json
196+
print(json.load(sys.stdin).get('id', ''))
197+
" 2>/dev/null || true)
198+
echo "Type id from file: ${INCOMING_TYPE_ID:-<unknown>}"
199+
200+
local CREATE_RESPONSE CREATE_HTTP_CODE CREATE_BODY
201+
CREATE_RESPONSE=$(curl -s -w "\n%{http_code}" \
202+
-X POST "${CMIS_URL}browser/${REPO_ID}" \
203+
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
204+
-F "cmisaction=createType" \
205+
-F "type=${TYPE_JSON}")
206+
CREATE_HTTP_CODE=$(echo "$CREATE_RESPONSE" | tail -n1)
207+
CREATE_BODY=$(echo "$CREATE_RESPONSE" | sed '$d')
208+
209+
case "$CREATE_HTTP_CODE" in
210+
200|201)
211+
echo "SUCCESS: Type '${INCOMING_TYPE_ID}' registered (HTTP ${CREATE_HTTP_CODE})."
212+
exit 0
213+
;;
214+
409|422|400)
215+
# Idempotency: SDM may return one of these when the type already exists.
216+
# Treat as success only if the response body indicates a duplicate / already-exists.
217+
if echo "$CREATE_BODY" | grep -qiE 'already exist|duplicate|exists already|conflict'; then
218+
echo "ALREADY_EXISTS: Type '${INCOMING_TYPE_ID}' already registered (HTTP ${CREATE_HTTP_CODE}). Treating as success."
219+
exit 0
220+
fi
221+
echo "ERROR: Failed to register type '${INCOMING_TYPE_ID}' (HTTP ${CREATE_HTTP_CODE})."
222+
echo "$CREATE_BODY"
223+
exit 1
224+
;;
225+
*)
226+
echo "ERROR: Failed to register type '${INCOMING_TYPE_ID}' (HTTP ${CREATE_HTTP_CODE})."
227+
echo "$CREATE_BODY"
228+
exit 2
229+
;;
230+
esac
231+
}
232+
233+
# ===========================================================================
234+
# ACTION: get-type — verify a CMIS type definition is registered
235+
# ===========================================================================
236+
# CMIS browser binding lookup:
237+
# GET {CMIS_URL}browser/{repoId}?cmisselector=typeDefinition&typeId=<id>
238+
# ===========================================================================
239+
action_get_type() {
240+
if [[ -z "$TYPE_ID" ]]; then
241+
echo "ERROR: --typeId is required for get-type"
242+
exit 2
243+
fi
244+
245+
get_token
246+
resolve_repo_id
247+
echo "Fetching type definition for '${TYPE_ID}' in repository '${EXTERNAL_ID}' (id: ${REPO_ID})..."
248+
249+
local GET_RESPONSE GET_HTTP_CODE GET_BODY
250+
GET_RESPONSE=$(curl -s -w "\n%{http_code}" \
251+
-X GET "${CMIS_URL}browser/${REPO_ID}" \
252+
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
253+
-G \
254+
--data-urlencode "cmisselector=typeDefinition" \
255+
--data-urlencode "typeId=${TYPE_ID}")
256+
GET_HTTP_CODE=$(echo "$GET_RESPONSE" | tail -n1)
257+
GET_BODY=$(echo "$GET_RESPONSE" | sed '$d')
258+
259+
if [[ "$GET_HTTP_CODE" == "200" ]] && echo "$GET_BODY" | grep -q "\"id\":\"${TYPE_ID}\""; then
260+
echo "FOUND: Type '${TYPE_ID}' is registered (HTTP 200)."
261+
exit 0
262+
fi
263+
264+
if [[ "$GET_HTTP_CODE" == "404" ]] || [[ "$GET_HTTP_CODE" == "200" ]]; then
265+
echo "NOT_FOUND: Type '${TYPE_ID}' is not registered (HTTP ${GET_HTTP_CODE})."
266+
echo "$GET_BODY"
267+
exit 1
268+
fi
269+
270+
echo "ERROR: Unexpected HTTP ${GET_HTTP_CODE} when querying type '${TYPE_ID}'."
271+
echo "$GET_BODY"
272+
exit 2
273+
}
274+
275+
# --- Dispatch ---
276+
case "$ACTION" in
277+
register-type) action_register_type ;;
278+
get-type) action_get_type ;;
279+
*)
280+
echo "Unknown action: $ACTION"
281+
echo "Usage: $0 {register-type|get-type} [options]"
282+
exit 2
283+
;;
284+
esac

0 commit comments

Comments
 (0)