Skip to content

Commit

Permalink
fix: add instance id support to timeseries API (#1165)
Browse files Browse the repository at this point in the history
fix: add instance id support to timeseries API

* Add support to query for timeseries by instance ID on relevant endpoints
* Add instance ID to response type for timeseries and timeseries datapoints
* Add instance ID to core SDK type
  • Loading branch information
roligheten authored Oct 24, 2024
1 parent 210ca61 commit 070dbb3
Show file tree
Hide file tree
Showing 7 changed files with 236 additions and 10 deletions.
1 change: 1 addition & 0 deletions packages/beta/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export {
DatapointsMultiQueryBase,
DatapointsQuery,
DatapointsQueryExternalId,
DatapointsQueryInstanceId,
DatapointsQueryId,
DatapointsQueryProperties,
DoubleDatapoint,
Expand Down
10 changes: 9 additions & 1 deletion packages/beta/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import type {
CogniteInternalId,
ExternalId,
FilterQuery,
InstanceId,
InternalId,
} from '@cognite/sdk-core';

Expand Down Expand Up @@ -373,7 +374,10 @@ export interface DatapointsMultiQueryBase
aggregates?: Aggregate[];
}

export type DatapointsQuery = DatapointsQueryId | DatapointsQueryExternalId;
export type DatapointsQuery =
| DatapointsQueryId
| DatapointsQueryExternalId
| DatapointsQueryInstanceId;

export interface DatapointsQueryExternalId
extends DatapointsQueryProperties,
Expand All @@ -383,6 +387,10 @@ export interface DatapointsQueryId
extends DatapointsQueryProperties,
InternalId {}

export interface DatapointsQueryInstanceId
extends DatapointsQueryProperties,
InstanceId {}

export type Aggregate =
| AggregateStable
| 'countGood'
Expand Down
27 changes: 27 additions & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,33 @@ export interface ExternalId {
externalId: CogniteExternalId;
}

export type IdEitherWithInstance = InternalId | ExternalId | InstanceId;

export interface InstanceId {
instanceId: CogniteInstanceId;
}

/**
* Unique identifier of an instance in Cognite Data Modeling
*/
export interface CogniteInstanceId {
externalId: InstanceExternalId;
space: InstanceSpace;
}

/**
* External id of an instance in Cognite Data Modeling
*
* @pattern ^[a-zA-Z]([a-zA-Z0-9_]{0,253}[a-zA-Z0-9])?$
*/
export type InstanceExternalId = string;
/**
* Instance space of an instance in Cognite Data Modeling
*
* @pattern ^[a-zA-Z][a-zA-Z0-9_-]{0,41}[a-zA-Z0-9]?$
*/
export type InstanceSpace = string;

/**
* External Id provided by client. Should be unique within the project.
*/
Expand Down
87 changes: 85 additions & 2 deletions packages/stable/src/__tests__/api/datapoints.int.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,43 @@

import { afterAll, beforeAll, describe, expect, test } from 'vitest';
import type CogniteClient from '../../cogniteClient';
import type { DatapointAggregate, Timeseries } from '../../types';
import { setupLoggedInClient } from '../testUtils';
import type { DatapointAggregate, NodeWrite, Timeseries } from '../../types';
import { randomInt, setupLoggedInClient } from '../testUtils';

describe('Datapoints integration test', () => {
let client: CogniteClient;
let timeserie: Timeseries;

const testSpace = {
space: 'test_data_space',
name: 'test_data_space',
description: 'Instance space used for integration tests.',
};

const timeseriesCdmInstance: NodeWrite = {
externalId: `external_${randomInt()}`,
space: testSpace.space,
instanceType: 'node',
sources: [
{
source: {
externalId: 'CogniteTimeSeries',
space: 'cdf_cdm',
type: 'view',
version: 'v1',
},
properties: {
type: 'numeric',
},
},
],
};

const timeseriesCdmInstanceId = {
externalId: timeseriesCdmInstance.externalId,
space: timeseriesCdmInstance.space,
};

const timestampFrom = Date.now() - 60 * 60 * 48 * 1000;
const timestampTill = Date.now() - 60 * 60 * 24 * 1000;
const datapoints = [
Expand All @@ -25,9 +55,20 @@ describe('Datapoints integration test', () => {
beforeAll(async () => {
client = setupLoggedInClient();
[timeserie] = await client.timeseries.create([{ name: 'tmp' }]);
await client.spaces.upsert([testSpace]);
await client.instances.upsert({
items: [timeseriesCdmInstance],
});
});
afterAll(async () => {
await client.timeseries.delete([{ id: timeserie.id }]);
await client.instances.delete([
{
instanceType: 'node',
externalId: timeseriesCdmInstance.externalId,
space: timeseriesCdmInstance.space,
},
]);
});

test('insert', async () => {
Expand All @@ -39,6 +80,16 @@ describe('Datapoints integration test', () => {
]);
});

test('insert by instance id', async () => {
const res = await client.datapoints.insert([
{
instanceId: timeseriesCdmInstanceId,
datapoints,
},
]);
expect(res).toEqual({});
});

test('retrieve', async () => {
const response = await client.datapoints.retrieve({
items: [{ id: timeserie.id }],
Expand All @@ -50,6 +101,17 @@ describe('Datapoints integration test', () => {
expect(response[0].isString).toBe(false);
});

test('retrieve by instance id', async () => {
const response = await client.datapoints.retrieve({
items: [{ instanceId: timeseriesCdmInstanceId }],
start: '2d-ago',
end: new Date(),
});
expect(response[0].datapoints.length).toBeGreaterThan(0);
expect(response[0].datapoints[0].timestamp).toBeInstanceOf(Date);
expect(response[0].isString).toBe(false);
});

test('retrieve latest', async () => {
const response = await client.datapoints.retrieveLatest([
{
Expand All @@ -61,6 +123,17 @@ describe('Datapoints integration test', () => {
expect(response[0].datapoints[0].timestamp).toBeInstanceOf(Date);
});

test('retrieve latest by instance id', async () => {
const response = await client.datapoints.retrieveLatest([
{
before: '1d-ago',
instanceId: timeseriesCdmInstanceId,
},
]);
expect(response[0].datapoints.length).toBeGreaterThan(0);
expect(response[0].datapoints[0].timestamp).toBeInstanceOf(Date);
});

test('retrieve with cursor', async () => {
const queryTimeRange = {
start: '3d-ago',
Expand Down Expand Up @@ -94,6 +167,16 @@ describe('Datapoints integration test', () => {
expect(result.length).toBe(1);
expect(result[0].datapoints[0].timestamp).toBeInstanceOf(Date);
});

test('delete by instance id', async () => {
const res = await client.datapoints.delete([
{
instanceId: timeseriesCdmInstanceId,
inclusiveBegin: 0,
},
]);
expect(res).toEqual({});
});
});

describe.skip('Datapoints integration test for monthly granularity', () => {
Expand Down
68 changes: 67 additions & 1 deletion packages/stable/src/__tests__/api/timeseries.int.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { afterAll, beforeAll, describe, expect, test } from 'vitest';
import type CogniteClient from '../../cogniteClient';
import type { Asset, Timeseries } from '../../types';
import type { Asset, NodeWrite, Timeseries } from '../../types';
import {
randomInt,
runTestWithRetryWhenFailing,
Expand All @@ -20,10 +20,21 @@ describe('Timeseries integration test', () => {
externalId: `external_${randomInt()}`,
},
]);
await client.spaces.upsert([testSpace]);
await client.instances.upsert({
items: [timeseriesCdmInstance],
});
});

afterAll(async () => {
await client.assets.delete([{ id: asset.id }]);
await client.instances.delete([
{
instanceType: 'node',
externalId: timeseriesCdmInstance.externalId,
space: timeseriesCdmInstance.space,
},
]);
});

const timeseries = [
Expand Down Expand Up @@ -91,6 +102,36 @@ describe('Timeseries integration test', () => {
},
];

const testSpace = {
space: 'test_data_space',
name: 'test_data_space',
description: 'Instance space used for integration tests.',
};

const timeseriesCdmInstance: NodeWrite = {
externalId: `external_${randomInt()}`,
space: testSpace.space,
instanceType: 'node',
sources: [
{
source: {
externalId: 'CogniteTimeSeries',
space: 'cdf_cdm',
type: 'view',
version: 'v1',
},
properties: {
type: 'numeric',
},
},
],
};

const timeseriesCdmInstanceId = {
externalId: timeseriesCdmInstance.externalId,
space: timeseriesCdmInstance.space,
};

let createdTimeseries: Timeseries[];

test('create', async () => {
Expand All @@ -107,6 +148,15 @@ describe('Timeseries integration test', () => {
expect(single.name).toBe(timeseries[0].name);
});

test('retrieve by instance id', async () => {
const [single] = await client.timeseries.retrieve([
{ instanceId: timeseriesCdmInstanceId },
]);
expect(single.instanceId?.externalId).toBe(
timeseriesCdmInstance.externalId
);
});

test('retrieve with non-existent external id', async () => {
const res = await client.timeseries.retrieve([{ externalId: '_n/a_' }], {
ignoreUnknownIds: true,
Expand All @@ -128,6 +178,22 @@ describe('Timeseries integration test', () => {
expect(updateResult[0].name).toBe(newName);
});

test('update by instance id', async () => {
const testMetadata = { testKey: 'testValue' };
const updateResult = await client.timeseries.update([
{
instanceId: timeseriesCdmInstanceId,
update: {
metadata: {
set: testMetadata,
},
},
},
]);
expect(updateResult[0].instanceId).toEqual(timeseriesCdmInstanceId);
expect(updateResult[0].metadata).toEqual(testMetadata);
});

test('connect to an asset', async () => {
await client.timeseries.update([
{
Expand Down
3 changes: 2 additions & 1 deletion packages/stable/src/api/timeSeries/timeSeriesApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
BaseResourceAPI,
type CDFHttpClient,
type CursorAndAsyncIterator,
type IdEitherWithInstance,
type MetadataMap,
} from '@cognite/sdk-core';
import type {
Expand Down Expand Up @@ -92,7 +93,7 @@ export class TimeSeriesAPI extends BaseResourceAPI<Timeseries> {
* ```
*/
public retrieve = (
ids: IdEither[],
ids: IdEitherWithInstance[],
params: TimeseriesRetrieveParams = {}
) => {
return super.retrieveEndpoint(ids, params);
Expand Down
Loading

0 comments on commit 070dbb3

Please sign in to comment.