@data-fair/dev-server 2.6.2 → 2.6.4

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/README.md CHANGED
@@ -50,10 +50,9 @@ développement, dans la même version mineure. La recherche part du nom lu dans
50
50
  balise `<meta name="application-name">` de l'`index.html` local et de la version
51
51
  du `package.json`.
52
52
 
53
- Une application de base réservée à une organisation (`privateAccess`) est
54
- invisible à une requête anonyme. Le paramètre `privateAccess` la rend visible,
55
- mais data-fair répond 401 sans authentification — il n'est donc envoyé que si
56
- `DATAFAIR_API_KEY` est renseignée.
53
+ Une application de base réservée à une organisation (`privateAccess`) reste
54
+ invisible : data-fair ne lit pas la clé d'API sur `/base-applications` et répond
55
+ 401 au paramètre `privateAccess`, que le dev-server n'envoie donc pas.
57
56
 
58
57
  Ne pas la trouver n'arrête pas la recherche pour autant : les applications qui
59
58
  tournent dessus sont souvent publiques, et il suffit de connaître son URL pour
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@data-fair/dev-server",
3
- "version": "2.6.2",
3
+ "version": "2.6.4",
4
4
  "description": "A development server for optimal development experience of data-fair applications.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/app.js CHANGED
@@ -30,42 +30,62 @@ let devServerWS = [];
30
30
  let dataFairWS = [];
31
31
  const dataFairWSChannels = new Map();
32
32
  const dfWsUrl = config.dataFair.url.replace('http://', 'ws://').replace('https://', 'wss://') + '/';
