Skip to content

Commit 036dd9e

Browse files
authored
New format for Propensity Score JSON (subscriptions-project#532)
Following changes are addressed - Process new format for Propensity score from server - New strings for subscription state - send version number in request
1 parent e9b0913 commit 036dd9e

6 files changed

+170
-40
lines changed

src/api/propensity-api.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@
1919
*/
2020
export const SubscriptionState = {
2121
// user's subscription state not known.
22-
UNKNOWN: 'na',
22+
UNKNOWN: 'unknown',
2323
// user is not a subscriber.
24-
NON_SUBSCRIBER: 'no',
24+
NON_SUBSCRIBER: 'non_subscriber',
2525
// user is a subscriber.
26-
SUBSCRIBER: 'yes',
26+
SUBSCRIBER: 'subscriber',
2727
// user's subscription has expired.
28-
PAST_SUBSCRIBER: 'ex',
28+
PAST_SUBSCRIBER: 'past_subscriber',
2929
}
3030

3131
/**

src/runtime/propensity-server-test.js

+95-5
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,12 @@ describes.realWin('PropensityServer', {}, env => {
5252
expect(queries).to.not.be.null;
5353
expect('cookie' in queries).to.be.true;
5454
expect(queries['cookie']).to.equal('noConsent');
55+
expect('v' in queries).to.be.true;
56+
expect(parseInt(queries['v'], 10)).to.equal(
57+
propensityServer.version_);
5558
expect('states' in queries).to.be.true;
5659
const userState = 'pub1:' + queries['states'].split(':')[1];
57-
expect(userState).to.equal('pub1:yes');
60+
expect(userState).to.equal('pub1:subscriber');
5861
const products = decodeURIComponent(queries['states'].split(':')[2]);
5962
expect(products).to.equal(JSON.stringify(entitlements));
6063
expect(capturedRequest.credentials).to.equal('include');
@@ -86,6 +89,8 @@ describes.realWin('PropensityServer', {}, env => {
8689
expect(queries).to.not.be.null;
8790
expect('cookie' in queries).to.be.true;
8891
expect(queries['cookie']).to.equal('noConsent');
92+
expect('v' in queries).to.be.true;
93+
expect(parseInt(queries['v'], 10)).to.equal(propensityServer.version_);
8994
expect('events' in queries).to.be.true;
9095
const events = decodeURIComponent(queries['events'].split(':')[2]);
9196
expect(events).to.equal(JSON.stringify(eventParam));
@@ -115,6 +120,9 @@ describes.realWin('PropensityServer', {}, env => {
115120
expect(queries).to.not.be.null;
116121
expect('cookie' in queries).to.be.true;
117122
expect(queries['cookie']).to.equal('noConsent');
123+
expect('v' in queries).to.be.true;
124+
expect(parseInt(queries['v'], 10)).to.equal(
125+
propensityServer.version_);
118126
expect('products' in queries).to.be.true;
119127
expect(queries['products']).to.equal('pub1');
120128
expect('type' in queries).to.be.true;
@@ -126,19 +134,95 @@ describes.realWin('PropensityServer', {}, env => {
126134
});
127135

128136
it('should test get propensity', () => {
129-
const score = {'values': [42]};
137+
const propensityResponse = {
138+
'header': {'ok': true},
139+
'scores': [
140+
{
141+
'product': 'pub1',
142+
'score': 90,
143+
},
144+
{
145+
'product': 'pub1:premium',
146+
'error_message': 'not available',
147+
},
148+
],
149+
};
150+
const response = new Response();
151+
const mockResponse = sandbox.mock(response);
152+
mockResponse.expects('json').returns(
153+
Promise.resolve(propensityResponse)).once();
154+
sandbox.stub(Xhr.prototype, 'fetch',
155+
() => {
156+
return Promise.resolve(response);
157+
});
158+
return propensityServer.getPropensity('/hello',
159+
PropensityApi.PropensityType.GENERAL).then(response => {
160+
expect(response).to.not.be.null;
161+
const header = response['header'];
162+
expect(header).to.not.be.null;
163+
expect(header['ok']).to.be.true;
164+
const body = response['body'];
165+
expect(body).to.not.be.null;
166+
expect(body['result']).to.equal(90);
167+
});
168+
});
169+
170+
it('should test only get propensity score for pub', () => {
171+
const propensityResponse = {
172+
'header': {'ok': true},
173+
'scores': [
174+
{
175+
'product': 'pub2',
176+
'score': 90,
177+
},
178+
{
179+
'product': 'pub1:premium',
180+
'error_message': 'not available',
181+
},
182+
],
183+
};
184+
const response = new Response();
185+
const mockResponse = sandbox.mock(response);
186+
mockResponse.expects('json').returns(
187+
Promise.resolve(propensityResponse)).once();
188+
sandbox.stub(Xhr.prototype, 'fetch',
189+
() => {
190+
return Promise.resolve(response);
191+
});
192+
return propensityServer.getPropensity('/hello',
193+
PropensityApi.PropensityType.GENERAL).then(response => {
194+
expect(response).to.not.be.null;
195+
const header = response['header'];
196+
expect(header).to.not.be.null;
197+
expect(header['ok']).to.be.false;
198+
const body = response['body'];
199+
expect(body).to.not.be.null;
200+
expect(body['result']).to.equal('No score available for pub1');
201+
});
202+
});
203+
204+
it('should test no propensity score available', () => {
205+
const propensityResponse = {
206+
'header': {'ok': false},
207+
'error': 'Service not available',
208+
};
130209
const response = new Response();
131210
const mockResponse = sandbox.mock(response);
132-
mockResponse.expects('json').returns(Promise.resolve(score)).once();
211+
mockResponse.expects('json').returns(
212+
Promise.resolve(propensityResponse)).once();
133213
sandbox.stub(Xhr.prototype, 'fetch',
134214
() => {
135215
return Promise.resolve(response);
136216
});
137217
return propensityServer.getPropensity('/hello',
138218
PropensityApi.PropensityType.GENERAL).then(response => {
139219
expect(response).to.not.be.null;
140-
expect('values' in response).to.be.true;
141-
expect(response.values[0]).to.equal(42);
220+
const header = response['header'];
221+
expect(header).to.not.be.null;
222+
expect(header['ok']).to.be.false;
223+
const body = response['body'];
224+
expect(body).to.not.be.null;
225+
expect(body['result']).to.equal('Service not available');
142226
});
143227
});
144228

@@ -164,6 +248,9 @@ describes.realWin('PropensityServer', {}, env => {
164248
expect(queries).to.not.be.null;
165249
expect('cookie' in queries).to.be.true;
166250
expect(queries['cookie']).to.equal('aaaaaa');
251+
expect('v' in queries).to.be.true;
252+
expect(parseInt(queries['v'], 10)).to.equal(
253+
propensityServer.version_);
167254
expect('products' in queries).to.be.true;
168255
expect(queries['products']).to.equal('pub1');
169256
expect('type' in queries).to.be.true;
@@ -197,6 +284,9 @@ describes.realWin('PropensityServer', {}, env => {
197284
const queries = parseQueryString(queryString);
198285
expect(queries).to.not.be.null;
199286
expect('cookie' in queries).to.be.false;
287+
expect('v' in queries).to.be.true;
288+
expect(parseInt(queries['v'], 10)).to.equal(
289+
propensityServer.version_);
200290
expect('products' in queries).to.be.true;
201291
expect(queries['products']).to.equal('pub1');
202292
expect('type' in queries).to.be.true;

src/runtime/propensity-server.js

+64-11
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ export class PropensityServer {
3737
this.userConsent_ = false;
3838
/** @private @const {!Xhr} */
3939
this.xhr_ = new Xhr(win);
40+
/** @private @const {number} */
41+
this.version_ = 1;
4042
}
4143

