-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RDP's in TS OSDK #1035
base: main
Are you sure you want to change the base?
RDP's in TS OSDK #1035
Changes from 3 commits
246f893
50ca838
4e17a4e
01f5935
ebe19e4
4ea27dc
d2d0a0d
efbed3d
40ee625
3e535da
20835ea
76a3ee1
73c23fe
d605787
214dad7
73fbffa
9070c29
ac7f03f
2777334
a8546ca
0cd49f5
0a89f9c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,10 @@ | |
* limitations under the License. | ||
*/ | ||
|
||
import type { | ||
NumericDeriveAggregateOption, | ||
StringDeriveAggregateOption, | ||
} from "../derivedProperties/WithPropertyObjectSet.js"; | ||
import type { PropertyValueClientToWire } from "../mapping/PropertyValueMapping.js"; | ||
import type { | ||
ObjectOrInterfaceDefinition, | ||
|
@@ -30,25 +34,24 @@ export type NumericAggregateOption = | |
| "approximateDistinct" | ||
| "exactDistinct"; | ||
|
||
type AGG_FOR_TYPE<T> = number extends T ? NumericAggregateOption | ||
: string extends T ? StringAggregateOption | ||
type AGG_FOR_TYPE<T, U extends boolean> = number extends T | ||
? U extends true ? NumericAggregateOption : NumericDeriveAggregateOption | ||
: string extends T | ||
? U extends true ? StringAggregateOption : StringDeriveAggregateOption | ||
: never; | ||
|
||
export type ValidAggregationKeys< | ||
Q extends ObjectOrInterfaceDefinition, | ||
StringOptions extends string = StringAggregateOption, | ||
NumericOptions extends string = NumericAggregateOption, | ||
R extends "aggregate" | "withPropertiesAggregate" = "aggregate", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ssanjay1 Is this what you had in mind? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea that works There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Though maybe keeping parity with the other names it should be |
||
> = keyof ( | ||
& { | ||
[ | ||
KK in AggregatableKeys<Q> as `${KK & string}:${number extends | ||
KK in AggregatableKeys<Q> as `${KK & string}:${AGG_FOR_TYPE< | ||
PropertyValueClientToWire[ | ||
CompileTimeMetadata<Q>["properties"][KK]["type"] | ||
] ? NumericOptions | ||
: string extends PropertyValueClientToWire[ | ||
CompileTimeMetadata<Q>["properties"][KK]["type"] | ||
] ? StringOptions | ||
: never}` | ||
], | ||
R extends "aggregate" ? true : false | ||
>}` | ||
]?: any; | ||
} | ||
& { $count?: any } | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,45 +51,43 @@ interface FilterableDeriveObjectSet< | |
clause: WhereClause<Q>, | ||
) => this; | ||
} | ||
export type CollectDeriveAggregations = "collectSet" | "collectList"; | ||
|
||
type CollectAggregations = "collectSet" | "collectList"; | ||
|
||
type BaseAggregations = | ||
export type BaseDeriveAggregations = | ||
| "approximateDistinct" | ||
| "exactDistinct" | ||
| "approximatePercentile"; | ||
|
||
type StringDeriveAggregateOption = | ||
| BaseAggregations | ||
| CollectAggregations; | ||
export type StringDeriveAggregateOption = | ||
| BaseDeriveAggregations | ||
| CollectDeriveAggregations; | ||
|
||
type NumericDeriveAggregateOption = | ||
export type NumericDeriveAggregateOption = | ||
| "min" | ||
| "max" | ||
| "sum" | ||
| "avg" | ||
| BaseAggregations | ||
| CollectAggregations; | ||
| BaseDeriveAggregations | ||
| CollectDeriveAggregations; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I am thinking about whats ideal we need to add some type tests. I'm okay if its in a follow up |
||
interface AggregatableWithPropertyObjectSet< | ||
Q extends ObjectOrInterfaceDefinition, | ||
> extends FilterableDeriveObjectSet<Q> { | ||
> { | ||
readonly aggregate: < | ||
V extends ValidAggregationKeys< | ||
Q, | ||
StringDeriveAggregateOption, | ||
NumericDeriveAggregateOption | ||
"withPropertiesAggregate" | ||
>, | ||
>( | ||
aggregationSpecifier: V, | ||
opts?: V extends `${any}:${infer P}` | ||
? P extends CollectAggregations ? { limit: number } | ||
? P extends CollectDeriveAggregations ? { limit: number } | ||
: P extends "approximatePercentile" ? { percentile: number } | ||
: never | ||
: never, | ||
) => WithPropertyDefinition< | ||
V extends `${infer N}:${infer P}` | ||
? P extends CollectAggregations ? PropertyDef< | ||
? P extends CollectDeriveAggregations ? PropertyDef< | ||
CompileTimeMetadata<Q>["properties"][N]["type"], | ||
"nullable", | ||
"array" | ||
|
@@ -104,15 +102,19 @@ interface AggregatableWithPropertyObjectSet< | |
|
||
interface SingleLinkWithPropertyObjectSet< | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
Q extends ObjectOrInterfaceDefinition, | ||
> extends AggregatableWithPropertyObjectSet<Q>, BaseWithPropertyObjectSet<Q> { | ||
> extends | ||
AggregatableWithPropertyObjectSet<Q>, | ||
FilterableDeriveObjectSet<Q>, | ||
BaseWithPropertyObjectSet<Q> | ||
{ | ||
readonly selectProperty: <R extends PropertyKeys<Q>>( | ||
propertyName: R, | ||
) => WithPropertyDefinition<CompileTimeMetadata<Q>["properties"][R]>; | ||
} | ||
|
||
interface ManyLinkWithPropertyObjectSet< | ||
Q extends ObjectOrInterfaceDefinition, | ||
> extends AggregatableWithPropertyObjectSet<Q> { | ||
> extends AggregatableWithPropertyObjectSet<Q>, FilterableDeriveObjectSet<Q> { | ||
readonly pivotTo: <L extends LinkNames<Q>>( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So just clarifying, the reason we need to duplicate There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah that's correct, which makes sense because for this to work you have to basically be navigating to exactly one object There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should probably document that here too. |
||
type: L, | ||
) => ManyLinkWithPropertyObjectSet<LinkedType<Q, L>>; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,7 +42,7 @@ export function createWithPropertiesObjectSet< | |
type: "searchAround", | ||
objectSet, | ||
link, | ||
}, definitionMap) as any; | ||
}, definitionMap); | ||
}, | ||
where: (clause) => { | ||
return createWithPropertiesObjectSet(objectType, { | ||
|
@@ -100,24 +100,30 @@ export function createWithPropertiesObjectSet< | |
"Invalid aggregation operation " + aggregationOperation, | ||
); | ||
} | ||
definitionMap.set(definitionId, { | ||
definitionMap.set(definitionId.toString(), { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suppose for a moment instead of
No need to do accounting of ids and we don't have to leak them/make them part of our public API. Its still not perfect because people might return their own objects thinking thats okay when its not and they still might do something weird like reuse an object from before (which won't work because its a different map) but its closer. Ultimately I think we are probably better just designing an IR that is based on raw objects. |
||
type: "selection", | ||
objectSet: objectSet, | ||
operation: aggregationOperationDefinition, | ||
}); | ||
return { definitionId: definitionId, type: { type: "integer" } as any }; | ||
return { | ||
definitionId: definitionId.toString(), | ||
type: {} as any, | ||
}; | ||
}, | ||
selectProperty: (name) => { | ||
const definitionId = idCounter++; | ||
definitionMap.set(definitionId, { | ||
definitionMap.set(definitionId.toString(), { | ||
type: "selection", | ||
objectSet: objectSet, | ||
operation: { | ||
type: "get", | ||
selectedPropertyApiName: name, | ||
}, | ||
}); | ||
return { definitionId: definitionId, type: { type: "integer" } as any }; | ||
return { | ||
definitionId: definitionId.toString(), | ||
type: {} as any, | ||
}; | ||
}, | ||
}; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,7 +18,7 @@ | |
CompileTimeMetadata, | ||
ConvertProps, | ||
InterfaceDefinition, | ||
ObjectMetadata, | ||
ObjectSet, | ||
Osdk, | ||
PropertyKeys, | ||
|
@@ -466,6 +466,19 @@ | |
}); | ||
}); | ||
|
||
it("enforces a return only of correct type", () => { | ||
client(Employee).withProperties({ | ||
// @ts-expect-error | ||
"derivedPropertyName": (base) => { | ||
return base.pivotTo("peeps"); | ||
}, | ||
// @ts-expect-error | ||
"derivedPropertyName2": (base) => { | ||
return { incorrect: "type" }; | ||
}, | ||
}); | ||
}); | ||
|
||
// Executed code fails since we're providing bad strings to the function | ||
it.fails("correctly narrows types of aggregate function", () => { | ||
client(Employee).withProperties({ | ||
|
@@ -479,6 +492,9 @@ | |
// @ts-expect-error | ||
base.pivotTo("lead").aggregate("employeeId:notAnOp"); | ||
|
||
// @ts-expect-error | ||
base.pivotTo("lead").aggregate(""); | ||
|
||
base.pivotTo("lead").aggregate("employeeId:collectList"); | ||
|
||
return base.pivotTo("lead").aggregate("employeeId:sum"); | ||
|
@@ -590,8 +606,17 @@ | |
base.pivotTo("lead").selectProperty("employeeId"), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we add a test to verify things don't break if we select a property that has all undefined values? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you mean? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Basically if a prop returns an undefined value |
||
}).fetchOne(50035); | ||
|
||
expectTypeOf(objectWithRdp.derivedPropertyName).toEqualTypeOf<number>(); | ||
expectTypeOf(objectWithRdp.derivedPropertyName).toEqualTypeOf< | ||
number | undefined | ||
>(); | ||
expect(objectWithRdp.derivedPropertyName).toBe(1); | ||
|
||
const objectWithUndefinedRdp = await client(Employee).withProperties({ | ||
"derivedPropertyName": (base) => | ||
base.pivotTo("lead").selectProperty("employeeId"), | ||
}).fetchOne(50036); | ||
|
||
expect(objectWithUndefinedRdp.derivedPropertyName).toBeUndefined(); | ||
}); | ||
|
||
describe("withPropertiesObjectSet", () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: don't pass this string. This test seems to be directly testing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this also means that this entire describe should really be in |
||
|
@@ -607,7 +632,7 @@ | |
}; | ||
|
||
const result = clause["derivedPropertyName"](deriveObjectSet); | ||
const definition = map.get(result.definitionId as string); | ||
const definition = map.get(result.definitionId); | ||
expect(definition).toMatchInlineSnapshot(` | ||
{ | ||
"objectSet": { | ||
|
@@ -645,12 +670,12 @@ | |
}; | ||
|
||
const result = clause["derivedPropertyName"](deriveObjectSet); | ||
const definition = map.get(result.definitionId as string); | ||
const definition = map.get(result.definitionId); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have some mixed feelings about the |
||
|
||
const secondResult = clause["secondaryDerivedPropertyName"]( | ||
deriveObjectSet, | ||
); | ||
const secondDefinition = map.get(secondResult.definitionId as string); | ||
const secondDefinition = map.get(secondResult.definitionId); | ||
|
||
expect(definition).toMatchInlineSnapshot(` | ||
{ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: rename
U
so its clear what that boolean generic is for