@contrail/flexplm 1.7.7-alpha.d153eea → 1.7.7-alpha.e66ae52

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.
@@ -4,19 +4,6 @@ exports.FlexPLMConnect = void 0;
4
4
  const app_framework_1 = require("@contrail/app-framework");
5
5
  // Module-level on purpose: a new FlexPLMConnect is constructed for every event. Declaring for Session reusing use case.
6
6
  const sessionCache = new Map();
7
- // Upper bound so users/option combinations that are never requested again cannot accumulate cookies forever.
8
- const MAX_CACHED_SESSIONS = 100;
9
- const pruneSessionCache = () => {
10
- const now = Date.now();
11
- for (const [key, session] of sessionCache) {
12
- if (session.expiresAt <= now)
13
- sessionCache.delete(key);
14
- }
15
- // Still full: drop the oldest entries (Map keeps insertion order).
16
- while (sessionCache.size >= MAX_CACHED_SESSIONS) {
17
- sessionCache.delete(sessionCache.keys().next().value);
18
- }
19
- };
20
7
  // In-flight cold session acquisitions per key, so concurrent events share one FlexPLM session instead of each creating their own.
21
8
  const inFlightAcquisitions = new Map();
22
9
  const DEFAULT_SESSION_COOKIE_NAME = 'JSESSIONID';
@@ -157,11 +144,7 @@ class FlexPLMConnect {
157
144
  }
158
145
  storeSession(cookie) {
159
146
  // sliding expiry: Windchill's session timeout is also sliding
160
- const key = this.getSessionKey();
161
- // delete first so a refreshed entry moves to the newest position (oldest-first eviction then acts as LRU)
162
- sessionCache.delete(key);
163
- pruneSessionCache();
164
- sessionCache.set(key, { cookie, expiresAt: Date.now() + this.sessionOptions.ttlMs });
147
+ sessionCache.set(this.getSessionKey(), { cookie, expiresAt: Date.now() + this.sessionOptions.ttlMs });
165
148
  }
