fiftyone.pipeline.cloudrequestengine 4.5.46 → 4.5.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -422,10 +422,16 @@ class CloudRequestEngine extends Engine {
422
422
  */
423
423
  addQueryData (flowData, queryData, allEvidence, evidence) {
424
424
  for (const [evidenceKey, evidenceValue] of Object.entries(evidence)) {
425
- // Get the key parts
426
- const evidenceKeyParts = evidenceKey.split('.');
427
- const prefix = evidenceKeyParts[0];
428
- const suffix = evidenceKeyParts[1];
425
+ // Split the key at its first dot only. The prefix is the part before
426
+ // it and the name sent to the cloud is everything after it, so a name
427
+ // that itself contains a dot, such as id.usage, is sent whole.
428
+ const separatorIndex = evidenceKey.indexOf('.');
429
+ const prefix = separatorIndex >= 0
430
+ ? evidenceKey.substring(0, separatorIndex)
431
+ : evidenceKey;
432
+ const suffix = separatorIndex >= 0
433
+ ? evidenceKey.substring(separatorIndex + 1)
434
+ : evidenceKey;
429
435
 
430
436
  // Check and add the evidence to the query parameters.
431
437
  if ((suffix in queryData) === false) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fiftyone.pipeline.cloudrequestengine",
3
- "version": "4.5.46",
3
+ "version": "4.5.48",
4
4
  "description": "Cloud request engine for the 51Degrees Pipeline API",
5
5
  "keywords": [
6
6
  "51degrees",
@@ -208,3 +208,60 @@ each([
208
208
  const result = engine.getContent(data);
209
209
  expect(result['User-Agent']).toBe(expectedValue);
210
210
  });
211
+
212
+ /**
213
+ * Test that an evidence name which itself contains a dot keeps every part
214
+ * after the prefix. The cloud service reads the 51Did usage from id.usage,
215
+ * so evidence named query.id.usage must be sent as id.usage and not cut
216
+ * down to id, otherwise the service never creates a 51Did.
217
+ */
218
+ each([
219
+ ['query.id.usage', 'standard', 'id.usage'],
220
+ ['header.id.usage', 'standard', 'id.usage'],
221
+ ['query.id.email', 'someone@example.com', 'id.email'],
222
+ ['server.client-ip', '81.2.69.142', 'client-ip'],
223
+ ['query.user-agent', 'iPhone', 'user-agent']
224
+ ])
225
+ .test('get content keeps the full name - %s', (key, value, expectedName) => {
226
+ const client = new MockRequestClient();
227
+ const engine = new CloudRequestEngine({
228
+ resourceKey: testResourceKey,
229
+ requestClient: client
230
+ });
231
+
232
+ const pipeline = new PipelineBuilder()
233
+ .add(engine)
234
+ .build();
235
+
236
+ const data = pipeline.createFlowData();
237
+ data.evidence.add(key, value);
238
+
239
+ const result = engine.getContent(data);
240
+ expect(result).toStrictEqual({ [expectedName]: value });
241
+ });
242
+
243
+ /**
244
+ * Test that two dotted names which share their first part after the prefix
245
+ * are sent as two separate parameters rather than one overwriting the other.
246
+ */
247
+ test('get content keeps dotted names apart', () => {
248
+ const client = new MockRequestClient();
249
+ const engine = new CloudRequestEngine({
250
+ resourceKey: testResourceKey,
251
+ requestClient: client
252
+ });
253
+
254
+ const pipeline = new PipelineBuilder()
255
+ .add(engine)
256
+ .build();
257
+
258
+ const data = pipeline.createFlowData();
259
+ data.evidence.add('query.id.usage', 'standard');
260
+ data.evidence.add('query.id.email', 'someone@example.com');
261
+
262
+ const result = engine.getContent(data);
263
+ expect(result).toStrictEqual({
264
+ 'id.usage': 'standard',
265
+ 'id.email': 'someone@example.com'
266
+ });
267
+ });
@@ -106,3 +106,46 @@ test('post with sequence evidence', done => {
106
106
  done();
107
107
  });
108
108
  });
109
+
110
+ /**
111
+ * Verify that a server using the engine gets a 51Did on its first call.
112
+ * The engine sends the end user's User-Agent as evidence and never as its
113
+ * own HTTP User-Agent header, so the cloud service does not treat the call
114
+ * as a browser page that still has snippets to run. The usage is sent as
115
+ * query.id.usage, whose name contains a dot and must reach the service
116
+ * whole. This is an integration test that uses the live cloud service. It
117
+ * is skipped when the resource key does not include the fodid product.
118
+ */
119
+ test('51Did on the first call', async () => {
120
+ if (myResourceKey === '!!YOUR_RESOURCE_KEY!!') {
121
+ fail('You need to supply a resource key in the ' +
122
+ '_51DEGREES_RESOURCE_KEY environment variable.');
123
+ }
124
+ const cloud = new CloudRequestEngine({ resourceKey: myResourceKey });
125
+ const pipeline = new PipelineBuilder()
126
+ .add(cloud)
127
+ .build();
128
+ await cloud.ready();
129
+ // The product name keeps the case the service gives it, such as FODid.
130
+ const fodidProduct = Object.entries(cloud.flowElementProperties)
131
+ .find(([name]) => name.toLowerCase() === 'fodid');
132
+ if (!fodidProduct || !fodidProduct[1].idprobglobal) {
133
+ console.warn('The resource key does not include ' +
134
+ 'fodid.idprobglobal, so the 51Did check was skipped.');
135
+ return;
136
+ }
137
+
138
+ const data = pipeline.createFlowData();
139
+ data.evidence.add('header.user-agent', 'Mozilla/5.0 (Windows NT 10.0; ' +
140
+ 'Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' +
141
+ 'Chrome/120.0.0.0 Safari/537.36');
142
+ data.evidence.add('server.client-ip', '81.2.69.142');
143
+ data.evidence.add('query.id.usage', 'standard');
144
+ await data.process();
145
+
146
+ const fodid = JSON.parse(data.cloud.cloud).fodid;
147
+ expect(fodid).toBeDefined();
148
+ expect(fodid.idprobglobalnullreason).toBeUndefined();
149
+ expect(typeof fodid.idprobglobal).toBe('string');
150
+ expect(fodid.idprobglobal.length).toBeGreaterThan(0);
151
+ });
@@ -212,3 +212,35 @@ test('post - data', done => {
212
212
  },
213
213
  done);
214
214
  });
215
+
216
+ // The cloud service treats a request whose own User-Agent header names a
217
+ // browser as a browser page, and such a page gets no 51Did until it has run
218
+ // its snippets. The end user's User-Agent must therefore only travel as the
219
+ // user-agent parameter in the body and never as the HTTP User-Agent header.
220
+ each([
221
+ ['post', (client, url, data) => client.post(url, data)],
222
+ ['get', (client, url) => client.get(url)]
223
+ ])
224
+ .test('%s - end user User-Agent is not sent as the header', (name, send, done) => {
225
+ const browserUserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' +
226
+ 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 ' +
227
+ 'Safari/537.36';
228
+ const port = POST_PORT;
229
+ let header;
230
+ server.addListener('request', (req, res) => {
231
+ header = req.headers['user-agent'];
232
+ res.writeHead(200);
233
+ res.end('{}');
234
+ });
235
+ server.listen(port, () => {
236
+ send(
237
+ new RequestClient(),
238
+ `http://localhost:${port}`,
239
+ { 'user-agent': browserUserAgent })
240
+ .then(() => {
241
+ expect(header === undefined || !header.includes('Mozilla'))
242
+ .toBe(true);
243
+ })
244
+ .then(() => done(), done);
245
+ });
246
+ });