Skip to content

Commit 6c927f7

Browse files
Merge pull request #87 from SAP/cf
Fix service instance creation and space guid
2 parents a97d59e + 129b36e commit 6c927f7

File tree

10 files changed

+175
-91
lines changed

10 files changed

+175
-91
lines changed

src/model/configuration.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ export interface IConfiguration {
55
appId?: string;
66
appName?: string;
77
appVersion?: string;
8-
spaceGuid?: string;
9-
orgGuid?: string;
8+
space?: string;
9+
org?: string;
1010
sapCloudService?: string;
1111
destination?: string;
1212
type?: "cf" | "abap";

src/model/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export interface ICreateServiceInstanceParams {
1010
spaceGuid: string;
1111
planName: string;
1212
serviceName: string;
13-
serviceInstanceName?: string;
13+
serviceInstanceName: string;
1414
tags: string[];
1515
parameters?: any;
1616
}

src/repositories/html5RepoManager.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export default class HTML5RepoManager {
2424

2525

2626
private static async getHtml5RepoInfo(configuration: IConfiguration): Promise<IHTML5RepoInfo> {
27-
const spaceGuid = await CFUtil.getSpaceGuid(configuration?.spaceGuid);
27+
const spaceGuid = await CFUtil.getSpaceGuid(configuration?.space);
2828
const credentials = await this.getHTML5Credentials(spaceGuid);
2929
const token = await this.getToken(credentials);
3030
return {
@@ -46,7 +46,8 @@ export default class HTML5RepoManager {
4646
const createParams: ICreateServiceInstanceParams = {
4747
spaceGuid,
4848
planName: PLAN_NAME,
49-
serviceName: SERVIСE_INSTANCE_NAME,
49+
serviceName: "html5-apps-repo",
50+
serviceInstanceName: SERVIСE_INSTANCE_NAME,
5051
tags: ["html5-apps-repo-rt"]
5152
};
5253
const serviceKeys = await CFUtil.getServiceInstanceKeys(getParams, createParams);

src/util/cfUtil.ts

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ICreateServiceInstanceParams, IGetServiceInstanceParams, IResource, IServiceInstance, IServiceKeys, KeyedMap } from "../model/types.js";
2-
import { cfCreateService, cfGetInstanceCredentials, cfGetTarget } from "@sap/cf-tools/out/src/cf-local.js";
2+
import { cfCreateService, cfGetInstanceCredentials } from "@sap/cf-tools/out/src/cf-local.js";
3+
import { getSpaceGuidThrowIfUndefined } from "@sap/cf-tools/out/src/utils.js";
34

45
import { Cli } from "@sap/cf-tools/out/src/cli.js";
56
import { eFilters } from "@sap/cf-tools/out/src/types.js";
@@ -42,15 +43,19 @@ export default class CFUtil {
4243

4344
static async createService(params: ICreateServiceInstanceParams) {
4445
log.verbose(`Creating a service instance with parameters: ${JSON.stringify(params)}`);
45-
const resources = await this.requestCfApi(`/v3/service_plans?names=${params.planName}&space_guids=${params.spaceGuid}`);
46-
const publicPlan = resources.find(resource => resource.visibility_type === "public");
47-
if (!publicPlan) {
48-
throw new Error(`Cannot find a public plan by name '${params.serviceName}' in space '${params.spaceGuid}'`);
46+
const serviceOfferings = await this.requestCfApi(`/v3/service_offerings?names=${params.serviceName}`);
47+
if (serviceOfferings.length === 0) {
48+
throw new Error(`Cannot find a service offering by name '${params.serviceName}'`);
49+
}
50+
const plans = await this.requestCfApi(`/v3/service_plans?service_offering_guids=${serviceOfferings[0].guid}`);
51+
const plan = plans.find(plan => plan.name === params.planName);
52+
if (!plan) {
53+
throw new Error(`Cannot find a plan by name '${params.planName}' for service '${params.serviceName}'`);
4954
}
5055
try {
51-
await cfCreateService(publicPlan.guid, params.serviceName, params.parameters, params.tags);
56+
await cfCreateService(plan.guid, params.serviceInstanceName, params.parameters, params.tags);
5257
} catch (error: any) {
53-
throw new Error(`Cannot create a service instance '${params.serviceName}' in space '${params.spaceGuid}': ${error.message}`);
58+
throw new Error(`Cannot create a service instance '${params.serviceInstanceName}' in space '${params.spaceGuid}': ${error.message}`);
5459
}
5560
}
5661

@@ -176,19 +181,8 @@ export default class CFUtil {
176181
* @memberof CFUtil
177182
*/
178183
static async getSpaceGuid(spaceGuid?: string): Promise<string> {
179-
if (spaceGuid == null) {
180-
const spaceName = (await cfGetTarget())?.space;
181-
if (spaceName) {
182-
const resources = await this.requestCfApi(`/v3/spaces?names=${spaceName}`);
183-
for (const resource of resources) {
184-
spaceGuid = resource.guid;
185-
break;
186-
}
187-
}
188-
}
189-
if (spaceGuid == null) {
190-
throw new Error("Please login to Cloud Foundry with 'cf login' and try again");
191-
}
192-
return spaceGuid;
184+
return spaceGuid ?? getSpaceGuidThrowIfUndefined().catch((e: any) => {
185+
throw new Error("Please specify space and org guids in ui5.yaml or login to Cloud Foundry with 'cf login' and try again: " + e.message);
186+
});
193187
}
194188
}

test/cfUtil.spec.ts

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -325,10 +325,25 @@ describe("CFUtil", () => {
325325
describe("when creating service", () => {
326326
const SPACE_GUID = "spaceGuid1";
327327
const NON_EXISTING_SERVICE_INSTANCE = "nonExistingServiceInstance";
328+
const SERVICE = "service1";
328329
const SERVICE_INSTANCE = "serviceInstance1";
329-
const PLAN = "serviceInstancePlan";
330+
const PLAN = "app-runtime";
330331

331-
it("should create a service with parameters", async () => {
332+
it("should create a service", async () => {
333+
await createService(TestUtil.getResource("service_offerings.json"), TestUtil.getResource("service_plans.json"));
334+
});
335+
336+
it("shouldn't find a plan by name", async () => {
337+
await expect(createService(TestUtil.getResource("service_offerings.json"), JSON.stringify({ resources: [{ name: "app-something" }] })))
338+
.to.be.rejectedWith("Cannot find a plan by name 'app-runtime' for service 'service1'");
339+
});
340+
341+
it("ahouldn't find service offering", async () => {
342+
await expect(createService(JSON.stringify({ resources: [] }), TestUtil.getResource("service_plans.json")))
343+
.to.be.rejectedWith("Cannot find a service offering by name 'service1'");
344+
});
345+
346+
async function createService(serviceOfferings: string, plans: string) {
332347
let call = 0;
333348
const credentialsJson = JSON.parse(TestUtil.getResource("credentials_bs.json"));
334349
const CFUtil = await esmock("../src/util/cfUtil.js", {}, {
@@ -342,8 +357,10 @@ describe("CFUtil", () => {
342357
} else {
343358
return TestUtil.getStdOut(TestUtil.getResource("service_instances_bs.json"));
344359
}
345-
} else if (args[1] === `/v3/service_plans?names=${PLAN}&space_guids=${SPACE_GUID}`) {
346-
return TestUtil.getStdOut(TestUtil.getResource("service_plans.json"));
360+
} else if (args[1] === `/v3/service_offerings?names=${SERVICE}`) {
361+
return TestUtil.getStdOut(serviceOfferings);
362+
} else if (args[1] === `/v3/service_plans?service_offering_guids=B8F4D0AC-9F30-4C18-B808-D8C1C6E2646E`) {
363+
return TestUtil.getStdOut(plans);
347364
}
348365
}
349366
}
@@ -368,7 +385,8 @@ describe("CFUtil", () => {
368385
spaceGuids: [SPACE_GUID],
369386
names: [NON_EXISTING_SERVICE_INSTANCE]
370387
}, {
371-
serviceName: SERVICE_INSTANCE,
388+
serviceName: SERVICE,
389+
serviceInstanceName: SERVICE_INSTANCE,
372390
planName: PLAN,
373391
spaceGuid: SPACE_GUID,
374392
tags: ["tag1"]
@@ -380,33 +398,22 @@ describe("CFUtil", () => {
380398
name: "serviceInstance1"
381399
}
382400
});
383-
});
401+
}
384402

385403
});
386404

387405
describe("when getting space", () => {
388406
it("should get space from cf if not specified in options", async () => {
389407
const CFUtil = await esmock("../src/util/cfUtil.js", {}, {
390-
"@sap/cf-tools/out/src/cli.js": {
391-
Cli: {
392-
execute: (args: string[]) => {
393-
if (args[1] === `/v3/spaces?names=${SPACE_NAME}`) {
394-
return TestUtil.getStdOut(TestUtil.getResource("spaces.json"))
395-
}
396-
}
397-
}
398-
},
399-
"@sap/cf-tools/out/src/cf-local.js": {
400-
cfGetTarget: () => Promise.resolve({ space: SPACE_NAME, "api endpoint": "apiEndpoint", "api version": "apiVersion", user: "user" })
408+
"@sap/cf-tools/out/src/utils.js": {
409+
getSpaceGuidThrowIfUndefined: () => Promise.resolve("spaceGuid1")
401410
}
402411
});
403-
const SPACE_NAME = "spaceName1"
404-
const spaceGuid = await CFUtil.getSpaceGuid();
405-
expect(spaceGuid).to.equal("spaceGuid1");
412+
expect(await CFUtil.getSpaceGuid()).to.equal("spaceGuid1");
406413
});
407414

408415
it("should return space guid specified in options", async () => {
409-
expect(await CFUtil.getSpaceGuid("spaceGuid1")).to.equal("spaceGuid1");
416+
expect(await CFUtil.getSpaceGuid("spaceGuid2")).to.equal("spaceGuid2");
410417
});
411418
});
412419

test/index.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ const OPTIONS: IProjectOptions = {
1919
appId: "appId",
2020
appName: "appName",
2121
appVersion: "appVersion",
22-
spaceGuid: "spaceGuid",
23-
orgGuid: "orgGuid",
22+
space: "spaceGuid",
23+
org: "orgGuid",
2424
sapCloudService: "sapCloudService",
2525
target: {
2626
url: "https://example.sap.com"

test/processors/processor.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ describe("Processor", () => {
3434
appId: "appId",
3535
appName: "appName",
3636
appVersion: "appVersion",
37-
spaceGuid: "spaceGuid",
38-
orgGuid: "orgGuid",
37+
space: "spaceGuid",
38+
org: "orgGuid",
3939
sapCloudService: "sapCloudService"
4040
});
4141
expect(processor instanceof CFProcessor)

test/repositories/html5RepoManager.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ describe("Html5RepoManager", () => {
1717
appId: "appId",
1818
appName: "appName",
1919
appVersion: "appVersion",
20-
spaceGuid: "spaceGuid",
21-
orgGuid: "orgGuid",
20+
space: "spaceGuid",
21+
org: "orgGuid",
2222
sapCloudService: "sapCloudService"
2323
}
2424
};
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
{
2+
"pagination": {
3+
"total_results": 1,
4+
"total_pages": 1,
5+
"first": {
6+
"href": "https://api.btp.sap.com/v3/service_offerings?names=html5-apps-repo\u0026page=1\u0026per_page=50"
7+
},
8+
"last": {
9+
"href": "https://api.btp.sap.com/v3/service_offerings?names=html5-apps-repo\u0026page=1\u0026per_page=50"
10+
},
11+
"next": null,
12+
"previous": null
13+
},
14+
"resources": [
15+
{
16+
"guid": "B8F4D0AC-9F30-4C18-B808-D8C1C6E2646E",
17+
"created_at": "2017-12-14T15:27:22Z",
18+
"updated_at": "2025-03-20T07:08:12Z",
19+
"name": "html5-apps-repo",
20+
"description": "Enables storage of HTML5 applications and provides runtime environment for HTML5 applications.",
21+
"available": true,
22+
"tags": [
23+
"html5appsrepo",
24+
"html5-apps-repo-rt",
25+
"html5-apps-rt",
26+
"html5-apps-repo-dt",
27+
"html5-apps-dt"
28+
],
29+
"requires": [],
30+
"shareable": false,
31+
"documentation_url": "https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/11d77aa154f64c2e83cc9652a78bb985.html",
32+
"broker_catalog": {
33+
"id": "1CBFEF2B-350E-417B-B7BC-0BDE0699CCE3",
34+
"metadata": {
35+
"displayName": "HTML5 Application Repository",
36+
"documentationUrl": "https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/11d77aa154f64c2e83cc9652a78bb985.html",
37+
"longDescription": "The HTML5 Application Repository service enables central storage of HTML5 applications in SAP BTP. In runtime, the service enables the consuming application, typically the application router, to access HTML5 application static content in a secure and efficient manner.",
38+
"supportUrl": "https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/9220a2fd35d84c888c0ae870ca62bfb7.html",
39+
"imageUrl": "data:image/svg+xml;base64,PHN2ZyBpZD0iaHRtbDUtYXBwbGljYXRpb25zIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1NiA1NiI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiMwNTNiNzA7fS5jbHMtMntmaWxsOiMwYTZlZDE7fTwvc3R5bGU+PC9kZWZzPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTQyLjMsMTlhOC4wMDgsOC4wMDgsMCwwLDAtNC4wNzgtNC40QTYuOTQ0LDYuOTQ0LDAsMCwwLDI3Ljc2OSw5LjkyOCw5LjQ1Myw5LjQ1MywwLDAsMCwxOS4wNiw0QzkuMDc4LDQsOS44LDE0LjYyMSw5LjgsMTQuNjIxYTguMzg3LDguMzg3LDAsMCwwLDIuNjEzLDE2LjM2NUgyOC4wMDd2LTNIMTIuNDEzYTUuMzg3LDUuMzg3LDAsMCwxLTEuNjc2LTEwLjUxNWwyLjMwOS0uNzU5TDEyLjc4MywxNC4zYTguMTE0LDguMTE0LDAsMCwxLDEuNS01LjI4NCw2LjQ4NCw2LjQ4NCwwLDAsMSwxMC43LDIuMDIybDEuMzA3LDMuMjlMMjkuMzE4LDEyLjVhMy45MjMsMy45MjMsMCwwLDEsMi4wNDQtLjU5MSwzLjk4OCwzLjk4OCwwLDAsMSwzLjkxNCwzLjI0OWwuMjg5LDEuNTI5LDEuNDE1LjY0NkE0LjgsNC44LDAsMCwxLDM4LjkzMywxOVoiLz48cG9seWdvbiBjbGFzcz0iY2xzLTIiIHBvaW50cz0iMzQuMDcgMjQuNjkxIDM1LjMwOCAyNC42OTEgMzUuMzA4IDI2LjA0NiAzNi42NiAyNi4wNDYgMzYuNjYgMjIgMzUuMzA4IDIyIDM1LjMwOCAyMy4zMzYgMzQuMDcgMjMuMzM2IDM0LjA3IDIyIDMyLjcxOCAyMiAzMi43MTggMjYuMDQ2IDM0LjA3IDI2LjA0NiAzNC4wNyAyNC42OTEiLz48cG9seWdvbiBjbGFzcz0iY2xzLTIiIHBvaW50cz0iMzguNDM5IDI2LjA0NiAzOS43OTIgMjYuMDQ2IDM5Ljc5MiAyMy4zNDIgNDAuOTgzIDIzLjM0MiA0MC45ODMgMjIgMzcuMjQ4IDIyIDM3LjI0OCAyMy4zNDIgMzguNDM5IDIzLjM0MiAzOC40MzkgMjYuMDQ2Ii8+PHBvbHlnb24gY2xhc3M9ImNscy0yIiBwb2ludHM9IjQyLjg5OSAyNC4wNCA0My44MyAyNS40NzkgNDMuODU0IDI1LjQ3OSA0NC43ODQgMjQuMDQgNDQuNzg0IDI2LjA0NiA0Ni4xMzEgMjYuMDQ2IDQ2LjEzMSAyMiA0NC43MiAyMiA0My44NTQgMjMuNDIxIDQyLjk4NiAyMiA0MS41NzYgMjIgNDEuNTc2IDI2LjA0NiA0Mi44OTkgMjYuMDQ2IDQyLjg5OSAyNC4wNCIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtMiIgcG9pbnRzPSI1MC4wNTkgMjQuNzA4IDQ4LjE1NyAyNC43MDggNDguMTU3IDIyIDQ2LjgwNCAyMiA0Ni44MDQgMjYuMDQ2IDUwLjA1OSAyNi4wNDYgNTAuMDU5IDI0LjcwOCIvPjxwb2x5Z29uIGNsYXNzPSJjbHMtMiIgcG9pbnRzPSIzNi4xNyAzNC40OTEgMzYuNjg1IDQwLjI2OCA0MS4zNjMgNDAuMjY4IDQxLjM3NyA0MC4yNjggNDMuOTQ1IDQwLjI2OCA0My43MDIgNDIuOTg2IDQxLjM2MyA0My42MTcgNDEuMzYzIDQzLjYxOCA0MS4zNjEgNDMuNjE4IDM5LjAyNiA0Mi45ODggMzguODc2IDQxLjMxNiAzNy43NDIgNDEuMzE2IDM2Ljc3MSA0MS4zMTYgMzcuMDY1IDQ0LjYwNyA0MS4zNjEgNDUuNzk5IDQxLjM3IDQ1Ljc5NiA0MS4zNyA0NS43OTYgNDUuNjYyIDQ0LjYwNyA0NS42OTMgNDQuMjUzIDQ2LjE4NiAzOC43MzUgNDYuMjM3IDM4LjE3MiA0NS42NzIgMzguMTcyIDQxLjM3NyAzOC4xNzIgNDEuMzYzIDM4LjE3MiAzOC42MDMgMzguMTcyIDM4LjQxMSAzNi4wMjUgNDEuMzcgMzYuMDI1IDQxLjM3NyAzNi4wMjUgNDYuNDI4IDM2LjAyNSA0Ni40MzUgMzYuMDI1IDQ2LjQ3NyAzNS41NTQgNDYuNTczIDM0LjQ5MSA0Ni42MjMgMzMuOTI5IDQxLjM3NyAzMy45MjkgNDEuMzcgMzMuOTI5IDM2LjEyIDMzLjkyOSAzNi4xNyAzNC40OTEiLz48cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik0zMC43NCwyNy45LDMyLjY3NCw0OS41OSw0MS4zNTcsNTJsOC43MDYtMi40MTNMNTIsMjcuOVpNNDcuNjg2LDQ3LjM1OCw0MS4zNyw0OS4xMDlsLTYuMzE2LTEuNzUxTDMzLjU2NywzMC43MTZoMTUuNloiLz48L3N2Zz4=",
40+
"serviceInventoryId": "SERVICE-123",
41+
"sm_offering_id": "0F20B7D8-99F3-42F2-A60A-EE4050FA22F0"
42+
},
43+
"features": {
44+
"plan_updateable": false,
45+
"bindable": true,
46+
"instances_retrievable": false,
47+
"bindings_retrievable": false,
48+
"allow_context_updates": false
49+
}
50+
},
51+
"relationships": {
52+
"service_broker": {
53+
"data": {
54+
"guid": "F76A4507-121A-43E5-8BA4-9604F434652D"
55+
}
56+
}
57+
},
58+
"metadata": {
59+
"labels": {},
60+
"annotations": {}
61+
},
62+
"links": {
63+
"self": {
64+
"href": "https://api.btp.sap.com/v3/service_offerings/B8F4D0AC-9F30-4C18-B808-D8C1C6E2646E"
65+
},
66+
"service_plans": {
67+
"href": "https://api.btp.sap.com/v3/service_plans?service_offering_guids=B8F4D0AC-9F30-4C18-B808-D8C1C6E2646E"
68+
},
69+
"service_broker": {
70+
"href": "https://api.btp.sap.com/v3/service_brokers/F76A4507-121A-43E5-8BA4-9604F434652D"
71+
}
72+
}
73+
}
74+
]
75+
}

0 commit comments

Comments
 (0)