166
149
  clearSession() {
167
150
  sessionCache.delete(this.getSessionKey());
@@ -243,7 +226,7 @@ class FlexPLMConnect {
243
226
  await wait;
244
227
  if (getNewSession) {
245
228
  const current = this.getCachedSession();
246
- if (rejectedCookie === undefined || !current || current === rejectedCookie)
229
+ if (!current || current === rejectedCookie)
247
230
  this.clearSession();
248
231
  }
249
232
  const cached = this.getCachedSession();
@@ -156,7 +156,7 @@ describe('FlexPLMConnect session reuse', () => {
156
156
  expect(callHeaders(1).Cookie).toBe('JSESSIONID=abc.tomcat1');
157
157
  expect(callHeaders(1).CSRF_NONCE).toBe('n1');
158
158
  });
159
- it('should reuse the cached session on the next event (new FlexPLMConnect instance)', async () => {
159
+ it('reuses the cached session on the next event (new FlexPLMConnect instance)', async () => {
160
160
  const u = uniqueUser();
161
161
  fetchMock()
162
162
  .mockResolvedValueOnce(resp(200, ['JSESSIONID=abc.tomcat1; Path=/'])) // event 1 GET
@@ -168,7 +168,7 @@ describe('FlexPLMConnect session reuse', () => {
168
168
  expect(callHeaders(2).Cookie).toBe('JSESSIONID=abc.tomcat1'); // GET reuses it
169
169
  expect(callHeaders(3).Cookie).toBe('JSESSIONID=abc.tomcat1'); // POST reuses it
170
170
  });
171
- it('should replace the cached session when Windchill answers the GET with a new Set-Cookie', async () => {
171
+ it('replaces the cached session when Windchill answers the GET with a new Set-Cookie', async () => {
172
172
  const u = uniqueUser();
173
173
  fetchMock()
174
174
  .mockResolvedValueOnce(resp(200, ['JSESSIONID=old.tomcat1; Path=/']))
@@ -185,7 +185,7 @@ describe('FlexPLMConnect session reuse', () => {
185
185
  expect(callHeaders(3).Cookie).toBe('JSESSIONID=new.tomcat1'); // POST uses the replacement
186
186
  expect(callHeaders(4).Cookie).toBe('JSESSIONID=new.tomcat1'); // event 3 GET sends the replacement
187
187
  });
188
- it('should retry once with a new session when the POST is rejected with 403', async () => {
188
+ it('retries once with a new session when the POST is rejected with 403', async () => {
189
189
  const u = uniqueUser();
190
190
  fetchMock()
191
191
  .mockResolvedValueOnce(resp(200, ['JSESSIONID=old.tomcat1; Path=/'])) // GET
@@ -199,7 +199,7 @@ describe('FlexPLMConnect session reuse', () => {
199
199
  expect(result.status).toBe(200);
200
200
  expect(result.data).toEqual({ ok: true });
201
201
  });
202
- it('should also retry on 401', async () => {
202
+ it('also retries on 401', async () => {
203
203
  const u = uniqueUser();
204
204
  fetchMock()
205
205
  .mockResolvedValueOnce(resp(200, ['JSESSIONID=old.tomcat1']))
@@ -209,7 +209,7 @@ describe('FlexPLMConnect session reuse', () => {
209
209
  await new flexplm_connect_1.FlexPLMConnect(sessionConfig(u)).sendToFlexPLM(payload);
210
210
  expect(fetchMock()).toHaveBeenCalledTimes(4);
211
211
  });
212
- it('should not loop: a second 403 after the retry is surfaced as an error', async () => {
212
+ it('does not loop: a second 403 after the retry is surfaced as an error', async () => {
213
213
  const u = uniqueUser();
214
214
  fetchMock()
215
215
  .mockResolvedValueOnce(resp(200, ['JSESSIONID=old.tomcat1']))
@@ -220,7 +220,7 @@ describe('FlexPLMConnect session reuse', () => {
220
220
  .rejects.toThrow('Error sending data to FlexPLM:status: 403');
221
221
  expect(fetchMock()).toHaveBeenCalledTimes(4);
222
222
  });
223
- it('should not retry on non-auth errors such as 500 (event may already be processed)', async () => {
223
+ it('does not retry on non-auth errors such as 500 (event may already be processed)', async () => {
224
224
  const u = uniqueUser();
225
225
  fetchMock()
226
226
  .mockResolvedValueOnce(resp(200, ['JSESSIONID=abc.tomcat1']))
@@ -229,7 +229,7 @@ describe('FlexPLMConnect session reuse', () => {
229
229
  .rejects.toThrow('Error sending data to FlexPLM:status: 500');
230
230
  expect(fetchMock()).toHaveBeenCalledTimes(2);
231
231
  });
232
- it('should retry the csrf GET without a cookie when the cached session is rejected there', async () => {
232
+ it('retries the csrf GET without a cookie when the cached session is rejected there', async () => {
233
233
  const u = uniqueUser();
234
234
  fetchMock()
235
235
  .mockResolvedValueOnce(resp(200, ['JSESSIONID=old.tomcat1'])) // event 1 GET
@@ -243,7 +243,7 @@ describe('FlexPLMConnect session reuse', () => {
243
243
  expect(callHeaders(3).Cookie).toBeUndefined();
244
244
  expect(callHeaders(4).Cookie).toBe('JSESSIONID=new.tomcat1');
245
245
  });
246
- it('should expire the cached session after ttlSeconds', async () => {
246
+ it('expires the cached session after ttlSeconds', async () => {
247
247
  const u = uniqueUser();
248
248
  const realNow = Date.now();
249
249
  const nowSpy = jest.spyOn(Date, 'now').mockReturnValue(realNow);
@@ -260,7 +260,7 @@ describe('FlexPLMConnect session reuse', () => {
260
260
  expect(callHeaders(3).Cookie).toBe('JSESSIONID=def.tomcat1');
261
261
  nowSpy.mockRestore();
262
262
  });
263
- it('should use the last JSESSIONID when several Set-Cookie lines are returned', async () => {
263
+ it('uses the last JSESSIONID when several Set-Cookie lines are returned', async () => {
264
264
  const u = uniqueUser();
265
265
  fetchMock()
266
266
  .mockResolvedValueOnce(resp(200, ['JSESSIONID=first.tomcat1; Path=/', 'OTHER=x; Path=/', 'JSESSIONID=second.tomcat1; Path=/']))
@@ -268,7 +268,7 @@ describe('FlexPLMConnect session reuse', () => {
268
268
  await new flexplm_connect_1.FlexPLMConnect(sessionConfig(u)).sendToFlexPLM(payload);
269
269
  expect(callHeaders(1).Cookie).toBe('JSESSIONID=second.tomcat1');
270
270
  });
271
- it('should fall back to headers.get("set-cookie") when getSetCookie is not available', async () => {
271
+ it('falls back to headers.get("set-cookie") when getSetCookie is not available', async () => {
272
272
  const u = uniqueUser();
273
273
  const legacy = {
274
274
  status: 200,
@@ -280,7 +280,7 @@ describe('FlexPLMConnect session reuse', () => {
280
280
  await new flexplm_connect_1.FlexPLMConnect(sessionConfig(u)).sendToFlexPLM(payload);
281
281
  expect(callHeaders(1).Cookie).toBe('JSESSIONID=legacy.tomcat1');
282
282
  });
283
- it('should honour a custom cookieName', async () => {
283
+ it('honours a custom cookieName', async () => {
284
284
  const u = uniqueUser();
285
285
  fetchMock()
286
286
  .mockResolvedValueOnce(resp(200, ['MYSESSION=zzz; Path=/', 'JSESSIONID=ignored; Path=/']))
@@ -288,7 +288,7 @@ describe('FlexPLMConnect session reuse', () => {
288
288
  await new flexplm_connect_1.FlexPLMConnect(sessionConfig(u, { enabled: true, cookieName: 'MYSESSION' })).sendToFlexPLM(payload);
289
289
  expect(callHeaders(1).Cookie).toBe('MYSESSION=zzz');
290
290
  });
291
- it('should accept enabled as the string "true"', async () => {
291
+ it('accepts enabled as the string "true"', async () => {
292
292
  const u = uniqueUser();
293
293
  fetchMock()
294
294
  .mockResolvedValueOnce(resp(200, ['JSESSIONID=abc.tomcat1']))
@@ -296,39 +296,7 @@ describe('FlexPLMConnect session reuse', () => {
296
296
  await new flexplm_connect_1.FlexPLMConnect(sessionConfig(u, { enabled: 'true' })).sendToFlexPLM(payload);
297
297
  expect(callHeaders(1).Cookie).toBe('JSESSIONID=abc.tomcat1');
298
298
  });
299
- it('should evict expired sessions of other users when a new session is stored', async () => {
300
- const userA = uniqueUser();
301
- const userB = uniqueUser();
302
- const realNow = Date.now();
303
- const nowSpy = jest.spyOn(Date, 'now').mockReturnValue(realNow);
304
- fetchMock()
305
- .mockResolvedValueOnce(resp(200, ['JSESSIONID=a.tomcat1'])) // A GET
306
- .mockResolvedValueOnce(resp(200, [], {})) // A POST
307
- .mockResolvedValueOnce(resp(200, ['JSESSIONID=b.tomcat1'])) // B GET
308
- .mockResolvedValueOnce(resp(200, [], {})) // B POST
309
- .mockResolvedValueOnce(resp(200, ['JSESSIONID=a2.tomcat1'])) // A GET again
310
- .mockResolvedValueOnce(resp(200, [], {}));
311
- const cfg = (u) => sessionConfig(u, { enabled: true, ttlSeconds: 1 });
312
- await new flexplm_connect_1.FlexPLMConnect(cfg(userA)).sendToFlexPLM(payload);
313
- nowSpy.mockReturnValue(realNow + 2000); // A has expired
314
- await new flexplm_connect_1.FlexPLMConnect(cfg(userB)).sendToFlexPLM(payload); // storing B sweeps A
315
- nowSpy.mockReturnValue(realNow); // rewind: only a swept A has no entry left
316
- await new flexplm_connect_1.FlexPLMConnect(cfg(userA)).sendToFlexPLM(payload);
317
- expect(callHeaders(4).Cookie).toBeUndefined();
318
- nowSpy.mockRestore();
319
- });
320
- it('should not hold more than the maximum number of cached sessions', async () => {
321
- const first = uniqueUser();
322
- fetchMock().mockImplementation(async () => resp(200, ['JSESSIONID=s.tomcat1']));
323
- await new flexplm_connect_1.FlexPLMConnect(sessionConfig(first)).sendToFlexPLM(payload);
324
- for (let i = 0; i < 100; i++) {
325
- await new flexplm_connect_1.FlexPLMConnect(sessionConfig(uniqueUser())).sendToFlexPLM(payload);
326
- }
327
- fetchMock().mockClear();
328
- await new flexplm_connect_1.FlexPLMConnect(sessionConfig(first)).sendToFlexPLM(payload);
329
- expect(callHeaders(0).Cookie).toBeUndefined(); // oldest entry was evicted
330
- });
331
- it('should keep separate sessions per user', async () => {
299
+ it('keeps separate sessions per user', async () => {
332
300
  const a = uniqueUser();
333
301
  const b = uniqueUser();
334
302
  fetchMock()
@@ -341,7 +309,7 @@ describe('FlexPLMConnect session reuse', () => {
341
309
  expect(callHeaders(2).Cookie).toBeUndefined(); // user b must not see user a's cookie
342
310
  expect(callHeaders(3).Cookie).toBe('JSESSIONID=bbb.tomcat1');
343
311
  });
344
- it('should never log the cookie value', async () => {
312
+ it('never logs the cookie value', async () => {
345
313
  const isInfoOn = jest.spyOn(app_framework_1.Logger, 'isInfoOn').mockReturnValue(true);
346
314
  const info = jest.spyOn(console, 'info').mockImplementation();
347
315
  const u = uniqueUser();
@@ -372,11 +340,11 @@ describe('FlexPLMConnect session reuse', () => {
372
340
  ['a comma separated string', '401, 403'],
373
341
  ['garbage (falls back to 401/403)', 'abc'],
374
342
  ['an empty array (falls back to 401/403)', []],
375
- ])('should not throw and still retry on 403 when retryStatuses is %s', async (_label, retryStatuses) => {
343
+ ])('retryStatuses given as %s does not throw and still retries on 403', async (_label, retryStatuses) => {
376
344
  await rejectedOnceThenOk(uniqueUser(), { retryStatuses });
377
345
  expect(fetchMock()).toHaveBeenCalledTimes(4);
378
346
  });
379
- it.each([['abc'], [0], [-5]])('should fall back to the 900s default when ttlSeconds is invalid (%p)', async (ttlSeconds) => {
347
+ it.each([['abc'], [0], [-5]])('invalid ttlSeconds (%p) falls back to the 900s default', async (ttlSeconds) => {
380
348
  const u = uniqueUser();
381
349
  const realNow = Date.now();
382
350
  const nowSpy = jest.spyOn(Date, 'now').mockReturnValue(realNow);
@@ -392,7 +360,7 @@ describe('FlexPLMConnect session reuse', () => {
392
360
  expect(callHeaders(2).Cookie).toBe('JSESSIONID=abc.tomcat1');
393
361
  nowSpy.mockRestore();
394
362
  });
395
- it('should fall back to JSESSIONID when cookieName is blank', async () => {
363
+ it('a blank cookieName falls back to JSESSIONID', async () => {
396
364
  const u = uniqueUser();
397
365
  fetchMock()
398
366
  .mockResolvedValueOnce(resp(200, ['JSESSIONID=abc.tomcat1']))
@@ -505,7 +473,7 @@ describe('FlexPLMConnect session reuse', () => {
505
473
  });
506
474
  });
507
475
  describe('cookie names containing regex metacharacters', () => {
508
- it('should extract a cookie whose name contains "+"', async () => {
476
+ it('extracts a cookie whose name contains "+"', async () => {
509
477
  const u = uniqueUser();
510
478
  fetchMock()
511
479
  .mockResolvedValueOnce(resp(200, ['SID+=abc; Path=/']))
@@ -513,7 +481,7 @@ describe('FlexPLMConnect session reuse', () => {
513
481
  await new flexplm_connect_1.FlexPLMConnect(sessionConfig(u, { enabled: true, cookieName: 'SID+' })).sendToFlexPLM(payload);
514
482
  expect(callHeaders(1).Cookie).toBe('SID+=abc');
515
483
  });
516
- it('should treat "." literally and not match a look-alike cookie', async () => {
484
+ it('treats "." literally and does not match a look-alike cookie', async () => {
517
485
  const u = uniqueUser();
518
486
  fetchMock()
519
487
  .mockResolvedValueOnce(resp(200, ['AxB=wrong; Path=/']))
@@ -525,7 +493,7 @@ describe('FlexPLMConnect session reuse', () => {
525
493
  describe('concurrent session acquisition', () => {
526
494
  const flush = () => new Promise(r => setImmediate(r));
527
495
  const isColdGet = (call) => call[1].method === 'GET' && !call[1].headers.Cookie;
528
- it('should share one session across concurrent events on a cold cache', async () => {
496
+ it('shares one session across concurrent events on a cold cache', async () => {
529
497
  const u = uniqueUser();
530
498
  let release;
531
499
  const gate = new Promise(r => { release = r; });
@@ -544,7 +512,7 @@ describe('FlexPLMConnect session reuse', () => {
544
512
  expect(fetchMock().mock.calls.filter(isColdGet)).toHaveLength(1);
545
513
  results.forEach(r => expect(r.sessionCookie).toBe('abc.tomcat1'));
546
514
  });
547
- it('should not make a waiter inherit the failure of the first acquisition', async () => {
515
+ it('a waiter does not inherit the failure of the first acquisition', async () => {
548
516
  const u = uniqueUser();
549
517
  let release;
550
518
  const gate = new Promise(r => { release = r; });
@@ -559,7 +527,7 @@ describe('FlexPLMConnect session reuse', () => {
559
527
  expect(await firstResult).toBeInstanceOf(Error);
560
528
  expect((await second).sessionCookie).toBe('abc.tomcat1');
561
529
  });
562
- it('should create only one replacement session for parallel events rejected with 403', async () => {
530
+ it('parallel events rejected with 403 create only one replacement session', async () => {
563
531
  const u = uniqueUser();
564
532
  fetchMock().mockResolvedValueOnce(resp(200, ['JSESSIONID=old.tomcat1']));
565
533
  await new flexplm_connect_1.FlexPLMConnect(sessionConfig(u)).getCSRF(); // prime the cache with the old session
@@ -579,7 +547,7 @@ describe('FlexPLMConnect session reuse', () => {
579
547
  });
580
548
  describe('FlexPLMConnect session reuse - feature disabled (default)', () => {
581
549
  const payload = { id: '123' };
582
- it('should send no Cookie header and make exactly one GET + one POST', async () => {
550
+ it('sends no Cookie header and makes exactly one GET + one POST', async () => {
583
551
  const u = uniqueUser();
584
552
  fetchMock()
585
553
  .mockResolvedValueOnce(resp(200, ['JSESSIONID=abc.tomcat1'])) // Set-Cookie present but must be ignored
@@ -593,7 +561,7 @@ describe('FlexPLMConnect session reuse - feature disabled (default)', () => {
593
561
  expect(callHeaders(i).Cookie).toBeUndefined();
594
562
  }
595
563
  });
596
- it('should not retry on 403 (behaves exactly as before)', async () => {
564
+ it('does not retry on 403 (behaves exactly as before)', async () => {
597
565
  const u = uniqueUser();
598
566
  fetchMock()
599
567
  .mockResolvedValueOnce(resp(200))
@@ -602,7 +570,7 @@ describe('FlexPLMConnect session reuse - feature disabled (default)', () => {
602
570
  .rejects.toThrow('Error sending data to FlexPLM:status: 403');
603
571
  expect(fetchMock()).toHaveBeenCalledTimes(2);
604
572
  });
605
- it('should behave the same as not configured when enabled is false', async () => {
573
+ it('explicit enabled:false behaves the same as not configured', async () => {
606
574
  const u = uniqueUser();
607
575
  fetchMock()
608
576
  .mockResolvedValueOnce(resp(200, ['JSESSIONID=abc.tomcat1']))
@@ -612,7 +580,7 @@ describe('FlexPLMConnect session reuse - feature disabled (default)', () => {
612
580
  });
613
581
  });
614
582
  describe('FlexPLMConnect.getRequest session reuse', () => {
615
- it('should send the cached cookie on image/GET requests', async () => {
583
+ it('sends the cached cookie on image/GET requests', async () => {
616
584
  const u = uniqueUser();
617
585
  fetchMock()
618
586
  .mockResolvedValueOnce(resp(200, ['JSESSIONID=abc.tomcat1'])) // csrf GET primes the cache
@@ -623,7 +591,7 @@ describe('FlexPLMConnect.getRequest session reuse', () => {
623
591
  await connect.getRequest();
624
592
  expect(callHeaders(2).Cookie).toBe('JSESSIONID=abc.tomcat1');
625
593
  });
626
- it('should retry once without the cookie when the cached session is rejected on getRequest', async () => {
594
+ it('retries once without the cookie when the cached session is rejected on getRequest', async () => {
627
595
  const u = uniqueUser();
628
596
  fetchMock()
629
597
  .mockResolvedValueOnce(resp(200, ['JSESSIONID=abc.tomcat1']))
@@ -636,7 +604,7 @@ describe('FlexPLMConnect.getRequest session reuse', () => {
636
604
  expect(callHeaders(2).Cookie).toBe('JSESSIONID=abc.tomcat1');
637
605
  expect(callHeaders(3).Cookie).toBeUndefined();
638
606
  });
639
- it('should not re-store the rejected cookie when the retry issues no Set-Cookie', async () => {
607
+ it('does not re-store the rejected cookie when the retry issues no Set-Cookie', async () => {
640
608
  const u = uniqueUser();
641
609
  fetchMock()
642
610
  .mockResolvedValueOnce(resp(200, ['JSESSIONID=stale.tomcat1']))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrail/flexplm",
3
- "version": "1.7.7-alpha.d153eea",
3
+ "version": "1.7.7-alpha.e66ae52",
4
4
  "description": "Library used for integration with flexplm.",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",