Skip to content

Conversation

knudtty
Copy link
Contributor

@knudtty knudtty commented Sep 22, 2025

Closes HDX-2468

Copy link

changeset-bot bot commented Sep 22, 2025

⚠️ No Changeset found

Latest commit: 2108f6c

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link

vercel bot commented Sep 22, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
hyperdx-v2-oss-app Ready Ready Preview Comment Sep 23, 2025 4:02pm

Copy link
Contributor

github-actions bot commented Sep 22, 2025

E2E Test Results

All tests passed • 21 passed • 3 skipped • 151s

Status Count
✅ Passed 21
❌ Failed 0
⚠️ Flaky 0
⏭️ Skipped 3

View full report →

Comment on lines 19 to 29
export function createSource(team: string, source: Omit<ISource, 'id'>) {
return Source.create({ ...source, team });
switch (source.kind) {
case SourceKind.Log:
return LogSource.create({ ...source, team });
case SourceKind.Trace:
return TraceSource.create({ ...source, team });
case SourceKind.Metric:
return MetricSource.create({ ...source, team });
case SourceKind.Session:
return SessionSource.create({ ...source, team });
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mongoose will strip out any unnecessary fields, so create source must use the new broken out discriminated models

Comment on lines 37 to 54
switch (source.kind) {
case SourceKind.Log:
return LogSource.findOneAndUpdate({ _id: sourceId, team }, source, {
new: true,
});
case SourceKind.Trace:
return TraceSource.findOneAndUpdate({ _id: sourceId, team }, source, {
new: true,
});
case SourceKind.Metric:
return MetricSource.findOneAndUpdate({ _id: sourceId, team }, source, {
new: true,
});
case SourceKind.Session:
return SessionSource.findOneAndUpdate({ _id: sourceId, team }, source, {
new: true,
});
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, using discriminated models

durationPrecision: Number,
parentSpanIdExpression: String,
spanNameExpression: String,
export const LogSource = Source.discriminator<
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Broke out the mongoose models into the base model (Source) and a discriminated model for each type

@@ -1,10 +1,10 @@
import { SourceKind, TSourceUnion } from '@hyperdx/common-utils/dist/types';
import { SourceKind, TSource } from '@hyperdx/common-utils/dist/types';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TSourceUnion is renamed TSource

Comment on lines +127 to +135
export function useSource(params: {
id?: string | null;
connection?: string | null;
}): UseQueryResult<TSource | undefined>;
export function useSource<K extends TSource['kind']>(params: {
id?: string | null;
connection?: string | null;
kind: K;
}): UseQueryResult<Extract<TSource, { kind: K }> | undefined>;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type signature without specifying kind returns the TSource type, but if you specify kind then the type signature understands the returned source is a particular kind. Super useful if you always know you'll be using a specific kind of source

Comment on lines -164 to +219
mutationFn: async ({ source }: { source: Omit<TSource, 'id'> }) => {
mutationFn: async ({ source }: { source: TSourceNoId }) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TS's built in Omit does not work well with discriminated unions, so it's better to construct with zod and type assert.

@wrn14897
Copy link
Member

wrn14897 commented Sep 23, 2025

The refactoring makes me a bit nervous, as the changes affect the core of the entire app and I’m not very confident in our test coverage in this area. To minimize the risk of breaking existing behavior, I’d suggest migrating to the new sources model gradually, one source type at a time. If it’s not that easy, I’d recommend adding more unit tests for different sources.

Overall, I really like these changes, which will help us catch more source type–related bugs :)

Comment on lines +22 to +28
return LogSource.create({ ...source, team });
case SourceKind.Trace:
return TraceSource.create({ ...source, team });
case SourceKind.Metric:
return MetricSource.create({ ...source, team });
case SourceKind.Session:
return SessionSource.create({ ...source, team });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should also handle default case

Comment on lines +125 to 132
export const SessionSource = Source.discriminator<
Extract<TSource, { kind: SourceKind.Session }>
>(
SourceKind.Session,
new mongoose.Schema<Extract<TSource, { kind: SourceKind.Session }>>({
traceSourceId: String,
}),
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will those non-public fields (e.g., ResourceAttributes) be inserted or updated?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's talk about the requirements for Sessions, because ResourceAttributes isn't currently listed in the schema

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm you are right. Sorry, I was thinking about the different thing. The mapping should only require a few fields

Comment on lines +31 to +38
case SourceKind.Log:
return s.toJSON({ getters: true });
case SourceKind.Trace:
return s.toJSON({ getters: true });
case SourceKind.Metric:
return s.toJSON({ getters: true });
case SourceKind.Session:
return s.toJSON({ getters: true });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this can be

 case SourceKind.Log:
 case SourceKind.Trace:
 case SourceKind.Metric:
 case SourceKind.Session:
     return s.toJSON({ getters: true });

also we need to handle default case

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason Typescript gets confused about the union so that does not work, it has to be broken out like this. I'll add an error for the default case

Comment on lines +488 to 495
const { data: logSource, isLoading: isLogSourceLoading } = useSource({
id: source,
kind: SourceKind.Log,
});
const { data: traceSource, isLoading: isTraceSourceLoading } = useSource({
id: source,
kind: SourceKind.Trace,
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any chance we could avoid duplicate requests here? I see the point, but the source ID should already give us the right source

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Under the hood they all are using the same query key to fetch all the sources and then just running find over the elements of the array. They are using the same query key, so only one request should be generated

@knudtty
Copy link
Contributor Author

knudtty commented Sep 23, 2025

To minimize the risk of breaking existing behavior, I’d suggest migrating to the new sources model gradually, one source type at a time. If it’s not that easy, I’d recommend adding more unit tests for different sources.

Unfortunately it is not easy. More unit tests makes sense, I'll seek guidance for which ones specifically we are interested in

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants