fhirsmith 0.7.1 → 0.7.3

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/xig/xig.js CHANGED
@@ -99,14 +99,15 @@ function validateExternalUrl(url) {
99
99
 
100
100
  // Secure SQL query building with parameterized queries
101
101
  function buildSecureResourceQuery(queryParams, offset = 0, limit = 50) {
102
- const { realm, auth, ver, type, rt, text } = queryParams;
102
+ const { realm, auth, ver, type, rt, text, pkg, onlyUsed } = queryParams;
103
103
 
104
104
  let baseQuery = `
105
105
  SELECT
106
106
  ResourceKey, ResourceType, Type, Kind, Description, PackageKey,
107
107
  Realm, Authority, R2, R2B, R3, R4, R4B, R5, R6,
108
108
  Id, Url, Version, Status, Date, Name, Title, Content,
109
- Supplements, Details, FMM, WG, StandardsStatus, Web
109
+ Supplements, Details, FMM, WG, StandardsStatus, Web,
110
+ (SELECT COUNT(*) FROM DependencyList WHERE TargetKey = Resources.ResourceKey) AS UsageCount
110
111
  FROM Resources
111
112
  WHERE 1=1
112
113
  `;
@@ -225,6 +226,17 @@ function buildSecureResourceQuery(queryParams, offset = 0, limit = 50) {
225
226
  }
226
227
  }
227
228
 
229
+ // Package filter - matches packageId#version containing the search string
230
+ if (pkg && pkg !== '') {
231
+ conditions.push('AND PackageKey IN (SELECT PackageKey FROM Packages WHERE (Id || \'#\' || Version) LIKE ?)');
232
+ params.push(`%${pkg}%`);
233
+ }
234
+
235
+ // Only used filter
236
+ if (onlyUsed === 'true') {
237
+ conditions.push('AND EXISTS (SELECT 1 FROM DependencyList WHERE TargetKey = Resources.ResourceKey)');
238
+ }
239
+
228
240
  // Build final query
229
241
  const fullQuery = baseQuery + ' ' + conditions.join(' ') + ' ORDER BY ResourceType, Type, Description LIMIT ? OFFSET ?';
230
242
  params.push(limit, offset);
@@ -233,7 +245,7 @@ function buildSecureResourceQuery(queryParams, offset = 0, limit = 50) {
233
245
  }
234
246
 
235
247
  function buildSecureResourceCountQuery(queryParams) {
236
- const { realm, auth, ver, type, rt, text } = queryParams;
248
+ const { realm, auth, ver, type, rt, text, pkg, onlyUsed } = queryParams;
237
249
 
238
250
  let baseQuery = 'SELECT COUNT(*) as total FROM Resources WHERE 1=1';
239
251
  const conditions = [];
@@ -324,6 +336,17 @@ function buildSecureResourceCountQuery(queryParams) {
324
336
  }
325
337
  }
326
338
 
339
+ // Package filter - matches packageId#version containing the search string
340
+ if (pkg && pkg !== '') {
341
+ conditions.push('AND PackageKey IN (SELECT PackageKey FROM Packages WHERE (Id || \'#\' || Version) LIKE ?)');
342
+ params.push(`%${pkg}%`);
343
+ }
344
+
345
+ // Only used filter
346
+ if (onlyUsed === 'true') {
347
+ conditions.push('AND EXISTS (SELECT 1 FROM DependencyList WHERE TargetKey = Resources.ResourceKey)');
348
+ }
349
+
327
350
  const fullQuery = baseQuery + ' ' + conditions.join(' ');
328
351
  return { query: fullQuery, params };
329
352
  }
@@ -449,7 +472,7 @@ function sqlEscapeString(str) {
449
472
  }
450
473
 
451
474
  function buildSqlFilter(queryParams) {
452
- const { realm, auth, ver, type, rt, text } = queryParams;
475
+ const { realm, auth, ver, type, rt, text, pkg, onlyUsed } = queryParams;
453
476
  let filter = '';
454
477
 
455
478
  // Realm filter
@@ -555,6 +578,17 @@ function buildSqlFilter(queryParams) {
555
578
  }
556
579
  }
557
580
 
581
+ // Package filter - matches packageId#version containing the search string
582
+ if (pkg && pkg !== '') {
583
+ const escapedPkg = sqlEscapeString(pkg);
584
+ filter += ` and PackageKey in (select PackageKey from Packages where (Id || '#' || Version) like '%${escapedPkg}%')`;
585
+ }
586
+
587
+ // Only used filter
588
+ if (onlyUsed === 'true') {
589
+ filter += ` and exists (select 1 from DependencyList where TargetKey = Resources.ResourceKey)`;
590
+ }
591
+
558
592
  // Convert to proper WHERE clause
