@data-fair/lib-node 2.8.1 → 2.9.0

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.
package/locks.js CHANGED
@@ -1,75 +1,77 @@
1
- import { hostname } from 'node:os'
2
- import { randomBytes } from 'node:crypto'
3
- import Debug from 'debug'
4
- const debug = Debug('locks')
1
+ import { hostname } from 'node:os';
2
+ import { randomBytes } from 'node:crypto';
3
+ import Debug from 'debug';
4
+ const debug = Debug('locks');
5
5
  export class Locks {
6
- pid
7
- interval
8
- _db
9
- get db () {
10
- if (!this._db) { throw new Error('locks utils was not initialized') }
11
- return this._db
12
- }
13
-
14
- get collection () {
15
- return this.db.collection('locks')
16
- }
17
-
18
- constructor () {
19
- this.pid = randomBytes(8).toString('hex')
20
- debug('locks with pid', this.pid)
21
- }
22
-
23
- start = async (db, ttl = 60) => {
24
- if (this._db) { throw new Error('locks utils was already initialized') }
25
- this._db = db
26
- await this.collection.createIndex({ pid: 1 })
27
- try {
28
- await this.collection.createIndex({ updatedAt: 1 }, { expireAfterSeconds: ttl })
29
- } catch (err) {
30
- console.log('Failure to create TTL index. Probably because the value changed. Try to update it.')
31
- db.command({ collMod: 'locks', index: { keyPattern: { updatedAt: 1 }, expireAfterSeconds: ttl } })
6
+ pid;
7
+ interval;
8
+ _db;
9
+ get db() {
10
+ if (!this._db)
11
+ throw new Error('locks utils was not initialized');
12
+ return this._db;
32
13
  }
33
- // prolongate lock acquired by this process while it is still active
34
- this.interval = setInterval(() => {
35
- this.collection.updateMany({ pid: this.pid }, { $currentDate: { updatedAt: true } })
36
- }, (ttl / 2) * 1000)
37
- }
38
-
39
- stop = async () => {
40
- clearInterval(this.interval)
41
- if (this._db) { await this.collection.deleteMany({ pid: this.pid }) }
42
- }
43
-
44
- acquire = async (_id, origin) => {
45
- debug('acquire', _id, origin)
46
- try {
47
- await this.collection.insertOne({ _id, pid: this.pid, origin, hostname: hostname(), createdAt: new Date() })
48
- try {
49
- await this.collection.updateOne({ _id }, { $currentDate: { updatedAt: true } })
50
- } catch (err) {
51
- await this.collection.deleteOne({ _id, pid: this.pid })
52
- throw err
53
- }
54
- debug('acquire ok', _id)
55
- return true
56
- } catch (err) {
57
- if (err.code !== 11000) { throw err }
58
- // duplicate means the lock was already acquired
59
- debug('acquire ko', _id)
60
- return false
14
+ get collection() {
15
+ return this.db.collection('locks');
61
16
  }
62
- }
63
-
64
- release = async (_id, delay = 0) => {
65
- debug('release', _id)
66
- if (delay) {
67
- const date = new Date((new Date()).getTime() + delay)
68
- await this.collection.updateOne({ _id, pid: this.pid }, { $unset: { pid: 1 }, $set: { delayed: true, updatedAt: date } })
69
- } else {
70
- await this.collection.deleteOne({ _id, pid: this.pid })
17
+ constructor() {
18
+ this.pid = randomBytes(8).toString('hex');
19
+ debug('locks with pid', this.pid);
71
20
  }
72
- }
21
+ start = async (db, ttl = 60) => {
22
+ if (this._db)
23
+ throw new Error('locks utils was already initialized');
24
+ this._db = db;
25
+ await this.collection.createIndex({ pid: 1 });
26
+ try {
27
+ await this.collection.createIndex({ updatedAt: 1 }, { expireAfterSeconds: ttl });
28
+ }
29
+ catch (err) {
30
+ console.log('Failure to create TTL index. Probably because the value changed. Try to update it.');
31
+ db.command({ collMod: 'locks', index: { keyPattern: { updatedAt: 1 }, expireAfterSeconds: ttl } });
32
+ }
33
+ // prolongate lock acquired by this process while it is still active
34
+ this.interval = setInterval(() => {
35
+ this.collection.updateMany({ pid: this.pid }, { $currentDate: { updatedAt: true } });
36
+ }, (ttl / 2) * 1000);
37
+ };
38
+ stop = async () => {
39
+ clearInterval(this.interval);
40
+ if (this._db)
41
+ await this.collection.deleteMany({ pid: this.pid });
42
+ };
43
+ acquire = async (_id, origin) => {
44
+ debug('acquire', _id, origin);
45
+ try {
46
+ await this.collection.insertOne({ _id, pid: this.pid, origin, hostname: hostname(), createdAt: new Date() });
47
+ try {
48
+ await this.collection.updateOne({ _id }, { $currentDate: { updatedAt: true } });
49
+ }
50
+ catch (err) {
51
+ await this.collection.deleteOne({ _id, pid: this.pid });
52
+ throw err;
53
+ }
54
+ debug('acquire ok', _id);
55
+ return true;
56
+ }
57
+ catch (err) {
58
+ if (err.code !== 11000)
59
+ throw err;
60
+ // duplicate means the lock was already acquired
61
+ debug('acquire ko', _id);
62
+ return false;
63
+ }
64
+ };
65
+ release = async (_id, delay = 0) => {
66
+ debug('release', _id);
67
+ if (delay) {
68
+ const date = new Date((new Date()).getTime() + delay);
69
+ await this.collection.updateOne({ _id, pid: this.pid }, { $unset: { pid: 1 }, $set: { delayed: true, updatedAt: date } });
70
+ }
71
+ else {
72
+ await this.collection.deleteOne({ _id, pid: this.pid });
73
+ }
74
+ };
73
75
  }
74
- const locks = new Locks()
75
- export default locks
76
+ const locks = new Locks();
77
+ export default locks;
package/mongo.js CHANGED
@@ -1,76 +1,80 @@
1
- import { MongoClient, MongoError } from 'mongodb'
1
+ import { MongoClient, MongoError } from 'mongodb';
2
2
  export class Mongo {
3
- _client
4
- _db
5
- get client () {
6
- if (!this._client) { throw new Error('db was not connected') }
7
- return this._client
8
- }
9
-
10
- get db () {
11
- if (!this._db) { throw new Error('db was not connected') }
12
- return this._db
13
- }
14
-
15
- connect = async (mongoUrl, options = {}) => {
16
- if (this._client) {
17
- console.warn('db already connected')
18
- return
3
+ _client;
4
+ _db;
5
+ get client() {
6
+ if (!this._client)
7
+ throw new Error('db was not connected');
8
+ return this._client;
19
9
  }
20
- console.log('connecting to mongodb...')
21
- this._client = await MongoClient.connect(mongoUrl, { maxPoolSize: 5, ignoreUndefined: true, ...options })
22
- this._db = this._client.db()
23
- console.log('...ok')
24
- }
25
-
26
- // Create an index if it does not exist and manage overwriting existing and conflicting index definitions
27
- ensureIndex = async (collection, key, options = {}) => {
28
- try {
29
- await this.db.collection(collection).createIndex(key, options)
30
- } catch (err) {
31
- if (options.name && err instanceof MongoError && (err.code === 85 || err.code === 86)) {
32
- // if the error is a conflict on keys or params of the index we automatically
33
- // delete then recreate the index
34
- console.log(`drop then recreate index ${collection}/${options.name}`)
35
- await this.db.collection(collection).dropIndex(options.name)
36
- await this.db.collection(collection).createIndex(key, options)
37
- } else {
38
- throw err
39
- }
10
+ get db() {
11
+ if (!this._db)
12
+ throw new Error('db was not connected');
13
+ return this._db;
40
14
  }
41
- }
42
-
43
- /**
15
+ connect = async (mongoUrl, options = {}) => {
16
+ if (this._client) {
17
+ console.warn('db already connected');
18
+ return;
19
+ }
20
+ console.log('connecting to mongodb...');
21
+ this._client = await MongoClient.connect(mongoUrl, { maxPoolSize: 5, ignoreUndefined: true, ...options });
22
+ this._db = this._client.db();
23
+ console.log('...ok');
24
+ };
25
+ // Create an index if it does not exist and manage overwriting existing and conflicting index definitions
26
+ ensureIndex = async (collection, key, options = {}) => {
27
+ try {
28
+ await this.db.collection(collection).createIndex(key, options);
29
+ }
30
+ catch (err) {
31
+ if (options.name && err instanceof MongoError && (err.code === 85 || err.code === 86)) {
32
+ // if the error is a conflict on keys or params of the index we automatically
33
+ // delete then recreate the index
34
+ console.log(`drop then recreate index ${collection}/${options.name}`);
35
+ await this.db.collection(collection).dropIndex(options.name);
36
+ await this.db.collection(collection).createIndex(key, options);
37
+ }
38
+ else {
39
+ throw err;
40
+ }
41
+ }
42
+ };
43
+ /**
44
44
  * create a bunch of indexes and overtwrite previous definitions
45
45
  * the structure forces giving names to indexes which is better for managing them properly
46
46
  */
47
- configure = async (indexDefinitions) => {
48
- for (const collectionName in indexDefinitions) {
49
- try {
50
- await this.db.createCollection(collectionName)
51
- } catch (err) {
52
- // nothing TODO
53
- }
54
- for (const indexName in indexDefinitions[collectionName]) {
55
- let key, options
56
- const indexDefinition = indexDefinitions[collectionName][indexName]
57
- if (indexDefinition === null) {
58
- if (await this.db.collection(collectionName).indexExists(indexName)) {
59
- await this.db.collection(collectionName).dropIndex(indexName)
60
- }
61
- continue
62
- } else if (Array.isArray(indexDefinition)) {
63
- [key, options] = indexDefinition
64
- if (options.name && options.name !== indexName) { throw new Error(`inconsistent index name ${indexName} or ${options.name}`) }
65
- options.name = indexName
66
- } else {
67
- key = indexDefinition
68
- options = { name: indexName }
47
+ configure = async (indexDefinitions) => {
48
+ for (const collectionName in indexDefinitions) {
49
+ try {
50
+ await this.db.createCollection(collectionName);
51
+ }
52
+ catch (err) {
53
+ // nothing TODO
54
+ }
55
+ for (const indexName in indexDefinitions[collectionName]) {
56
+ let key, options;
57
+ const indexDefinition = indexDefinitions[collectionName][indexName];
58
+ if (indexDefinition === null) {
59
+ if (await this.db.collection(collectionName).indexExists(indexName)) {
60
+ await this.db.collection(collectionName).dropIndex(indexName);
61
+ }
62
+ continue;
63
+ }
64
+ else if (Array.isArray(indexDefinition)) {
65
+ [key, options] = indexDefinition;
66
+ if (options.name && options.name !== indexName)
67
+ throw new Error(`inconsistent index name ${indexName} or ${options.name}`);
68
+ options.name = indexName;
69
+ }
70
+ else {
71
+ key = indexDefinition;
72
+ options = { name: indexName };
73
+ }
74
+ await this.ensureIndex(collectionName, key, options);
75
+ }
69
76
  }
70
- await this.ensureIndex(collectionName, key, options)
71
- }
72
- }
73
- }
77
+ };
74
78
  }
75
- const mongo = new Mongo()
76
- export default mongo
79
+ const mongo = new Mongo();
80
+ export default mongo;
package/observer.js CHANGED
@@ -1,102 +1,109 @@
1
1
  // very light web server to expose prometheus metrics, export a cpu profile and maybe other incoming obervability features
2
- import { hostname } from 'node:os'
3
- import { createServer } from 'node:http'
4
- import { Registry, Counter, register, collectDefaultMetrics } from 'prom-client'
5
- import eventPromise from '@data-fair/lib-utils/event-promise.js'
2
+ import { hostname } from 'node:os';
3
+ import { createServer } from 'node:http';
4
+ import { Registry, Counter, register, collectDefaultMetrics } from 'prom-client';
5
+ import eventPromise from '@data-fair/lib-utils/event-promise.js';
6
6
  // a special registry for metrics that are global
7
7
  // not based on this specific process state but instead on the content of db for example
8
- export const servicePromRegistry = new Registry()
9
- collectDefaultMetrics()
8
+ export const servicePromRegistry = new Registry();
9
+ collectDefaultMetrics();
10
10
  // local metrics incremented throughout the code
11
11
  const internalErrorCounter = new Counter({
12
- name: 'df_internal_error',
13
- help: 'A counter of errors from any service, worker, etc. Do not use for client errors, only for anomalies that should trigger alerts. Each increment should be accompanied by an error log with matching code.',
14
- labelNames: ['errorCode']
15
- })
12
+ name: 'df_internal_error',
13
+ help: 'A counter of errors from any service, worker, etc. Do not use for client errors, only for anomalies that should trigger alerts. Each increment should be accompanied by an error log with matching code.',
14
+ labelNames: ['errorCode']
15
+ });
16
16
  export const internalError = (errorCode, error, ...optionalParams) => {
17
- internalErrorCounter.inc({ errorCode })
18
- const message = error.message || error
19
- console.error(`[${errorCode}] ${message}`, ...optionalParams)
20
- if (error.stack) { console.error(error.stack) }
21
- }
17
+ internalErrorCounter.inc({ errorCode });
18
+ const message = error.message || error;
19
+ console.error(`[${errorCode}] ${message}`, ...optionalParams);
20
+ if (error.stack)
21
+ console.error(error.stack);
22
+ };
22
23
  /**
23
24
  * @param {import('node:http').ServerResponse} res
24
25
  * @param {any} err
25
26
  */
26
27
  const httpErrorHandler = (res, err) => {
27
- console.error('failed to server prometheus /metrics', err)
28
- res.writeHead(500)
29
- res.end()
30
- }
28
+ console.error('failed to server prometheus /metrics', err);
29
+ res.writeHead(500);
30
+ res.end();
31
+ };
31
32
  const serveRegistry = async (res, registry) => {
32
- const metrics = await registry.metrics()
33
- res.setHeader('Content-Type', registry.contentType)
34
- res.writeHead(200)
35
- res.write(metrics)
36
- res.end()
37
- }
33
+ const metrics = await registry.metrics();
34
+ res.setHeader('Content-Type', registry.contentType);
35
+ res.writeHead(200);
36
+ res.write(metrics);
37
+ res.end();
38
+ };
38
39
  // live CPU performance inspection
39
40
  const serveCPUProfile = async (res, duration) => {
40
- const { Session } = await import('node:inspector/promises')
41
- const session = new Session()
42
- session.connect()
43
- await session.post('Profiler.enable')
44
- await session.post('Profiler.start')
45
- await new Promise(resolve => setTimeout(resolve, duration))
46
- const { profile } = await session.post('Profiler.stop')
47
- await session.post('Profiler.disable')
48
- session.disconnect()
49
- res.setHeader('Content-Disposition', `attachment; filename="data-fair-${hostname()}-${new Date().toISOString()}.cpuprofile"`)
50
- res.write(JSON.stringify(profile))
51
- res.end()
52
- }
41
+ const { Session } = await import('node:inspector/promises');
42
+ const session = new Session();
43
+ session.connect();
44
+ await session.post('Profiler.enable');
45
+ await session.post('Profiler.start');
46
+ await new Promise(resolve => setTimeout(resolve, duration));
47
+ const { profile } = await session.post('Profiler.stop');
48
+ await session.post('Profiler.disable');
49
+ session.disconnect();
50
+ res.setHeader('Content-Disposition', `attachment; filename="data-fair-${hostname()}-${new Date().toISOString()}.cpuprofile"`);
51
+ res.write(JSON.stringify(profile));
52
+ res.end();
53
+ };
53
54
  // live heap allocation sampler