4244
/**
@@ -92,11 +94,11 @@ export class PropensityServer {
9294
userState = userState + ':' + encodeURIComponent(entitlements);
9395
}
9496
let url = ServiceUrl.adsUrl('/subopt/data?states=')
95-
+ encodeURIComponent(userState);
97+
+ encodeURIComponent(userState) + '&u_tz=240'
98+
+ '&v=' + this.version_;
9699
if (clientId) {
97100
url = url + '&cookie=' + clientId;
98101
}
99-
url = url + '&u_tz=240';
100102
return this.xhr_.fetch(url, init);
101103
}
102104

@@ -115,18 +117,71 @@ export class PropensityServer {
115117
eventInfo = eventInfo + ':' + encodeURIComponent(context);
116118
}
117119
let url = ServiceUrl.adsUrl('/subopt/data?events=')
118-
+ encodeURIComponent(eventInfo);
120+
+ encodeURIComponent(eventInfo) + '&u_tz=240'
121+
+ '&v=' + this.version_;
119122
if (clientId) {
120123
url = url + '&cookie=' + clientId;
121124
}
122-
url = url + '&u_tz=240';
123125
return this.xhr_.fetch(url, init);
124126
}
125127

128+
/**
129+
* @param {JsonObject} response
130+
* @return {!../api/propensity-api.PropensityScore}
131+
*/
132+
parsePropensityResponse_(response) {
133+
let defaultScore =
134+
/** @type {!../api/propensity-api.PropensityScore} */ ({});
135+
if (!response['header']) {
136+
defaultScore =
137+
/** @type {!../api/propensity-api.PropensityScore} */ ({
138+
header: {ok: false},
139+
body: {result: 'No valid response'},
140+
});
141+
}
142+
const status = response['header'];
143+
if (status['ok']) {
144+
const scores = response['scores'];
145+
let found = false;
146+
for (let i = 0; i < scores.length; i++) {
147+
const result = scores[i];
148+
if (result['product'] == this.publicationId_) {
149+
found = true;
150+
const scoreStatus = !!result['score'];
151+
let value = undefined;
152+
if (scoreStatus) {
153+
value = result['score'];
154+
} else {
155+
value = result['error_message'];
156+
}
157+
defaultScore =
158+
/** @type {!../api/propensity-api.PropensityScore} */ ({
159+
header: {ok: scoreStatus},
160+
body: {result: value},
161+
});
162+
break;
163+
}
164+
}
165+
if (!found) {
166+
const errorMessage = 'No score available for ' + this.publicationId_;
167+
defaultScore = /** @type {!../api/propensity-api.PropensityScore} */ ({
168+
header: {ok: false},
169+
body: {result: errorMessage},
170+
});
171+
}
172+
} else {
173+
const errorMessage = response['error'];
174+
defaultScore = /** @type {!../api/propensity-api.PropensityScore} */ ({
175+
header: {ok: false},
176+
body: {result: errorMessage},
177+
});
178+
}
179+
return defaultScore;
180+
}
126181
/**
127182
* @param {string} referrer
128183
* @param {string} type
129-
* @return {?Promise<JsonObject>}
184+
* @return {?Promise<../api/propensity-api.PropensityScore>}
130185
*/
131186
getPropensity(referrer, type) {
132187
const clientId = this.getClientId_();
@@ -136,16 +191,14 @@ export class PropensityServer {
136191
});
137192
let url = ServiceUrl.adsUrl('/subopt/pts?products=') + this.publicationId_
138193
+ '&type=' + type + '&u_tz=240'
139-
+ '&ref=' + referrer;
194+
+ '&ref=' + referrer
195+
+ '&v=' + this.version_;
140196
if (clientId) {
141197
url = url + '&cookie=' + clientId;
142198
}
143199
return this.xhr_.fetch(url, init).then(result => result.json())
144-
.then(score => {
145-
if (!score) {
146-
score = "{'values': [-1]}";
147-
}
148-
return score;
200+
.then(response => {
201+
return this.parsePropensityResponse_(response);
149202
});
150203
}
151204
}

