Skip to content

Commit 10d2421

Browse files
committed
introducing QueryTokenString<T>
1 parent 60901c9 commit 10d2421

9 files changed

+130
-45
lines changed

Signum.React/Scripts/FindOptions.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { TypeReference, PseudoType, QueryKey } from './Reflection';
1+
import { TypeReference, PseudoType, QueryKey, getLambdaMembers, QueryTokenString } from './Reflection';
22
import { Lite, Entity } from './Signum.Entities';
33
import { PaginationMode, OrderType, FilterOperation, FilterType, ColumnOptionsMode, UniqueType, SystemTimeMode, FilterGroupOperation } from './Signum.Entities.DynamicQuery';
44
import { SearchControlProps } from "./Search";
@@ -25,7 +25,7 @@ export interface ModalFindOptions {
2525
export interface FindOptions {
2626
queryName: PseudoType | QueryKey;
2727
groupResults?: boolean;
28-
parentToken?: string;
28+
parentToken?: string | QueryTokenString<any>;
2929
parentValue?: any;
3030

3131
filterOptions?: FilterOption[];
@@ -54,14 +54,14 @@ export function isFilterGroupOption(fo: FilterOption): fo is FilterGroupOption {
5454
}
5555

5656
export interface FilterConditionOption {
57-
token: string;
57+
token: string | QueryTokenString<any>;
5858
frozen?: boolean;
5959
operation?: FilterOperation;
6060
value: any;
6161
}
6262

6363
export interface FilterGroupOption {
64-
token?: string;
64+
token?: string | QueryTokenString<any>;
6565
groupOperation: FilterGroupOperation;
6666
filters: FilterOption[];
6767
}
@@ -87,7 +87,7 @@ export interface FilterGroupOptionParsed {
8787
}
8888

8989
export interface OrderOption {
90-
token: string;
90+
token: string | QueryTokenString<any>;
9191
orderType: OrderType;
9292
}
9393

@@ -97,7 +97,7 @@ export interface OrderOptionParsed {
9797
}
9898

9999
export interface ColumnOption {
100-
token: string;
100+
token: string | QueryTokenString<any>;
101101
displayName?: string;
102102
}
103103

Signum.React/Scripts/Finder.tsx

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -327,11 +327,11 @@ export function parseOrderOptions(orderOptions: OrderOption[], groupResults: boo
327327

328328
const completer = new TokenCompleter(qd);
329329
var sto = SubTokensOptions.CanElement | (groupResults ? SubTokensOptions.CanAggregate : 0);
330-
orderOptions.forEach(a => completer.request(a.token, sto));
330+
orderOptions.forEach(a => completer.request(a.token.toString(), sto));
331331

332332
return completer.finished()
333333
.then(() => orderOptions.map(oo => ({
334-
token: completer.get(oo.token),
334+
token: completer.get(oo.token.toString()),
335335
orderType: oo.orderType || "Ascending",
336336
}) as OrderOptionParsed));
337337
}
@@ -340,12 +340,12 @@ export function parseColumnOptions(columnOptions: ColumnOption[], groupResults:
340340

341341
const completer = new TokenCompleter(qd);
342342
var sto = SubTokensOptions.CanElement | (groupResults ? SubTokensOptions.CanAggregate : 0);
343-
columnOptions.forEach(a => completer.request(a.token, sto));
343+
columnOptions.forEach(a => completer.request(a.token.toString(), sto));
344344

345345
return completer.finished()
346346
.then(() => columnOptions.map(co => ({
347-
token: completer.get(co.token),
348-
displayName: co.displayName || completer.get(co.token).niceName,
347+
token: completer.get(co.token.toString()),
348+
displayName: co.displayName || completer.get(co.token.toString()).niceName,
349349
}) as ColumnOptionParsed));
350350
}
351351

@@ -496,10 +496,10 @@ export function parseFindOptions(findOptions: FindOptions, qd: QueryDescription)
496496
fo.filterOptions.forEach(fo => completer.requestFilter(fo, SubTokensOptions.CanElement | SubTokensOptions.CanAnyAll | canAggregate));
497497

498498
if (fo.orderOptions)
499-
fo.orderOptions.forEach(oo => completer.request(oo.token, SubTokensOptions.CanElement | canAggregate));
499+
fo.orderOptions.forEach(oo => completer.request(oo.token.toString(), SubTokensOptions.CanElement | canAggregate));
500500

501501
if (fo.columnOptions)
502-
fo.columnOptions.forEach(co => completer.request(co.token, SubTokensOptions.CanElement | canAggregate));
502+
fo.columnOptions.forEach(co => completer.request(co.token.toString(), SubTokensOptions.CanElement | canAggregate));
503503

504504
return completer.finished().then(() => {
505505

@@ -510,12 +510,12 @@ export function parseFindOptions(findOptions: FindOptions, qd: QueryDescription)
510510
systemTime: fo.systemTime,
511511

512512
columnOptions: (fo.columnOptions || []).map(co => ({
513-
token: completer.get(co.token),
514-
displayName: co.displayName || completer.get(co.token).niceName
513+
token: completer.get(co.token.toString()),
514+
displayName: co.displayName || completer.get(co.token.toString()).niceName
515515
}) as ColumnOptionParsed),
516516

517517
orderOptions: (fo.orderOptions || []).map(oo => ({
518-
token: completer.get(oo.token),
518+
token: completer.get(oo.token.toString()),
519519
orderType: oo.orderType,
520520
}) as OrderOptionParsed),
521521

@@ -642,7 +642,7 @@ export function expandParentColumn(fo: FindOptions): FindOptions {
642642
...(fo.filterOptions || [])
643643
];
644644

645-
if (!fo.parentToken.contains(".") && (fo.columnOptionsMode == undefined || fo.columnOptionsMode == "Remove")) {
645+
if (!fo.parentToken.toString().contains(".") && (fo.columnOptionsMode == undefined || fo.columnOptionsMode == "Remove")) {
646646
fo.columnOptions = [
647647
{ token: fo.parentToken },
648648
...(fo.columnOptions || [])
@@ -680,12 +680,12 @@ export class TokenCompleter {
680680
requestFilter(fo: FilterOption, options: SubTokensOptions) {
681681

682682
if (isFilterGroupOption(fo)) {
683-
fo.token && this.request(fo.token, options);
683+
fo.token && this.request(fo.token.toString(), options);
684684

685685
fo.filters.forEach(f => this.requestFilter(f, options));
686686
} else {
687687

688-
this.request(fo.token, options);
688+
this.request(fo.token.toString(), options);
689689
}
690690
}
691691

@@ -735,13 +735,13 @@ export class TokenCompleter {
735735
toFilterOptionParsed(fo: FilterOption): FilterOptionParsed {
736736
if (isFilterGroupOption(fo))
737737
return ({
738-
token: fo.token && this.get(fo.token),
738+
token: fo.token && this.get(fo.token.toString()),
739739
groupOperation: fo.groupOperation,
740740
filters: fo.filters.map(f => this.toFilterOptionParsed(f))
741741
} as FilterGroupOptionParsed);
742742
else
743743
return ({
744-
token: this.get(fo.token),
744+
token: this.get(fo.token.toString()),
745745
operation: fo.operation || "EqualTo",
746746
value: fo.value,
747747
frozen: fo.frozen || false,

Signum.React/Scripts/Lines/AutoCompleteConfig.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as React from 'react'
22
import * as Finder from '../Finder'
33
import { AbortableRequest } from '../Services'
44
import { FindOptions, FilterOptionParsed, OrderOptionParsed, OrderRequest, ResultRow, ColumnOptionParsed, ColumnRequest } from '../FindOptions'
5-
import { getTypeInfo, getQueryKey } from '../Reflection'
5+
import { getTypeInfo, getQueryKey, QueryTokenString } from '../Reflection'
66
import { ModifiableEntity, Lite, Entity, toLite, is, isLite, isEntity, getToString } from '../Signum.Entities'
77
import { Typeahead } from '../Components'
88
import { toFilterRequests } from '../Finder';
@@ -173,7 +173,7 @@ export class FindOptionsAutocompleteConfig implements AutocompleteConfig<ResultR
173173
Finder.API.FindRowsLike({
174174
queryKey: getQueryKey(this.findOptions.queryName),
175175
columns: columns.map(c => ({ token: c.token!.fullKey, displayName: c.displayName }) as ColumnRequest),
176-
filters: [{ token: "Entity.Id", operation: "EqualTo", value: lite.id }],
176+
filters: [{ token: QueryTokenString.entity<Entity>().append(e => e.id).toString(), operation: "EqualTo", value: lite.id }],
177177
orders: [],
178178
count: 1,
179179
subString: ""

Signum.React/Scripts/Operations.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import * as React from "react"
1+
import * as React from "react"
22
import { Dic } from './Globals'
33
import { ajaxPost } from './Services'
44
import {
@@ -25,7 +25,7 @@ export function start() {
2525

2626
QuickLinks.registerGlobalQuickLink(ctx => new QuickLinks.QuickLinkExplore({
2727
queryName: OperationLogEntity,
28-
parentToken: "Target",
28+
parentToken: OperationLogEntity.token(e => e.target),
2929
parentValue: ctx.lite
3030
},
3131
{

Signum.React/Scripts/Reflection.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Dic } from './Globals';
44
import { ModifiableEntity, Entity, Lite, MListElement, ModelState, MixinEntity } from './Signum.Entities'; //ONLY TYPES!
55
import { ajaxGet } from './Services';
66
import { MList } from "./Signum.Entities";
7+
import QueryTokenBuilder from './SearchControl/QueryTokenBuilder';
78

89
export function getEnumInfo(enumTypeName: string, enumId: number) {
910

@@ -865,6 +866,85 @@ export class Type<T extends ModifiableEntity> implements IType {
865866
isLite(obj: any): obj is Lite<T & Entity> {
866867
return obj && (obj as Lite<Entity>).EntityType == this.typeName;
867868
}
869+
870+
/* Constructs a QueryToken compatible string like "Name" from a strongly typed lambda like a => a.name
871+
* Note: The QueryToken language is quite different to javascript lambdas (Any, Lites, Nullable, etc) but this method works in the common simple cases*/
872+
token(): QueryTokenString<T>;
873+
token<S>(lambdaToColumn: (v: T) => S) : QueryTokenString<S>;
874+
token(lambdaToColumn?: Function): QueryTokenString<any> {
875+
if (lambdaToColumn == null)
876+
return new QueryTokenString("");
877+
else
878+
return new QueryTokenString(getLambdaMembers(lambdaToColumn).map(a => a.name.firstUpper()).join("."));
879+
}
880+
}
881+
882+
883+
/* Some examples being in ExceptionEntity:
884+
* "User" -> ExceptionEntity.token().append(a => a.user)
885+
* ExceptionEntity.token(a => a.user)
886+
* "Entity.User" -> ExceptionEntity.token().entity(a => a.user)
887+
* ExceptionEntity.token().entity().append(a=>a.user)
888+
*
889+
*/
890+
export class QueryTokenString<T> {
891+
892+
token: string;
893+
constructor(token: string) {
894+
this.token = token;
895+
}
896+
897+
toString() {
898+
return this.token;
899+
}
900+
901+
static entity<T extends Entity = Entity>() {
902+
return new QueryTokenString<T>("Entity");
903+
}
904+
905+
static count() {
906+
return new QueryTokenString("Count");
907+
}
908+
909+
systemValidFrom() {
910+
return new QueryTokenString(this.token + ".SystemValidFrom");
911+
}
912+
913+
systemValidTo() {
914+
return new QueryTokenString(this.token + "Entity.SystemValidTo");
915+
}
916+
917+
entity() : QueryTokenString<T>;
918+
entity<S>(lambdaToProperty: (v: T) => S) : QueryTokenString<S>;
919+
entity(lambdaToProperty?: Function): QueryTokenString<any> {
920+
if (this.token != "")
921+
throw new Error("entity is only meant to be used with an empty token");
922+
923+
if (lambdaToProperty == null)
924+
return new QueryTokenString("Entity")
925+
else
926+
return new QueryTokenString("Entity." + getLambdaMembers(lambdaToProperty).map(a => a.name.firstUpper()).join("."));
927+
}
928+
929+
cast<R extends Entity>(t: Type<R>): QueryTokenString<R> {
930+
return new QueryTokenString<R>(this.token + ".(" + t.typeName + ")");
931+
}
932+
933+
append<S>(lambdaToProperty: (v: T) => S): QueryTokenString<S> {
934+
return new QueryTokenString<S>(this.token + "." + getLambdaMembers(lambdaToProperty).map(a => a.name.firstUpper()).join("."));
935+
}
936+
937+
mixin<M extends MixinEntity>(t: Type<M>): QueryTokenString<M> {
938+
return new QueryTokenString<M>(this.token);
939+
}
940+
941+
implicit<S>(lambdaToProperty: (v: T) => S): QueryTokenString<S> {
942+
return new QueryTokenString<S>(this.token);
943+
}
944+
945+
expression<S>(expressionName: string): QueryTokenString<S> {
946+
return new QueryTokenString<S>(this.token + "." + expressionName);
947+
}
868948
}
869949

870950
export class EnumType<T extends string> {

Signum.React/Scripts/SearchControl/SearchControlLoaded.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ export default class SearchControlLoaded extends React.Component<SearchControlLo
173173
groupResults: fo.groupResults,
174174
filters: toFilterRequests(fo.filterOptions),
175175
columns: fo.columnOptions.filter(a => a.token != undefined).map(co => ({ token: co.token!.fullKey, displayName: co.displayName! }))
176-
.concat((!fo.groupResults && qs && qs.hiddenColumns || []).map(co => ({ token: co.token, displayName: "" }))),
176+
.concat((!fo.groupResults && qs && qs.hiddenColumns || []).map(co => ({ token: co.token.toString(), displayName: "" }))),
177177
orders: fo.orderOptions.filter(a => a.token != undefined).map(oo => ({ token: oo.token.fullKey, orderType: oo.orderType })),
178178
pagination: fo.pagination,
179179
systemTime: fo.systemTime,

Signum.React/Scripts/SearchControl/SystemTimeEditor.tsx

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
import * as moment from 'moment'
1+
import * as moment from 'moment'
22
import * as React from 'react'
33
import * as Finder from '../Finder'
44
import { classes } from '../Globals';
55
import { SystemTime, FindOptionsParsed, QueryDescription } from '../FindOptions'
66
import { SystemTimeMode } from '../Signum.Entities.DynamicQuery'
77
import { JavascriptMessage } from '../Signum.Entities'
88
import { DateTimePicker } from 'react-widgets';
9+
import { QueryTokenString } from '../Reflection';
10+
import QueryTokenBuilder from './QueryTokenBuilder';
11+
import { OperationLogEntity } from '../Signum.Entities.Basics';
912

1013
interface SystemTimeEditorProps extends React.Props<SystemTime> {
1114
findOptions: FindOptionsParsed;
@@ -33,15 +36,15 @@ export default class SystemTimeEditor extends React.Component<SystemTimeEditorPr
3336
var fop = this.props.findOptions;
3437
if (this.isPeriodChecked()) {
3538
fop.columnOptions.extract(a => a.token != null && (
36-
a.token.fullKey.startsWith("Entity.SystemValidFrom") ||
37-
a.token.fullKey.startsWith("Entity.SystemValidTo")));
39+
a.token.fullKey.startsWith(QueryTokenString.entity().systemValidFrom().toString()) ||
40+
a.token.fullKey.startsWith(QueryTokenString.entity().systemValidTo().toString())));
3841
this.props.onChanged();
3942
}
4043
else {
4144

4245
Finder.parseColumnOptions([
43-
{ token: "Entity.SystemValidFrom" },
44-
{ token: "Entity.SystemValidTo" }
46+
{ token: QueryTokenString.entity().systemValidFrom() },
47+
{ token: QueryTokenString.entity().systemValidTo() }
4548
], fop.groupResults, this.props.queryDescription).then(cops => {
4649
fop.columnOptions = [...cops, ...fop.columnOptions];
4750
this.props.onChanged();
@@ -53,8 +56,8 @@ export default class SystemTimeEditor extends React.Component<SystemTimeEditorPr
5356
var cos = this.props.findOptions.columnOptions;
5457

5558
return cos.some(a => a.token != null && (
56-
a.token.fullKey.startsWith("Entity.SystemValidFrom") ||
57-
a.token.fullKey.startsWith("Entity.SystemValidTo"))
59+
a.token.fullKey.startsWith(QueryTokenString.entity().systemValidFrom().toString()) ||
60+
a.token.fullKey.startsWith(QueryTokenString.entity().systemValidTo().toString()))
5861
);
5962
}
6063

@@ -70,23 +73,25 @@ export default class SystemTimeEditor extends React.Component<SystemTimeEditorPr
7073
}
7174

7275
handlePreviousOperationClicked = () => {
76+
77+
var prevLogToken = QueryTokenString.entity().expression<OperationLogEntity>("PreviousOperationLog");
78+
7379
var fop = this.props.findOptions;
7480
if (this.isPreviousOperationChecked()) {
75-
fop.columnOptions.extract(a => a.token != null && a.token.fullKey.startsWith("Entity.PreviousOperationLog"));
81+
fop.columnOptions.extract(a => a.token != null && a.token.fullKey.startsWith(prevLogToken.toString()));
7682
this.props.onChanged();
7783
}
7884
else {
7985

8086
Finder.parseColumnOptions([
81-
{ token: "Entity.PreviousOperationLog.Start" },
82-
{ token: "Entity.PreviousOperationLog.User" },
83-
{ token: "Entity.PreviousOperationLog.Operation" },
87+
{ token: prevLogToken.append(a => a.start) },
88+
{ token: prevLogToken.append(a => a.user) },
89+
{ token: prevLogToken.append(a => a.operation) },
8490
], fop.groupResults, this.props.queryDescription).then(cops => {
8591
fop.columnOptions = [...cops, ...fop.columnOptions];
8692
this.props.onChanged();
8793
}).done();
8894
}
89-
9095
}
9196

9297
isPreviousOperationChecked() {

Signum.React/Scripts/SearchControl/ValueSearchControl.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ import { classes } from '../Globals'
55
import * as Finder from '../Finder'
66
import { FindOptions, FindOptionsParsed, SubTokensOptions, QueryToken, QueryValueRequest } from '../FindOptions'
77
import { Lite, Entity, getToString, EmbeddedEntity } from '../Signum.Entities'
8-
import { getQueryKey, toNumbroFormat, toMomentFormat, getEnumInfo } from '../Reflection'
8+
import { getQueryKey, toNumbroFormat, toMomentFormat, getEnumInfo, QueryTokenString } from '../Reflection'
99
import { AbortableRequest } from "../Services";
1010
import { SearchControlProps } from "./SearchControl";
1111
import { BsColor } from '../Components';
1212
import { toFilterRequests } from '../Finder';
1313

1414
export interface ValueSearchControlProps extends React.Props<ValueSearchControl> {
15-
valueToken?: string;
15+
valueToken?: string | QueryTokenString<any>;
1616
findOptions: FindOptions;
1717
isLink?: boolean;
1818
isBadge?: boolean | "MoreThanZero";
@@ -53,7 +53,7 @@ export default class ValueSearchControl extends React.Component<ValueSearchContr
5353
return {
5454
queryKey: fo.queryKey,
5555
filters: toFilterRequests(fo.filterOptions),
56-
valueToken: this.props.valueToken,
56+
valueToken: this.props.valueToken && this.props.valueToken.toString(),
5757
systemTime: fo.systemTime && { ...fo.systemTime }
5858
};
5959
}
@@ -87,7 +87,7 @@ export default class ValueSearchControl extends React.Component<ValueSearchContr
8787

8888
this.setState({ token: undefined, value: undefined });
8989
if (props.valueToken)
90-
Finder.parseSingleToken(props.findOptions.queryName, props.valueToken, SubTokensOptions.CanAggregate | SubTokensOptions.CanAnyAll)
90+
Finder.parseSingleToken(props.findOptions.queryName, props.valueToken.toString(), SubTokensOptions.CanAggregate | SubTokensOptions.CanAnyAll)
9191
.then(st => {
9292
this.setState({ token: st });
9393
this.props.onTokenLoaded && this.props.onTokenLoaded();

0 commit comments

Comments
 (0)