Skip to content

Commit 8dbac12

Browse files
authored
Issue 53742: Sample Manager: Unable to perform actions from various pages on sample type with a '/' (#1848)
1 parent 794aaf0 commit 8dbac12

7 files changed

Lines changed: 43 additions & 16 deletions

File tree

packages/components/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/components/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@labkey/components",
3-
"version": "6.60.0",
3+
"version": "6.60.1",
44
"description": "Components, models, actions, and utility functions for LabKey applications and pages",
55
"sideEffects": false,
66
"files": [

packages/components/releaseNotes/components.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# @labkey/components
22
Components, models, actions, and utility functions for LabKey applications and pages
33

4+
### version 6.60.1
5+
*Released*: 3 September 2025
6+
- Issue 53742: Sample Manager: Unable to perform actions from various pages on sample type with a '/'
7+
- Update `createQueryModelId` to use encode schema/query
8+
49
### version 6.60.0
510
*Released*: 3 September 2025
611
- Issue 53801: Filter out columns for validation where `isAncestorInput()` is true
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { SchemaQuery } from '../public/SchemaQuery';
2+
import { createGridModelId } from './models';
3+
4+
describe('createGridModelId', () => {
5+
test('schemaQuery encoding', () => {
6+
expect(createGridModelId('g', new SchemaQuery('g', 'g'))).toEqual('g|g/g');
7+
expect(createGridModelId('g', new SchemaQuery('g', 'ABC', null))).toEqual('g|g/abc');
8+
expect(createGridModelId('g', new SchemaQuery('g', 'DEF', ''))).toEqual('g|g/def');
9+
expect(createGridModelId('g', new SchemaQuery('/s', '/with/SLASH/', 'sl/asH'))).toEqual(
10+
'g|$ss/$swith$sslash$s/sl$sash'
11+
);
12+
13+
const decoded = '\\$\\/&}~,\\.';
14+
const encoded = '\\$d\\$s$a$b$t$c\\$p';
15+
expect(createGridModelId('g', new SchemaQuery(decoded, decoded))).toEqual(`g|${encoded}/${encoded}`);
16+
expect(createGridModelId('g', new SchemaQuery(decoded, decoded), 'kEy')).toEqual(`g|${encoded}/${encoded}|key`);
17+
expect(createGridModelId('g', new SchemaQuery(decoded, decoded), decoded)).toEqual(
18+
`g|${encoded}/${encoded}|${decoded}`
19+
);
20+
});
21+
});
Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,10 @@
1-
import { List, Map } from 'immutable';
21
import { SchemaQuery } from '../public/SchemaQuery';
32

43
export function createGridModelId(gridId: string, schemaQuery: SchemaQuery, keyValue?: any): string {
54
const parts = [gridId, schemaQuery.getKey()];
6-
7-
if (schemaQuery && schemaQuery.viewName) {
8-
parts.push(schemaQuery.viewName);
9-
}
105
if (keyValue !== undefined) {
116
parts.push(keyValue);
127
}
138

149
return parts.join('|').toLowerCase();
1510
}
16-
17-
export interface GridData {
18-
data: Map<any, Map<string, any>>;
19-
dataKeys: List<any>;
20-
}

packages/components/src/public/QueryModel/QueryModel.test.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { ViewInfo } from '../../internal/ViewInfo';
1515
import { getQueryParams } from '../../internal/util/URL';
1616

1717
import {
18+
createQueryModelId,
1819
DEFAULT_MAX_ROWS,
1920
DEFAULT_OFFSET,
2021
flattenValuesFromRow,
@@ -56,7 +57,7 @@ describe('QueryModel', () => {
5657
expect(model.queryName).toEqual('mixtures');
5758
expect(model.viewName).toEqual(undefined);
5859
// Auto-generated model ids are based off of the SchemaQuery in the QueryConfig
59-
expect(model.id).toEqual('exp.data.mixtures');
60+
expect(model.id).toEqual('exp$Pdata.mixtures');
6061
const schemaQuery = new SchemaQuery('exp.data', 'mixtures', 'someViewName');
6162
model = new QueryModel({ schemaQuery });
6263
expect(model.viewName).toEqual('someViewName');
@@ -316,6 +317,16 @@ describe('QueryModel', () => {
316317
});
317318
});
318319

320+
describe('createQueryModelId', () => {
321+
test('with/without special characters in schema/query', () => {
322+
expect(createQueryModelId(new SchemaQuery("samples", "Blood"))).toBe('samples.Blood');
323+
expect(createQueryModelId(new SchemaQuery("exp.data", "participant"))).toBe('exp$Pdata.participant');
324+
expect(createQueryModelId(new SchemaQuery("samples", "Blood Plasma"))).toBe('samples.Blood Plasma');
325+
expect(createQueryModelId(new SchemaQuery("samples", "Blood/Plasma"))).toBe('samples.Blood$SPlasma');
326+
expect(createQueryModelId(new SchemaQuery("exp.data", "Blood/Plasma"))).toBe('exp$Pdata.Blood$SPlasma');
327+
});
328+
});
329+
319330
describe('flattenValuesFromRow', () => {
320331
test('missing params', () => {
321332
expect(JSON.stringify(flattenValuesFromRow(undefined, undefined))).toBe('{}');

packages/components/src/public/QueryModel/QueryModel.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,14 @@ export function locationHasQueryParamSettings(prefix: string, searchParams?: URL
8585
}
8686

8787
/**
88-
* Creates a QueryModel ID for a given SchemaQuery. The id is just the SchemaQuery snake-cased as
88+
* Creates a QueryModel ID for a given SchemaQuery. The id is just the SchemaQuery snake-cased as encoded
8989
* schemaName.queryName.
9090
*
9191
* @param schemaQuery: SchemaQuery
9292
*/
9393
export function createQueryModelId(schemaQuery: SchemaQuery): string {
9494
const { schemaName, queryName } = schemaQuery;
95-
return `${schemaName}.${queryName}`;
95+
return `${encodePart(schemaName)}.${encodePart(queryName)}`;
9696
}
9797

9898
const sortStringMapper = (s: QuerySort): string => s.toRequestString();

0 commit comments

Comments
 (0)