Skip to content

Commit c5dd11c

Browse files
committed
Merge pull request #1 from Backendless/vitaly/services
Vitaly/services
2 parents 6c53a2d + 598f1c2 commit c5dd11c

39 files changed

+1875
-463
lines changed

examples/models/order.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* global Backendless */
2+
3+
'use strict';
4+
5+
class Order extends Backendless.ServerCode.PersistenceItem {
6+
constructor(items) {
7+
super();
8+
9+
/**
10+
* @type {Array.<ShoppingItem>}
11+
*/
12+
this.items = items || [];
13+
14+
/**
15+
* @type {Number}
16+
*/
17+
this.orderPrice = this.items.reduce((sum, item) => {
18+
return (sum || 0) + (item.price * item.quantity);
19+
}, 0);
20+
}
21+
}
22+
23+
module.exports = Backendless.ServerCode.addType(Order);

examples/models/shopping-item.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/* global Backendless */
2+
3+
'use strict';
4+
5+
/**
6+
* @property {String} objectId
7+
* @property {String} product
8+
* @property {Number} price
9+
* @property {Number} quantity
10+
*/
11+
class ShoppingItem extends Backendless.ServerCode.PersistenceItem {
12+
13+
}
14+
15+
module.exports = Backendless.ServerCode.addType(ShoppingItem);

examples/services/greetings.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* global Backendless */
2+
3+
'use strict';
4+
5+
const DICT = {};
6+
DICT['English'] = 'Welcome';
7+
DICT['German'] = 'Willkommen';
8+
DICT['Spanish'] = 'Bienvenido';
9+
10+
class GreetingsService {
11+
/**
12+
* @param {String} userName
13+
* @returns {String}
14+
*/
15+
getGreeting(userName) {
16+
return `${DICT[this.config.lang]} ${userName} !`;
17+
}
18+
}
19+
20+
Backendless.ServerCode.addService(GreetingsService, [
21+
{
22+
name : 'lang',
23+
type : 'choice',
24+
displayName : 'Language',
25+
required : true,
26+
defaultValue: 'English',
27+
options : ['English', 'German', 'Spanish'],
28+
hint : 'Please select a Greetings Language'
29+
}
30+
]);

examples/services/shopping-cart.js

Lines changed: 99 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2,98 +2,141 @@
22

33
'use strict';
44

5-
class Order {
6-
constructor(items) {
7-
/**
8-
* @type {Array.<ShoppingItem>}
9-
*/
10-
this.items = items;
11-
12-
/**
13-
* @type {Number}
14-
*/
15-
this.orderPrice = items.reduce((sum, item) => (sum || 0) + (item.price * item.quantity));
16-
}
17-
}
5+
const Order = require('../models/order');
186