src/runtime/propensity-test.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,10 @@ describes.realWin('Propensity', {}, env => {
127127
() => {
128128
return new Promise(resolve => {
129129
setTimeout(() => {
130-
resolve({'values': [42]});
130+
resolve({
131+
'header': {'ok': true},
132+
'body': {'result': 42},
133+
});
131134
}, 10);
132135
});
133136
});

src/runtime/propensity.js

+1-17
Original file line numberDiff line numberDiff line change
@@ -57,23 +57,7 @@ export class Propensity {
5757
type = PropensityApi.PropensityType.GENERAL;
5858
}
5959
return this.propensityServer_.getPropensity(this.win_.document.referrer,
60-
type).then(result => {
61-
// TODO(sohanirao): Match HTTP interface
62-
let propensityScore = undefined;
63-
const score = result && result['values'];
64-
if (!score) {
65-
propensityScore = {
66-
header: {ok: false},
67-
body: {result: 'no score available'},
68-
};
69-
} else {
70-
propensityScore = {
71-
header: {ok: true},
72-
body: {result: score[0]},
73-
};
74-
}
75-
return propensityScore;
76-
});
60+
type);
7761
}
7862

7963
/** @override */

src/runtime/runtime-test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1447,9 +1447,9 @@ describes.realWin('ConfiguredRuntime', {}, env => {
14471447
() => Promise.resolve(propensityResponse));
14481448
return runtime.getPropensityModule().then(propensity => {
14491449
expect(propensity).to.not.be.null;
1450-
propensity.sendSubscriptionState('na');
1450+
propensity.sendSubscriptionState('unknown');
14511451
propensity.sendEvent('expired');
1452-
expect(sendSubscriptionStateStub).to.be.calledWithExactly('na');
1452+
expect(sendSubscriptionStateStub).to.be.calledWithExactly('unknown');
14531453
expect(eventStub).to.be.calledWithExactly('expired');
14541454
return propensity.getPropensity().then(score => {
14551455
expect(score).to.not.be.null;

0 commit comments

Comments
 (0)