Since VPSes are bound to offers, we need to modify the existing VPS views as illustrated in this document.
In this document:
In the following updates we assume the following:
Every VPS is linked with an offer as specified by the updated resource model.
When creating or editing a VPS the limits on VPS parameters cannot exceed the limits set in the offer the VPS is linked with.
The OS for a new VPS will be defined by the selected offer. So, we do not need to have a predefined OS selection list.
Unlike the offer management in PCP, the VPSes are managed in UX1. Respectively, you will use the single page application technology in the following updates.
This section continues the demo project from the previous step.
You should update the server-new-1
(server creation) and server-edit
(server editor) views.
During the VPS creation process, we must present a list of offers for selection. Each offer has its own limits on
VPS properties.
Thus, the server-new-1.js
file needs substantial updates.
In the required
and main callback function, add some more aps
and dojo
modules required by the updated code
and also add the newoffer.json
file to initiate the current offer control object:
require([
//... leave the existing modules intact and add the following ones:
"dojo/when",
"aps/ResourceStore",
//...,
"aps/json!app/newoffer.json" // "app" is mapped to "ui/" in bootstrapApp.html
], function (
// ...,
when,
Store,
//...,
newOffer
) {
var self; // A global object to keep some objects for all functions
return declare(_View, {
init: function() {
self = this;
//... function contents
},
onNext: function() { /*... function contents */ },
onContext: function() { /*... function contents */ }
});
In the above code, you will also find a declaration and initialization of a global object self
to keep
in it some objects for all functions of the module you are developing.
In the init
method, change data sources as follows.
Remove the oses
object since now an OS will be assigned through the selected offer.
Define a memory cache that will contain a list of offers to propose to a user:
self.offerCache = new Memory({
data: [],
idProperty: "aps.id"
});
Define an offer control object that will store a JSON representation of the current offer.
Initialize the object using the values from the newoffer.json
file:
self.offerCtrl = getStateful({
model: newOffer
});
The values of the current offer must be used as the limits to the properties of the new VPS.
In the init
method, add a function activated on offer change. This handler must change the values of the current offer
representation and bind the new VPS with the selected offer:
function onChangeOffer(offerID) {
console.log("Offer ID is changing for: " + offerID); // For debugging only
when(self.offerCache.get(offerID), function(data) {
self.offerCtrl.set("model", data); // Update the current offer
});
}
When a subscriber selects an offer, the onChangeOffer()
function is called with the single input argument
presenting the APS ID of the selected offer. The function reads the offer from the offer cache and then
updates the offer control. This updates the current limits on VPS properties.
In the widget definition, add a panel with a selection list that allows a subscriber to select an offer.
When an offer is selected, the widget must call the onChangeOffer
handler defined earlier.
["aps/Panel", {id: this.genId("srvNew1_offers_panel"), title: "Offering"}, [
["aps/FieldSet", {id: this.genId("srvNew1_offers")}, [
["aps/Select", {
id: self.genId("srvNew1_offerSelect"),
gridSize: 'md-3 xs-12',
store: self.offerCache,
required: true,
label: "Service profile:",
onChange: onChangeOffer,
labelAttr: "name",
value: at(self.vpsModel.data.offer.aps, "id")
}]
]]
]],
In the widget definition, make the os.name
property assigned from the current offer.
For this, replace the aps/Select
widget with the aps/Output
widget as follows:
["aps/Output", {
id: this.genId("new1_os"),
label: "Operating System:",
value: at(self.offerCtrl.model.platform.OS, "name")
}],
Set the maximum
values of the main resources to the corresponding values in the current offer.
For this, update the aps/Spinner
widgets as follows.
For the CPU Cores:
maximum: at(self.offerCtrl.model.hardware.CPU, "number"),
For the Disk Space:
maximum: at(self.offerCtrl.model.hardware, "diskspace"),
For the RAM:
maximum: at(self.offerCtrl.model.hardware, "memory"),
In the new onContext
method, add an offer store offerStore
to communicate with the APS controller.
Use this store to query the available offers and update the offer cache offerCache
. Make the
first offer from the list to be the current offer offerCtrl
.
Then update the offer selection widget and bind the new VPS to the current offer.
onContext: function() {
if (!aps.context.wizardData["http://aps-standard.org/samples/offer1p#server-new-1"]) {
var offerStore = new Store({ // The list of offers for selection
apsType: "http://aps-standard.org/samples/offer1p/offer/1.0",
target: "/aps/2/resources/"
});
offerStore.query().then(function(offers) {
if (offers.length === 0) return;
self.offerCache.setData(offers);
self.offerCtrl.set("model", offers[0]);
self.vpsModel.data.offer.aps.set("id", self.offerCtrl.model.aps.id);
self.byId("srvNew1_offerSelect").set("store", self.offerCache);
}).then(function() {
aps.apsc.hideLoading();
});
}
else aps.apsc.hideLoading();
}
In the onNext
handler, copy the currently selected OS name to the VPS model. So, the handler
will look as follows:
onNext: function() {
var form = this.byId("srvNew_form");
/* Validate the values assigned to widgets */
if (!form.validate()) {
aps.apsc.cancelProcessing();
return;
}
self.vpsModel.data.platform.OS.set("name", self.offerCtrl.model.platform.OS.name);
/* Proceed to the next screen */
aps.apsc.next(self.vpsModel.data);
},
The server-new-last.js
file does not require any substantial changes.
The server-edit.js
view is very similar to the view used for creating
a VPS. It requires similar updates but also has some specifics:
The view must work with the selected VPS presented by the vps
variable as specified in the navigation metadata.
There is no need in data wizard, thus all data is stored in the APS database on clicking the Submit button.
Update the view source as follows.
In the required
and main callback function, add some more aps
and dojo
modules required by the updated code
and also add the newoffer.json
file to initiate the current offer control object:
require([
//... leave the existing modules intact and add the following ones:
"dojo/promise/all",
//...,
"aps/Memory",
"aps/xhr",
//...,
"aps/json!./newoffer.json"
], function (
// ...,
all,
//...,
Memory,
xhr,
//...,
newOffer
) {
var self; // A global object to keep some objects for all functions
return declare(View, {
init: function() {
self = this;
//... function contents
},
onCancel: function() { /*... function contents */ },
onSubmit: function() { /*... function contents */ },
onContext(context): function() { /*... function contents */ }
});
In the init
method, change data sources as follows.
Define a memory cache that will contain a list of offers to propose to a user:
self.offerCache = new Memory({
"data": [],
idProperty: "aps.id"
});
Define an offer control object that will store a JSON representation of the current offer.
Initialize the object using the values from the newoffer.json
file:
self.offerCtrl = getStateful({
model: newOffer
});
The values of the current offer must be used as the limits to the properties of the new VPS.
The initial offer can be changed for the actual offer only in the onContext
method, where
the vps
variable and the related offer are available.
In the init
method, add a handler activated on offer change. This handler must change the values of the current offer
and bind the new VPS with the selected offer:
function onChangeOffer(offerID) {
console.log("Changed for offer: " + offerID); // For debugging only
when(self.offerCache.get(offerID), function(data) {
self.offerCtrl.set("model", data); // Update the current offer
});
}
When a subscriber selects an offer, the onChangeOffer()
function is called with the single input argument
presenting the APS ID of the selected offer. The function reads the offer from the offer cache and then
updates the offer control. This updates the current limits on VPS properties.
In the widget definition, add a panel with a selection list that allows a subscriber to select an offer.
When an offer is selected, the widget must call the onChangeOffer
handler defined earlier.
["aps/Panel", {id: this.genId("srvEdit_offers_panel"), title: "Offer"}, [
["aps/FieldSet", {id: this.genId("srvEdit_offers")}, [
["aps/Select", {
id: this.genId("srvEdit_offerSelect"),
gridSize: 'md-3 xs-12',
store: self.offerCache,
required: true,
label: "Bundle",
onChange: onChangeOffer,
labelAttr: "name",
value: at(self.vpsModel.data.offer.aps, "id")
}]
]]
]],
In the widget definition, make the os.name
property assigned from the current offer.
For this, add the aps/Output
widget as follows:
["aps/Output", {
id: this.genId("srvEdit_os"),
label: "OS",
value: at(self.offerCtrl.model.platform.OS, "name")
}],
Set the maximum
values of the main resources to the corresponding values in the current offer.
For this, update the aps/Spinner
widgets as follows.
For the CPU Cores:
maximum: at(self.offerCtrl.model.hardware.CPU, "number"),
For the Disk Space:
maximum: at(self.offerCtrl.model.hardware, "diskspace"),
For the RAM:
maximum: at(self.offerCtrl.model.hardware, "memory"),
In the onContext(context)
method, set the current values in the VPS model
and in the offerCtrl
model
using the vps
navigation variable.
Add an offer store offerStore
to communicate with the APS controller.
Use this store to query the available offers and update the offer cache offerCache
.
var offerStore = new Store({ // The list of offers for selection
apsType: "http://aps-standard.org/samples/offer1p/offer/1.0",
target: "/aps/2/resources/"
});
self.vpsModel.set("data", context.vars.vps);
self.offerCtrl.set("model", context.vars.vps.offer);
offerStore.query().then(function(offers) {
self.offerCache.setData(offers);
self.byId("srvEdit_offerSelect").set("store", self.offerCache);
})
.then(function() {
aps.apsc.hideLoading();
});
Rewrite completely the onSubmit
button handler.
// The following results in addition of the subscription ID as a header to xhr requests:
aps.context.subscriptionId = aps.context.vars.vps.aps.subscription;
var vpsStore = new Store({ // Interface with the APS controller
apsType: "http://aps-standard.org/samples/offer1p/vps/1.0",
target: "/aps/2/resources/"
});
var vps = getPlainValue(self.vpsModel.data), // New properties to save
reqs = []; /* We may have one or two REST requests */
/* If the offer has to be changed, we need to first request re-linking the VPS
to the other offer */
if(vps.offer.aps.id != aps.context.vars.vps.offer.aps.id)
reqs.push(xhr("/aps/2/resources/" + vps.aps.id + "/offer",
{ method: "POST",
data: JSON.stringify({ "aps": { "id": vps.offer.aps.id } })
}
));
vps.platform.OS.name = self.offerCtrl.model.platform.OS.name;
reqs.push(vpsStore.put(vps)); /* And now update the VPS properties */
all(reqs).then(function() { /* Once the requests are completed,
navigate to the "servers" view */
aps.apsc.gotoView("servers");
},
displayError
);
Keynotes:
If the VPS must be re-linked to another offer, we need to call explicitly the POST request specifying the VPS ID in the URL field and the offer ID in the request body as explained in Relinking Resources.
If the re-link operation is needed, the reqs
list will contain two REST requests, otherwise it will have
only one REST PUT request for updating the VPS properties.
This completes the updates of the views managing VPSes.
The project files you have updated are similar to the respective files in the
sample package
.