Skip to content

Commit

Permalink
FAI-6093: Handle different timestamp types in adapter (#137)
Browse files Browse the repository at this point in the history
  • Loading branch information
eskrm committed May 2, 2023
1 parent 321b531 commit a4ec178
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 43 deletions.
17 changes: 10 additions & 7 deletions src/adapter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,11 @@ export function getFieldPaths(
} else if (isLeafType(type)) {
const fieldPath = fieldStack.join('.');
let newFieldPath = newFieldStack.join('.');
let fieldType = asLeafValueType(type);
// Timestamps in v1 are always stringified epoch millis, except when
// they're inside embedded object lists. In that case, they're stored
// as epoch millis.
const stringifyTimestamps = !mirrorPaths;
let fieldType = asLeafValueType(type, stringifyTimestamps);
if (mirrorPaths) {
fieldPaths[fieldPath] = {path: fieldPath, type: fieldType};
return undefined;
Expand All @@ -172,7 +176,7 @@ export function getFieldPaths(
// In V2, it's stored and typed like every other timestamp.
// We force conversion from ISO 8601 string => epoch millis
// string by overriding the type from string to timestamp.
fieldType = 'timestamp';
fieldType = 'epoch_millis_string';
}
} else {
// Prefix the last field name with the embedded field name
Expand Down Expand Up @@ -346,16 +350,15 @@ export class QueryAdapter {
return `${v2Value}`;
}
throw new VError('invalid long: %s', v2Value);
} else if (type === 'timestamp') {
// A timestamp will be an ISO string in v2, unless it's inside a list of
// embedded objects, in which case it will be an epoch millis number
} else if (type === 'epoch_millis' || type === 'epoch_millis_string') {
const stringify = type === 'epoch_millis_string';
if (_.isString(v2Value)) {
const millis = DateTime.fromISO(v2Value).toMillis();
if (!isNaN(millis)) {
return `${millis}`;
return stringify ? `${millis}` : millis;
}
} else if (_.isNumber(v2Value)) {
return v2Value;
return stringify ? `${v2Value}` : v2Value;
}
throw new VError('invalid timestamp: %s', v2Value);
}
Expand Down
13 changes: 10 additions & 3 deletions src/adapter/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ const primitiveTypes = [
'int',
'long',
'string',
'timestamp',
'epoch_millis',
'epoch_millis_string',
] as const;
type PrimitiveType = typeof primitiveTypes[number];

Expand Down Expand Up @@ -68,7 +69,10 @@ export interface FieldPaths {
readonly [path: string | symbol]: PathValue;
}

export function asLeafValueType(type: gql.GraphQLType): LeafValueType {
export function asLeafValueType(
type: gql.GraphQLType,
stringifyTimestamps = true
): LeafValueType {
function asPrimitiveType(type: gql.GraphQLLeafType): PrimitiveType {
if (gql.isEnumType(type) || type.name === 'String' || type.name === 'ID') {
return 'string';
Expand All @@ -83,7 +87,10 @@ export function asLeafValueType(type: gql.GraphQLType): LeafValueType {
} else if (type.name === 'Long') {
return 'long';
} else if (type.name === 'Timestamp') {
return 'timestamp';
if (stringifyTimestamps) {
return 'epoch_millis_string';
}
return 'epoch_millis';
}
throw new VError('unknown GraphQL leaf type: %s', type);
}
Expand Down
69 changes: 36 additions & 33 deletions test/adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ describe('AST utilities', () => {
},
'deployment.refreshedAt': {
path: 'deployment.metadata.refreshedAt',
type: 'timestamp'
type: 'epoch_millis_string'
},
'commit.sha': {
path: 'commit.sha',
Expand All @@ -204,7 +204,7 @@ describe('AST utilities', () => {
},
'commit.refreshedAt': {
path: 'commit.metadata.refreshedAt',
type: 'timestamp'
type: 'epoch_millis_string'
}
}
}
Expand Down Expand Up @@ -374,7 +374,7 @@ describe('AST utilities', () => {
nestedPaths: {
changedAt: {
path: 'changedAt',
type: 'timestamp'
type: 'epoch_millis'
},
'status.category': {
path: 'status.category',
Expand Down Expand Up @@ -548,90 +548,93 @@ describe('AST utilities', () => {
nestedPaths: {
uid: {path: 'uid', type: 'string'},
description: {path: 'description', type: 'string'},
createdAt: {path: 'createdAt', type: 'timestamp'},
createdAt: {path: 'createdAt', type: 'epoch_millis_string'},
origin: {path: 'metadata.origin', type: 'string'},
refreshedAt: {path: 'metadata.refreshedAt', type: 'timestamp'},
refreshedAt: {
path: 'metadata.refreshedAt',
type: 'epoch_millis_string',
},
releases: {
path: 'releases.nodes',
nestedPaths: {
'release.uid': {
path: 'release.uid',
type: 'string'
type: 'string',
},
'release.releasedAt': {
path: 'release.releasedAt',
type: 'timestamp'
type: 'epoch_millis_string',
},
'release.origin': {
path: 'release.metadata.origin',
type: 'string'
type: 'string',
},
'release.refreshedAt': {
path: 'release.metadata.refreshedAt',
type: 'timestamp'
}
}
type: 'epoch_millis_string',
},
},
},
tasks: {
path: 'tasks.nodes',
nestedPaths: {
'task.uid': {
path: 'task.uid',
type: 'string'
type: 'string',
},
'task.typeCategory': {
path: 'task.type.category',
type: 'string'
type: 'string',
},
'task.typeDetail': {
path: 'task.type.detail',
type: 'string'
type: 'string',
},
'task.origin': {
path: 'task.metadata.origin',
type: 'string'
type: 'string',
},
'task.refreshedAt': {
path: 'task.metadata.refreshedAt',
type: 'timestamp'
type: 'epoch_millis_string',
},
'task.parent.uid': {
path: 'task.parent.uid',
type: 'string'
type: 'string',
},
'task.parent.createdAt': {
path: 'task.parent.createdAt',
type: 'timestamp'
type: 'epoch_millis_string',
},
'task.additionalFields': {
path: 'task.additionalFields',
nestedPaths: {
name: {path: 'name', type: 'string'},
value: {path: 'value', type: 'string'}
}
value: {path: 'value', type: 'string'},
},
},
'task.statusChangelog': {
path: 'task.statusChangelog',
nestedPaths: {
changedAt: {
path: 'changedAt',
type: 'timestamp'
type: 'epoch_millis',
},
'status.category': {
path: 'status.category',
type: 'string'
type: 'string',
},
'status.detail': {
path: 'status.detail',
type: 'string'
}
}
}
}
}
}
}
}
type: 'string',
},
},
},
},
},
},
},
},
});
});
});
Expand Down Expand Up @@ -757,7 +760,7 @@ describe('query adapter', () => {
status: {category: 'c1a', detail: 'd1a'}
},
{
changedAt: 1667871145261,
changedAt: '2022-11-08T01:32:25.261Z',
status: {category: 'c1b', detail: 'd1b'}
}
]
Expand All @@ -774,7 +777,7 @@ describe('query adapter', () => {
status: {category: 'c2a', detail: 'd2a'}
},
{
changedAt: 1667871145261,
changedAt: '2022-11-08T01:32:25.261Z',
status: {category: 'c2b', detail: 'd2b'}
}
]
Expand Down

0 comments on commit a4ec178

Please sign in to comment.