559
593
  if (filter !== '') {
560
594
  // Remove the first " and " and prepend "WHERE "
@@ -600,7 +634,8 @@ function buildResourceListQuery(queryParams, offset = 0, limit = 50) {
600
634
  FMM,
601
635
  WG,
602
636
  StandardsStatus,
603
- Web
637
+ Web,
638
+ (SELECT COUNT(*) FROM DependencyList WHERE TargetKey = Resources.ResourceKey) AS UsageCount
604
639
  FROM Resources
605
640
  ${whereClause}
606
641
  ORDER BY ResourceType, Type, Description
@@ -784,6 +819,7 @@ async function buildResourceTable(queryParams, resourceCount, offset = 0) {
784
819
  break;
785
820
  }
786
821
 
822
+ parts.push('<th>Usage #</th>');
787
823
  parts.push('</tr>');
788
824
 
789
825
  const resourceRows = await new Promise((resolve, reject) => {
@@ -912,6 +948,10 @@ async function buildResourceTable(queryParams, resourceCount, offset = 0) {
912
948
  }
913
949
  }
914
950
 
951
+ // Usage count column
952
+ const usageCount = row.UsageCount || 0;
953
+ parts.push(`<td>${usageCount > 0 ? usageCount.toLocaleString() : ''}</td>`);
954
+
915
955
  parts.push('</tr>');
916
956
  }
917
957
 
@@ -1116,7 +1156,7 @@ function makeSelect(selectedValue, optionsList, name = 'rt') {
1116
1156
  }
1117
1157
 
1118
1158
  function buildAdditionalForm(queryParams) {
1119
- const { ver, realm, auth, type, rt, text } = queryParams;
1159
+ const { ver, realm, auth, type, rt, text, pkg, onlyUsed } = queryParams;
1120
1160
 
1121
1161
  let html = '<form method="GET" action="" style="background-color: #eeeeee; border: 1px black solid; padding: 6px; font-size: 12px; font-family: verdana;">';
1122
1162
 
@@ -1142,6 +1182,7 @@ function buildAdditionalForm(queryParams) {
1142
1182
  const profileResources = getCachedSet('profileResources');
1143
1183
  if (profileResources.length > 0) {
1144
1184
  html += 'Type: ' + makeSelect(rt, profileResources) + ' ';
1185
+ html += '<br/>';
1145
1186
  }
1146
1187
  break;
1147
1188
  }
@@ -1150,6 +1191,7 @@ function buildAdditionalForm(queryParams) {
1150
1191
  const profileTypes = getCachedSet('profileTypes');
1151
1192
  if (profileTypes.length > 0) {
1152
1193
  html += 'Type: ' + makeSelect(rt, profileTypes) + ' ';
1194
+ html += '<br/>';
1153
1195
  }
1154
1196
  break;
1155
1197
  }
@@ -1162,6 +1204,7 @@ function buildAdditionalForm(queryParams) {
1162
1204
  const extensionContexts = getCachedSet('extensionContexts');
1163
1205
  if (extensionContexts.length > 0) {
1164
1206
  html += 'Context: ' + makeSelect(rt, extensionContexts) + ' ';
1207
+ html += '<br/>';
1165
1208
  }
1166
1209
  break;
1167
1210
  }
@@ -1172,6 +1215,7 @@ function buildAdditionalForm(queryParams) {
1172
1215
  // Convert txSources map to "code=display" format
1173
1216
  const sourceOptions = Object.keys(txSources).map(code => `${code}=${txSources[code]}`);
1174
1217
  html += 'Source: ' + makeSelect(rt, sourceOptions) + ' ';
1218
+ html += '<br/>';
1175
1219
  }
1176
1220
  break;
1177
1221
  }
@@ -1182,6 +1226,7 @@ function buildAdditionalForm(queryParams) {
1182
1226
  // Convert txSources map to "code=display" format
1183
1227
  const sourceOptionsCM = Object.keys(txSourcesCM).map(code => `${code}=${txSourcesCM[code]}`);
1184
1228
  html += 'Source: ' + makeSelect(rt, sourceOptionsCM) + ' ';
1229
+ html += '<br/>';
1185
1230
  }
1186
1231
  break;
1187
1232
  }
@@ -1190,15 +1235,19 @@ function buildAdditionalForm(queryParams) {
1190
1235
  const resourceTypes = getCachedSet('resourceTypes');
1191
1236
  if (resourceTypes.length > 0) {
1192
1237
  html += 'Type: ' + makeSelect(rt, resourceTypes);
1238
+ html += '<br/>';
1193
1239
  }
1194
1240
  break;
1195
1241
  }
1196
1242
  }
1197
1243
 
1198
- // Add text search field
1244
+ // Add text search field and package filter field
1199
1245
  html += `Text: <input type="text" name="text" value="${escape(text || '')}" class="" style="width: 200px;"/> `;
1246
+ html += `Package: <input type="text" name="pkg" value="${escape(pkg || '')}" placeholder="e.g. hl7.fhir.us" class="" style="width: 200px;"/> `;
1200
1247
 
1201
- // Add submit button
1248
+ // Add submit button with 'only used' checkbox immediately before it
1249
+ const onlyUsedChecked = onlyUsed === 'true' ? ' checked' : '';
1250
+ html += `<input type="checkbox" name="onlyUsed" value="true"${onlyUsedChecked}/> Only Used `;
1202
1251
  html += '<input type="submit" value="Search" style="color:rgb(89, 137, 241)"/>';
1203
1252
 
1204
1253
  html += '</form>';
@@ -2252,6 +2301,8 @@ router.get('/', async (req, res) => {
2252
2301
  type: req.query.type || '',
2253
2302
  rt: req.query.rt || '',
2254
2303
  text: req.query.text || '',
2304
+ pkg: req.query.pkg || '',
2305
+ onlyUsed: req.query.onlyUsed || '',
2255
2306
  offset: req.query.offset || '0'
2256
2307
  };
2257
2308