-
Notifications
You must be signed in to change notification settings - Fork 9
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
[mds-ingest-service] Add pagination and order support to getEvents via ingest #643
[mds-ingest-service] Add pagination and order support to getEvents via ingest #643
Conversation
@@ -42,7 +42,7 @@ export class EventEntity extends IdentityColumn(RecordedColumn(class {})) implem | |||
provider_id: EventEntityModel['provider_id'] | |||
|
|||
@Column('bigint', { transformer: BigintTransformer, primary: true }) | |||
timestamp: EventEntityModel['timestamp'] | |||
timestamp: Timestamp |
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.
Strange type behavior here with the typeorm-cursor-pagination
lib.
When this is left as EventEntityModel['timestamp']
, and you pass limit
to the buildPaginator(...)
function, it results in the following error:
unknown type in cursor: [object]1625149089973
Which corresponds to the time_range: {begin}
parameter.
Interestingly, this is only an issue when limit is passed. If the type declaration here is left as EventEntityModel['timestamp']
and limit isn't provided, it works fine. As soon as limit is provided, it breaks.
Changing this to Timestamp
directly here fixes the issue in both cases.
This doesn't have any issues afaict, all previous tests are still passing with this change. π€·
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.
Yep. Weβve seen this before.
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.
We've recently stopped defining entity properties in terms of Entity or (especially) Domain model properties and use the base types instead. Some libraries that use metadata reflection have issues tracing the aliases to the base type. In recent repositories, you will see a pattern something like:
export class SomeEntity {
@Column(...)
entity_id: UUID
@Column(...)
timestamp: Timestamp
}
/* Create an alias to keep consistent with other services */
export SomeEntityModel = SomeEntity
@@ -199,14 +219,61 @@ class IngestReadWriteRepository extends ReadWriteRepository { | |||
query.andWhere('events.provider_id = ANY(:provider_ids)', { provider_ids }) | |||
} | |||
|
|||
const entities = await query.limit(limit).getMany() | |||
// Use query instead of paginator to manage order if using a joined field |
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.
couldn't figure out how to pass an order column string for a joined field to buildPaginator without it throwing an error, if there's a better way to handle this field happy to change it.
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.
Nice work!
@@ -70,6 +70,19 @@ export type TimeRange = { | |||
start: Timestamp | |||
end: Timestamp | |||
} | |||
|
|||
export const GetVehicleEventsOrderColumn = <const>['timestamp', 'vehicle_id', 'provider_id', 'vehicle_state'] |
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.
Just curious as I've seen this in other places as I learn typescript. Is it common to name the value and type name the same? I've seen it where the value name is descriptive and would name it like "GetVehicleEventOrderColumns" (with an s to describe these are all the columns) and the type is then without the "s". Also, is it preferred to use vs "as const"? Are they the same?
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.
I've seen instances of both
export const FooTypes = <const>['bar', 'baz']
export type FooType = typeof FooTypes[number]
and
export const FooType = <const>['bar', 'baz']
export type FooType = typeof FooType[number]
In various places in the code base. I think the latter is the preferred.
My assumption is that the latter just reduces the overhead of knowing which type to choose (FooTypes vs FooTypes
) since TS will interpret it as the type declaration. (@drtyh2o feel free to tell me I'm off base here π ).
As for
<const> foo
vs foo as const
, they are equivalent except in a .tsx
file.
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.
There are probably opportunities for creating some cursor utilities as there seems to be a lot of similarities between this backend and the trips backend, but this LGTM.
@@ -42,7 +42,7 @@ export class EventEntity extends IdentityColumn(RecordedColumn(class {})) implem | |||
provider_id: EventEntityModel['provider_id'] | |||
|
|||
@Column('bigint', { transformer: BigintTransformer, primary: true }) | |||
timestamp: EventEntityModel['timestamp'] | |||
timestamp: Timestamp |
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.
Yep. Weβve seen this before.
@@ -42,7 +42,7 @@ export class EventEntity extends IdentityColumn(RecordedColumn(class {})) implem | |||
provider_id: EventEntityModel['provider_id'] | |||
|
|||
@Column('bigint', { transformer: BigintTransformer, primary: true }) | |||
timestamp: EventEntityModel['timestamp'] | |||
timestamp: Timestamp |
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.
We've recently stopped defining entity properties in terms of Entity or (especially) Domain model properties and use the base types instead. Some libraries that use metadata reflection have issues tracing the aliases to the base type. In recent repositories, you will see a pattern something like:
export class SomeEntity {
@Column(...)
entity_id: UUID
@Column(...)
timestamp: Timestamp
}
/* Create an alias to keep consistent with other services */
export SomeEntityModel = SomeEntity
const entities = await query.limit(limit).getMany() | ||
// Use query instead of paginator to manage order if using a joined field | ||
if (order && order.column === 'vehicle_id') { | ||
query.orderBy('devices.vehicle_id', order.direction) |
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.
I'm unsure how the query.orderBy
and paginationKeys
interact, but I assume the pagination keys will get appended to the orderBy. Is timestamp
always the secondary sort column?
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.
Good call out, I took a look at this and it turns out that paginationKeys
always override anything added to the query with orderBy
or addOrderBy
. If you remove paginationKeys
param entirely, it just uses id
(the default).
There seems to be an open issue for this (can't sort on relational tables / override paginationKeys
) - and the maintainer has ack'd it recently.
benjamin658/typeorm-cursor-pagination#4
So for now, removed the ability to sort by vehicle_id
. Will follow up with the open issue. Not worth sinking a bunch of time for this one sort-able field atm.
That being said, sorting on joined tables will most likely come up in the future at some point - so hopefully this gets addressed in typeorm-cursor-pagination shortly.
PS - updated tests since apparently they were false positives for sorting on vehicle_id.
π Purpose
This PR adds pagination and order support for the existing
getEvents()
service method inmds-ingest-service
.π Resolves:
π¦ Impacts:
β PR Checklist