197
class ShoppingCart {
20-
constructor() {
21-
this.items = [];
8+
constructor(opts) {
9+
opts = opts || {};
10+
11+
this.name = opts.name;
12+
this.items = opts.items || [];
13+
this.___class = ShoppingCart.name;
2214
}
2315

2416
addItem(item) {
17+
item.objectId = null;
18+
2519
this.items.push(item);
2620
}
2721

22+
deleteItem(product) {
23+
const idx = this.items.findIndex(item => item.product === product);
24+
25+
if (idx === -1) {
26+
throw new Error(`No ${product} in cart`);
27+
}
28+
29+
this.items.splice(idx, 1);
30+
31+
return this;
32+
}
33+
34+
setQuantity(product, quantity) {
35+
const productItem = this.items.find(item => item.product === product);
36+
37+
if (!productItem) {
38+
throw new Error(`No [${product}] in cart`);
39+
}
40+
41+
productItem.quantity = quantity;
42+
43+
return this;
44+
}
45+
2846
getItems() {
2947
return this.items;
3048
}
49+
50+
destroy() {
51+
Backendless.Cache.remove(this.name, this);
52+
}
53+
54+
save() {
55+
Backendless.Cache.put(this.name, this);
56+
}
57+
58+
static get(name, mustExist) {
59+
Backendless.Cache.setObjectFactory(ShoppingCart.name, ShoppingCart);
60+
61+
return Backendless.Cache.get(name).then(cart => {
62+
if (!cart && mustExist) {
63+
throw new Error(`Shopping cart [${name}] does not exist`);
64+
}
65+
66+
return cart;
67+
});
68+
}
3169
}
3270

3371
class ShoppingCartService {
3472

3573
/**
36-
* A custom type can be described in jsdoc typedef declaration without having its own class
37-
* This will help to build a valid Swager doc but the CodeRunner won't be able to transform a plain JS object
38-
* from the request into concrete class.
39-
*
40-
* @typedef {Object} ShoppingItem
41-
* @property {String} objectId
42-
* @property {String} product
43-
* @property {Number} price
44-
* @property {Number} quantity
45-
* */
46-
47-
/**
48-
* @public
4974
* @param {String} cartName
5075
* @param {ShoppingItem} item
51-
* @returns {void}
76+
* @returns {Promise.<void>}
5277
*/
5378
addItem(cartName, item) {
54-
let shoppingCart = this.getCart(cartName);
79+
return this.addItems(cartName, [item]);
80+
}
5581

56-
if (!shoppingCart) {
57-
shoppingCart = new ShoppingCart();
58-
}
82+
/**
83+
* @param {String} cartName
84+
* @param {Array.<ShoppingItem>} items
85+
* @returns {Promise.<void>}
86+
*/
87+
addItems(cartName, items) {
88+
return ShoppingCart.get(cartName).then(cart => {
89+
if (!cart) {
90+
cart = new ShoppingCart({ name: cartName });
91+
}
5992

60-
shoppingCart.addItem(item);
61-
item.objectId = null;
93+
items.forEach(item => cart.addItem(item));
6294

63-
Backendless.Cache.put(cartName, shoppingCart);
95+
return cart.save();
96+
});
6497
}
6598

6699
/**
67-
* @public
68100
* @param {String} cartName
69-
* @returns {Promise.<Order>}
101+
* @param {String} product
102+
* @returns {Promise.<void>}
70103
*/
71-
purchase(cartName) {
72-
const shoppingCart = this.getCart(cartName);
73-
74-
if (!shoppingCart) {
75-
throw new Error(`Shopping cart ${cartName} does not exist`);
76-
}
77-
78-
const order = new Order(shoppingCart.getItems());
104+
deleteItem(cartName, product) {
105+
return ShoppingCart.get(cartName, true).then(cart => cart.deleteItem(product).save());
106+
}
79107

80-
return order.save()
81-
.then(() => {
82-
Backendless.Cache.delete(cartName);
108+
/**
109+
* @param {String} cartName
110+
* @returns {Promise.<Array.<ShoppingItem>>}
111+
*/
112+
getItems(cartName) {
113+
return ShoppingCart.get(cartName, true).then(cart => cart.getItems());
114+
}
83115

84-
return order;
85-
});
116+
/**
117+
* @param {String} cartName
118+
* @param {String} productName
119+
* @param {Number} quantity
120+
* @returns {Promise.<void>}
121+
*/
122+
setQuantity(cartName, productName, quantity) {
123+
return ShoppingCart.get(cartName, true).then(cart => cart.setQuantity(productName, quantity).save());
86124
}
87125

88126
/**
89-
* @private
90127
* @param {String} cartName
91-
* @returns {ShoppingCart}
128+
* @returns {Promise.<Order>}
92129
*/
93-
static getCart(cartName) {
94-
return Backendless.Cache.get(cartName, ShoppingCart.class);
130+
purchase(cartName) {
131+
return ShoppingCart.get(cartName, true).then(cart => {
132+
const order = new Order(cart.getItems());
133+
134+
return order.save()
135+
.then(() => cart.destroy())
136+
.then(() => order);
137+
});
95138
}
96139
}
97140

98-
Backendless.ServerCode.addType(Order);
141+
Backendless.enablePromises();
99142
Backendless.ServerCode.addService(ShoppingCartService);
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/* global Backendless */
2+
3+
'use strict';
4+
5+
//you should add 'easysoap' module to your project dependencies as [npm i easysoap --save]
6+
const easysoap = require('easysoap');
7+
const soapClient = easysoap.createClient({
8+
host: 'http://ws.cdyne.com/',
9+
path: 'ip2geo/ip2geo.asmx',
10+
wsdl: 'ip2geo/ip2geo.asmx?WSDL'
11+
});
12+
13+
/**
14+
* @typedef {Object} ResolveIPRequest
15+
* @property {String} ipAddress
16+
* @property {String} licenseKey
17+
*/
18+
19+
/**
20+
* @typedef {Object} ResolveIPResponse
21+
* @property {IPInformation} ResolveIPResult
22+
*/
23+
24+
/**
25+
* @typedef {Object} IPInformation
26+
* @property {String} City
27+
* @property {String} StateProvince
28+
* @property {String} Country
29+
* @property {String} Organization
30+
* @property {Number} Latitude
31+
* @property {Number} Longitude
32+
* @property {String} AreaCode
33+
* @property {String} TimeZone
34+
* @property {Boolean} HasDaylightSavings
35+
* @property {Number} Certainty
36+
* @property {String} RegionName
37+
* @property {String} CountryCode
38+
*/
39+
40+
/**
41+
* http://ws.cdyne.com/ip2geo/ip2geo.asmx?op=ResolveIP
42+
*/
43+
class Ip2Geo {
44+
/**
45+
* @param {ResolveIPRequest} request
46+
* @returns {ResolveIPResponse}
47+
*/
48+
resolveIp(request) {
49+
return soapClient.call({
50+
method: 'ResolveIP',
51+
attributes: {
52+
xmlns: 'http://ws.cdyne.com/'
53+
},
54+
params: request
55+
}).then(callResponse => callResponse.data);
56+
}
57+
}
58+
59+
Backendless.ServerCode.addService(Ip2Geo);

examples/timers/heartbeat.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Backendless.ServerCode.addTimer({
66

77
frequency: {
88
schedule: 'custom',
9-
repeat : { every: 60 }
9+
repeat : { every: 600 }
1010
},
1111

1212
execute: function() {

lib/index.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
'use strict';
22

3-
global.Backendless = require('backendless');
4-
global.Backendless.ServerCode = require('./server-code/api');
3+
require('backendless').ServerCode = require('./server-code/api');
54

65
exports.debug = function(opts) {
76
return require('./server-code/runners/debug').start(opts);

0 commit comments

Comments
 (0)