54
55
  const serveHeapSnapshot = async (res, includeCollected, duration) => {
55
- const { Session } = await import('node:inspector/promises')
56
- const session = new Session()
57
- session.connect()
58
- await session.post('HeapProfiler.enable')
59
- await session.post('HeapProfiler.startSampling', { includeObjectsCollectedByMajorGC: includeCollected, includeObjectsCollectedByMinorGC: true })
60
- await new Promise(resolve => setTimeout(resolve, duration))
61
- const { profile } = await session.post('HeapProfiler.stopSampling')
62
- await session.post('HeapProfiler.disable')
63
- session.disconnect()
64
- res.setHeader('Content-Disposition', `attachment; filename="data-fair-${hostname()}-${new Date().toISOString()}.heapprofile"`)
65
- res.write(JSON.stringify(profile))
66
- res.end()
67
- }
68
- let server
56
+ const { Session } = await import('node:inspector/promises');
57
+ const session = new Session();
58
+ session.connect();
59
+ await session.post('HeapProfiler.enable');
60
+ await session.post('HeapProfiler.startSampling', { includeObjectsCollectedByMajorGC: includeCollected, includeObjectsCollectedByMinorGC: true });
61
+ await new Promise(resolve => setTimeout(resolve, duration));
62
+ const { profile } = await session.post('HeapProfiler.stopSampling');
63
+ await session.post('HeapProfiler.disable');
64
+ session.disconnect();
65
+ res.setHeader('Content-Disposition', `attachment; filename="data-fair-${hostname()}-${new Date().toISOString()}.heapprofile"`);
66
+ res.write(JSON.stringify(profile));
67
+ res.end();
68
+ };
69
+ let server;
69
70
  export const startObserver = async (port = 9090) => {
70
- server = createServer((req, res) => {
71
- const url = new URL(req.url || '', `http://${req.headers.host}`)
72
- if (req.method === 'GET' && url.pathname === '/metrics') {
73
- serveRegistry(res, register).catch(err => httpErrorHandler(res, err))
74
- } else if (req.method === 'GET' && (url.pathname === '/global-metrics' || url.pathname === '/service-metrics')) {
75
- serveRegistry(res, servicePromRegistry).catch(err => httpErrorHandler(res, err))
76
- } else if (req.method === 'GET' && url.pathname === '/cpu-profile') {
77
- const duration = parseInt(url.searchParams.get('duration') || '2000')
78
- serveCPUProfile(res, duration).catch(err => httpErrorHandler(res, err))
79
- } else if (req.method === 'GET' && url.pathname === '/heap-profile') {
80
- const duration = parseInt(url.searchParams.get('duration') || '2000')
81
- serveHeapSnapshot(res, false, duration).catch(err => httpErrorHandler(res, err))
82
- } else if (req.method === 'GET' && url.pathname === '/heap-profile-all') {
83
- const duration = parseInt(url.searchParams.get('duration') || '2000')
84
- serveHeapSnapshot(res, true, duration).catch(err => httpErrorHandler(res, err))
85
- } else {
86
- res.writeHead(404)
87
- res.end()
88
- }
89
- })
90
- server.listen(port)
91
- await eventPromise(server, 'listening')
92
- console.log(`Observer server available on http://localhost:${port}
71
+ server = createServer((req, res) => {
72
+ const url = new URL(req.url || '', `http://${req.headers.host}`);
73
+ if (req.method === 'GET' && url.pathname === '/metrics') {
74
+ serveRegistry(res, register).catch(err => httpErrorHandler(res, err));
75
+ }
76
+ else if (req.method === 'GET' && (url.pathname === '/global-metrics' || url.pathname === '/service-metrics')) {
77
+ serveRegistry(res, servicePromRegistry).catch(err => httpErrorHandler(res, err));
78
+ }
79
+ else if (req.method === 'GET' && url.pathname === '/cpu-profile') {
80
+ const duration = parseInt(url.searchParams.get('duration') || '2000');
81
+ serveCPUProfile(res, duration).catch(err => httpErrorHandler(res, err));
82
+ }
83
+ else if (req.method === 'GET' && url.pathname === '/heap-profile') {
84
+ const duration = parseInt(url.searchParams.get('duration') || '2000');
85
+ serveHeapSnapshot(res, false, duration).catch(err => httpErrorHandler(res, err));
86
+ }
87
+ else if (req.method === 'GET' && url.pathname === '/heap-profile-all') {
88
+ const duration = parseInt(url.searchParams.get('duration') || '2000');
89
+ serveHeapSnapshot(res, true, duration).catch(err => httpErrorHandler(res, err));
90
+ }
91
+ else {
92
+ res.writeHead(404);
93
+ res.end();
94
+ }
95
+ });
96
+ server.listen(port);
97
+ await eventPromise(server, 'listening');
98
+ console.log(`Observer server available on http://localhost:${port}
93
99
  GET /metrics -> get prometheus metrics for this instance
94
100
  GET /service-metrics -> get prometheus metrics shared accross all instances
95
101
  GET /cpu-profile -> generate and fetch a CPU profile
96
102
  GET /heap-profile -> generate and fetch a sampled heap allocation snapshot
97
103
  GET /heap-profile-all -> generate and fetch a sampled heap allocation snapshot including objects collected by GC
98
- `)
99
- }
104
+ `);
105
+ };
100
106
  export const stopObserver = async () => {
101
- if (server) { server.close() }
102
- }
107
+ if (server)
108
+ server.close();
109
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@data-fair/lib-node",
3
- "version": "2.8.1",
3
+ "version": "2.9.0",
4
4
  "description": "Small nodejs related utility functions for the data-fair stack.",
5
5
  "main": "index.js",
6
6
  "files": [
package/session.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import type { SessionState } from '@data-fair/lib-common-types/session/index.js';
2
+ import JwksClient from 'jwks-rsa';
3
+ export declare class SessionHandler {
4
+ private _jwksClient;
5
+ defaultLang: string;
6
+ get jwksClient(): JwksClient.JwksClient;
7
+ initJWKS(directoryUrl?: string): void;
8
+ verifyToken(token: string): Promise<any>;
9
+ readStateFromCookie(cookieStr: string | undefined, onlyDecode?: boolean, clearBrokenSession?: () => void): Promise<SessionState>;
10
+ }
package/session.js ADDED
@@ -0,0 +1,110 @@
1
+ import jwt from 'jsonwebtoken';
2
+ import JwksClient from 'jwks-rsa';
3
+ import cookie from 'cookie';
4
+ import { httpError } from '@data-fair/lib-utils/http-errors.js';
5
+ export class SessionHandler {
6
+ _jwksClient;
7
+ defaultLang = 'fr';
8
+ get jwksClient() {
9
+ if (!this._jwksClient)
10
+ throw new Error('session management was not initialized');
11
+ return this._jwksClient;
12
+ }
13
+ initJWKS(directoryUrl = 'http://simple-directory:8080') {
14
+ this._jwksClient = JwksClient({ jwksUri: directoryUrl + '/.well-known/jwks.json' });
15
+ }
16
+ // used for the main session token, but can also be used to use the jwks to verify other types of tokens
17
+ async verifyToken(token) {
18
+ const decoded = jwt.decode(token, { complete: true });
19
+ if (!decoded)
20
+ throw new Error('failed to decode token');
21
+ const signingKey = await this.jwksClient.getSigningKey(decoded.header.kid);
22
+ return jwt.verify(token, signingKey.getPublicKey());
23
+ }
24
+ async readStateFromCookie(cookieStr, onlyDecode, clearBrokenSession) {
25
+ const session = { lang: this.defaultLang };
26
+ if (!cookieStr)
27
+ return session;
28
+ const cookies = cookie.parse(cookieStr);
29
+ session.dark = cookies.theme_dark === '1' || cookies.theme_dark === 'true';
30
+ if (cookies.i18n_lang)
31
+ session.lang = cookies.i18n_lang;
32
+ if (!cookies.id_token || !cookies.id_token_sign)
33
+ return session;
34
+ const token = cookies.id_token + '.' + cookies.id_token_sign;
35
+ let user;
36
+ if (onlyDecode) {
37
+ user = jwt.decode(token);
38
+ }
39
+ else {
40
+ try {
41
+ user = await this.verifyToken(token);
42
+ }
43
+ catch (err) {
44
+ console.warn(err);
45
+ if (err.name === 'JwksError') {
46
+ // happens in case of temporary unavailability if SD
47
+ // better not to disconnect user in this case
48
+ throw httpError(500, 'Session token public keys not initialized');
49
+ }
50
+ else {
51
+ if (clearBrokenSession)
52
+ clearBrokenSession();
53
+ throw httpError(401);
54
+ }
55
+ }
56
+ }
57
+ if (!user)
58
+ return session;
59
+ // this is to prevent null values that are put by SD versions that do not strictly respect their schema
60
+ for (const org of user.organizations) {
61
+ if (!org.department) {
62
+ delete org.department;
63
+ delete org.departmentName;
64
+ }
65
+ }
66
+ session.user = user;
67
+ const organizationId = cookies.id_token_org;
68
+ const departmentId = cookies.id_token_dep;
69
+ const switchedRole = cookies.id_token_role;
70
+ if (organizationId) {
71
+ session.organization = user.organizations.find(o => {
72
+ if (o.id !== organizationId)
73
+ return false;
74
+ if (departmentId && departmentId !== o.department)
75
+ return false;
76
+ if (switchedRole && switchedRole !== o.role)
77
+ return false;
78
+ return true;
79
+ });
80
+ }
81
+ if (session.organization) {
82
+ session.account = {
83
+ type: 'organization',
84
+ id: session.organization.id,
85
+ name: session.organization.name,
86
+ department: session.organization.department,
87
+ departmentName: session.organization.departmentName
88
+ };
89
+ session.accountRole = session.organization.role;
90
+ }
91
+ else {
92
+ session.account = {
93
+ type: 'user',
94
+ id: user.id,
95
+ name: user.name
96
+ };
97
+ session.accountRole = 'admin';
98
+ return session;
99
+ }
100
+ if (session.user?.siteOwner) {
101
+ if (session.user.siteOwner.type === 'user' && session.user.siteOwner.id === session.user.id) {
102
+ session.siteRole = 'admin';
103
+ }
104
+ if (session.user.siteOwner.type === 'organization' && session.user.siteOwner.id === session.organization?.id) {
105
+ session.siteRole = session.organization.role;
106
+ }
107
+ }
108
+ return session;
109
+ }
110
+ }
@@ -1,16 +1,16 @@
1
1
  // @ts-nocheck
2
2
  // cf https://nodejs.org/api/module.html#customization-hooks
3
- const regexp = /\/\/ @test:spy\((.*)\)\n/g
4
- export async function load (url, context, nextLoad) {
5
- const result = await nextLoad(url, context)
6
- if (result.format === 'module' || result.format === 'module-typescript') {
7
- let source = result.source.toString()
8
- const matches = source.match(regexp)
9
- if (matches) {
10
- source = 'import { emit as __emitTestSpy } from "@data-fair/lib-node/test-spies.js"\n' + source
11
- source = source.replace(regexp, '__emitTestSpy($1)\n')
12
- result.source = Buffer.from(source, 'utf-8')
3
+ const regexp = /\/\/ @test:spy\((.*)\)\n/g;
4
+ export async function load(url, context, nextLoad) {
5
+ const result = await nextLoad(url, context);
6
+ if (result.format === 'module' || result.format === 'module-typescript') {
7
+ let source = result.source.toString();
8
+ const matches = source.match(regexp);
9
+ if (matches) {
10
+ source = 'import { emit as __emitTestSpy } from "@data-fair/lib-node/test-spies.js"\n' + source;
11
+ source = source.replace(regexp, '__emitTestSpy($1)\n');
12
+ result.source = Buffer.from(source, 'utf-8');
13
+ }
13
14
  }
14
- }
15
- return result
15
+ return result;
16
16
  }