33
- const dataFairOutputWS = new WebSocket(dfWsUrl);
34
- dataFairOutputWS.on('open', () => {
35
- debug('connect to external websocket server ' + dfWsUrl);
36
- });
37
- dataFairOutputWS.on('message', (data) => {
38
- const dataStr = data.toString();
39
- const message = JSON.parse(dataStr);
40
- debug('incoming message from data-fair socket', message);
41
- for (const ws of dataFairWS) {
42
- if (dataFairWSChannels.get(ws)?.includes(message.channel)) {
43
- ws.send(dataStr);
33
+ // the connection to data-fair is reopened whenever it closes (network change, data-fair restart),
34
+ // and the channels of the connected apps are subscribed again on it
35
+ let dataFairOutputWS;
36
+ const subscribe = (channel) => {
37
+ const message = { type: 'subscribe', channel };
38
+ if (config.dataFair.apiKey)
39
+ message.apiKey = config.dataFair.apiKey;
40
+ dataFairOutputWS.send(JSON.stringify(message));
41
+ };
42
+ const connectDataFairWS = () => {
43
+ dataFairOutputWS = new WebSocket(dfWsUrl);
44
+ dataFairOutputWS.on('open', () => {
45
+ debug('connect to external websocket server ' + dfWsUrl);
46
+ for (const channel of new Set([...dataFairWSChannels.values()].flat()))
47
+ subscribe(channel);
48
+ });
49
+ dataFairOutputWS.on('message', (data) => {
50
+ const dataStr = data.toString();
51
+ const message = JSON.parse(dataStr);
52
+ debug('incoming message from data-fair socket', message);
53
+ for (const ws of dataFairWS) {
54
+ if (dataFairWSChannels.get(ws)?.includes(message.channel)) {
55
+ ws.send(dataStr);
56
+ }
44
57
  }
45
- }
46
- });
47
- dataFairOutputWS.on('error', (err) => console.error('failed to connect to external websocket server', dfWsUrl, err));
48
- dataFairOutputWS.on('close', () => console.error('external websocket was closed', dfWsUrl));
58
+ });
59
+ dataFairOutputWS.on('error', (err) => console.error('failed to connect to external websocket server', dfWsUrl, err));
60
+ dataFairOutputWS.on('close', () => {
61
+ console.error('external websocket was closed, reconnecting in 2s', dfWsUrl);
62
+ setTimeout(connectDataFairWS, 2000);
63
+ });
64
+ };
65
+ connectDataFairWS();
49
66
  server.on('upgrade', function upgrade(req, socket, head) {
50
67
  wss.handleUpgrade(req, socket, head, (ws) => {
51
68
  debug('Opening websocket ' + req.url);
52
69
  if (req.url === '/data-fair' || req.url === '/data-fair/') {
53
70
  dataFairWS.push(ws);
54
- ws.on('close', () => { dataFairWS = dataFairWS.filter(_ws => _ws !== ws); });
71
+ ws.on('close', () => {
72
+ dataFairWS = dataFairWS.filter(_ws => _ws !== ws);
73
+ dataFairWSChannels.delete(ws);
74
+ });
55
75
  ws.on('message', (data) => {
56
- if (dataFairOutputWS) {
57
- const message = JSON.parse(data);
58
- debug('outgoing message to data-fair socket', message);
59
- if (message.type === 'subscribe') {
60
- dataFairWSChannels.set(ws, (dataFairWSChannels.get(ws) ?? []).concat([message.channel]));
61
- if (config.dataFair.apiKey)
62
- message.apiKey = config.dataFair.apiKey;
63
- }
64
- if (message.type === 'unsubscribe') {
65
- dataFairWSChannels.set(ws, (dataFairWSChannels.get(ws) ?? []).filter(c => c !== message.channel));
66
- }
67
- dataFairOutputWS.send(JSON.stringify(message));
76
+ const message = JSON.parse(data);
77
+ debug('outgoing message to data-fair socket', message);
78
+ if (message.type === 'subscribe') {
79
+ dataFairWSChannels.set(ws, (dataFairWSChannels.get(ws) ?? []).concat([message.channel]));
80
+ if (config.dataFair.apiKey)
81
+ message.apiKey = config.dataFair.apiKey;
82
+ }
83
+ if (message.type === 'unsubscribe') {
84
+ dataFairWSChannels.set(ws, (dataFairWSChannels.get(ws) ?? []).filter(c => c !== message.channel));
68
85
  }
86
+ // while data-fair is unreachable the subscriptions are only recorded, the open handler sends them
87
+ if (dataFairOutputWS.readyState === WebSocket.OPEN)
88
+ dataFairOutputWS.send(JSON.stringify(message));
69
89
  });
70
90
  }
71
91
  else if (req.url === '/') {
@@ -164,15 +184,6 @@ const localAppInfo = async () => {
164
184
  }
165
185
  return { name, version, packageName };
166
186
  };
167
- // The owner the dev-server works on behalf of, in the "type:id[:department]" form the
168
- // data-fair api expects for its owner and privateAccess filters.
169
- const ownerFilter = () => {
170
- const owner = config.dataFair.owner;
171
- let filter = `${owner.type}:${owner.id}`;
172
- if (owner.department)
173
- filter += ':' + owner.department;
174
- return filter;
175
- };
176
187
  // Enrich the datasets from the remote data-fair and rewrite every remote origin to ours
177
188
  // (pure helpers in enrich.ts, wired here to our remote api and local origins).
178
189
  const prepareConfig = (configuration) => prepareRemoteConfig(configuration, {
@@ -323,15 +334,12 @@ app.get('/configurations', async (req, res) => {
323
334
  return;
324
335
  }
325
336
  response.minorVersion = minorVersion(version);
326
- // a base application restricted to an organization is invisible to an anonymous request.
327
- // privateAccess is what makes it visible, and data-fair answers 401 to it without
328
- // credentials, so it is only sent when an api key is configured.
329
337
  response.authenticated = !!config.dataFair.apiKey;
330
338
  try {
331
- let baseAppsQuery = '/base-applications?applicationName=' + encodeURIComponent(name) + '&size=1000&count=false';
332
- if (response.authenticated)
333
- baseAppsQuery += '&privateAccess=' + encodeURIComponent(ownerFilter());
334
- const baseApps = await remoteFetch(baseAppsQuery);
339
+ // no privateAccess filter: data-fair reads no api key on /base-applications and answers 401
340
+ // to it, so a base application restricted to an organization stays invisible here and is
341
+ // reached by guessing its url below
342
+ const baseApps = await remoteFetch('/base-applications?applicationName=' + encodeURIComponent(name) + '&size=1000&count=false');
335
343
  const matchingBaseApps = (baseApps.results ?? []).filter((b) => typeof b.version === 'string' && minorVersion(b.version) === response.minorVersion);
336
344
  // Not finding the base application is not the end of the search: it may simply be private
337
345
  // while the applications running on it are public, and those are reachable as soon as its