Skip to content
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

feat: add relationships resource api #423

Merged
merged 12 commits into from
Sep 28, 2020
2 changes: 1 addition & 1 deletion packages/beta/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"esCheck": "es-check es5 './dist/index.js'",
"docs": "typedoc --options typedoc.js --tsconfig tsconfig.json src/index.ts",
"docs:bundle": "yarn docs && mkdir -p ../../docs/beta && cp -r docs/* ../../docs/beta/",
"extract-snippets": "rm -rf codeSnippets/ && yarn docs --json codeSnippets/docs.json && node ../../scripts/extractCodeSnippets.js",
"extract-snippets": "rm -rf codeSnippets/ && yarn docs --json codeSnippets/docs.json && node ../../scripts/extractCodeSnippets.js --beta",
polomani marked this conversation as resolved.
Show resolved Hide resolved
"test-snippets": "yarn extract-snippets && yarn tsc -p codeSnippets/tsconfig.build.json"
},
"dependencies": {
Expand Down
130 changes: 130 additions & 0 deletions packages/beta/src/__tests__/api/relationships.int.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright 2020 Cognite AS

import { Asset, CogniteEvent } from '@cognite/sdk';
import { randomInt } from '@cognite/sdk-core/src/testUtils';
import CogniteClient from '../../cogniteClient';
import { setupLoggedInClient } from '../testUtils';

describe('relationships integration test', () => {
const assetName = 'relationship_test_asset' + randomInt();
const eventName = 'relationship_test_event' + randomInt();
const relationshipId = 'test_relationship' + randomInt();
const confidenceExternalId = 'relationship_test_confidence' + randomInt();
const relationshipConf = {
externalId: relationshipId,
sourceExternalId: assetName,
sourceType: 'asset' as const,
targetExternalId: eventName,
targetType: 'event' as const,
};

let client: CogniteClient;
let asset: Asset;
let event: CogniteEvent;

beforeEach(async () => {
await client.relationships.delete([{ externalId: confidenceExternalId }], {
ignoreUnknownIds: true,
});
});

beforeAll(async () => {
client = setupLoggedInClient();
[asset] = await client.assets.create([
{ externalId: assetName, name: assetName },
]);
[event] = await client.events.create([{ externalId: eventName }]);
});

afterAll(async () => {
await client.assets.delete([{ externalId: assetName }]);
await client.events.delete([{ externalId: eventName }]);
});

describe('create', () => {
mrtraser marked this conversation as resolved.
Show resolved Hide resolved
test('should create relationship', async () => {
const [relationship] = await client.relationships.create([
relationshipConf,
]);

expect(relationship.externalId).toEqual(relationshipId);
expect(relationship.sourceExternalId).toEqual(asset.externalId);
expect(relationship.targetExternalId).toEqual(event.externalId);
});
test('should throw error if confidence param is out of 0..1 range', async () => {
const createPromise = client.relationships.create([
{ ...relationshipConf, confidence: 5 },
]);
await expect(createPromise).rejects.toThrow();
});
test('should retrieve just created relationship', async () => {
const [relationship] = await client.relationships.create([
{
...relationshipConf,
confidence: 1.0,
externalId: confidenceExternalId,
},
]);

const [retrieved] = await client.relationships.retrieve([
{ externalId: confidenceExternalId },
]);

expect(retrieved).toEqual(relationship);
});
test('should list just created relationship', async () => {
await client.relationships.create([
{
...relationshipConf,
confidence: 1.0,
externalId: confidenceExternalId,
},
]);
const result = await client.relationships
.list({ filter: { sourceExternalIds: [assetName] } })
.autoPagingToArray();

expect(result.length).toBe(2);
});
});

describe('retrieve', () => {
test('should retrieve relationship', async () => {
const result = await client.relationships.retrieve([
{ externalId: relationshipId },
]);

expect(result.length).toBe(1);
expect(result[0].externalId).toBe(relationshipId);
});
test('should ignore unknown ids if provided', async () => {
const result = await client.relationships.retrieve(
[{ externalId: relationshipId }, { externalId: 'unknown_external_id' }],
{ ignoreUnknownIds: true }
);

expect(result.length).toBe(1);
});
});

describe('filter', () => {
test('should filter relationship', async () => {
const result = await client.relationships
.list({ filter: { sourceExternalIds: [assetName] } })
.autoPagingToArray({ limit: 5 });

expect(result.length).toBeGreaterThan(0);
expect(result[0].sourceExternalId).toBe(assetName);
});
});

describe('delete', () => {
test('should delete relationship', async () => {
const result = await client.relationships.delete([
{ externalId: relationshipId },
]);

expect(result).toEqual({});
});
});
});
78 changes: 78 additions & 0 deletions packages/beta/src/api/relationships/relationshipsApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright 2020 Cognite AS

import {
BaseResourceAPI,
CursorAndAsyncIterator,
ExternalId,
} from '@cognite/sdk-core';
import {
IgnoreUnknownIds,
Relationship,
RelationshipsFilterRequest,
} from '../../types';

export class RelationshipsApi extends BaseResourceAPI<Relationship> {
/**
* [Create relationship](https://docs.cognite.com/api/v1/#operation/createRelationships)
*
* ```js
* const relationships = [
* {
* externalId: 'some_relationship',
* sourceExternalId: 'some_source_external_id',
* sourceType: 'asset' as const,
* targetExternalId: 'some_target_external_id',
* targetType: 'event' as const
* }
* ];
* const createdRelationships = await client.relationships.create(relationships);
* ```
*/
public create = (items: Relationship[]): Promise<Relationship[]> => {
return this.createEndpoint(items);
};

/**
* [List relationships](https://docs.cognite.com/api/v1/#operation/listRelationships)
*
* ```js
* const relationships = await client.relationships.list({ filter: { createdTime: { min: new Date('1 jan 2018'), max: new Date('1 jan 2019') }}});
* ```
*/
public list = (
query?: RelationshipsFilterRequest
): CursorAndAsyncIterator<Relationship> => {
return super.listEndpoint(this.callListEndpointWithPost, query);
};

/**
* [Retrieve relationships](https://docs.cognite.com/api/v1/#operation/retrieveRelationships)
*
* ```js
* const relationships = await client.relationships.retrieve([{externalId: 'abc'}, {externalId: 'def'}]);
* ```
*/
public retrieve = (
ids: ExternalId[],
params: RelationshipsRetrieveParams = {}
): Promise<Relationship[]> => {
return super.retrieveEndpoint(ids, params);
};

/**
* [Delete relationships](https://doc.cognitedata.com/api/v1/#operation/deleteRelationships)
*
* ```js
* await client.relationships.delete([{externalId: 'abc'}, {externalId: 'def'}]);
* ```
*/
public delete = (
ids: ExternalId[],
params: RelationshipsDeleteParams = {}
) => {
return super.deleteEndpoint(ids, params);
};
}

export type RelationshipsRetrieveParams = IgnoreUnknownIds;
export type RelationshipsDeleteParams = IgnoreUnknownIds;
16 changes: 16 additions & 0 deletions packages/beta/src/cogniteClient.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
// Copyright 2020 Cognite AS

import {
ClientOptions,
CogniteClient as CogniteClientStable,
} from '@cognite/sdk';
import { accessApi } from '@cognite/sdk-core';
import { version } from '../package.json';
import { RelationshipsApi } from './api/relationships/relationshipsApi';

class CogniteClientCleaned extends CogniteClientStable {
// Remove type restrictions
}

export default class CogniteClient extends CogniteClientCleaned {
private relationshipsApi?: RelationshipsApi;

/**
* Create a new SDK client (beta)
*
Expand All @@ -29,9 +34,20 @@ export default class CogniteClient extends CogniteClientCleaned {
*/
constructor(options: ClientOptions) {
super(options);
this.httpClient.setDefaultHeader('version', 'beta');
}

public get relationships() {
return accessApi(this.relationshipsApi);
}

protected get version() {
return `${version}-beta`;
}

protected initAPIs() {
super.initAPIs();

this.relationshipsApi = this.apiFactory(RelationshipsApi, 'relationships');
}
}
2 changes: 2 additions & 0 deletions packages/beta/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@

export * from '@cognite/sdk';
export { default as CogniteClient } from './cogniteClient';
export * from './api/relationships/relationshipsApi';
export * from './types';
50 changes: 50 additions & 0 deletions packages/beta/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,55 @@
// Copyright 2020 Cognite AS

import {
CogniteExternalId,
CogniteInternalId,
CreatedAndLastUpdatedTimeFilter,
DateRange,
FilterQuery,
IdEither,
Label,
Timestamp,
Range,
LabelFilter,
} from '@cognite/sdk';

export * from '@cognite/sdk';
// This file is here mostly to allow apis to import { ... } from '../../types';
// Overriding types should probably be done in their respective API endpoint files, where possible

export type RelationshipResourceType =
| 'asset'
| 'timeSeries'
| 'file'
| 'event'
| 'sequence';

export interface Relationship {
externalId: CogniteExternalId;
sourceExternalId: CogniteExternalId;
sourceType: RelationshipResourceType;
targetExternalId: CogniteExternalId;
targetType: RelationshipResourceType;
startTime?: Timestamp;
endTime?: Timestamp;
confidence?: number;
dataSetId?: CogniteInternalId;
labels?: Label[];
}

export interface RelationshipsFilterRequest extends FilterQuery {
filter: RelationshipsFilter;
mrtraser marked this conversation as resolved.
Show resolved Hide resolved
}

export interface RelationshipsFilter extends CreatedAndLastUpdatedTimeFilter {
sourceExternalIds?: CogniteExternalId[];
sourceTypes?: RelationshipResourceType[];
targetExternalIds?: CogniteExternalId[];
targetTypes?: RelationshipResourceType[];
dataSetIds?: IdEither[];
startTime?: DateRange;
endTime?: DateRange;
confidence?: Range<number>;
activeAtTime?: DateRange;
labels?: LabelFilter;
}
5 changes: 3 additions & 2 deletions packages/stable/src/__tests__/api/labels.int.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { ExternalLabelDefinition } from '@cognite/sdk';
import { randomInt } from '@cognite/sdk-core/src/testUtils';
import CogniteClient from '../../cogniteClient';
import { setupLoggedInClient } from '../testUtils';
mrtraser marked this conversation as resolved.
Show resolved Hide resolved

describe('Labels integration test', () => {
let client: CogniteClient;
const externalLabel: ExternalLabelDefinition = {
externalId: 'ROTATING_EQUIPMENT',
name: 'Pump',
externalId: 'ROTATING_EQUIPMENT' + randomInt(),
name: 'Pump' + randomInt(),
description: 'Asset with rotating parts',
};

Expand Down
3 changes: 2 additions & 1 deletion scripts/extractCodeSnippets.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const snippetsFolder = path.join(process.cwd(), './codeSnippets');
const jsDoc = require(snippetsFolder + '/docs.json');
const _ = require('lodash');
const fs = require('fs');
const isBeta = !!~process.argv.indexOf('--beta');
mrtraser marked this conversation as resolved.
Show resolved Hide resolved

function stripMarkdownCodeSnippet(rawCode) {
return rawCode
Expand Down Expand Up @@ -62,7 +63,7 @@ codeSnippets.forEach((snippets, operationId) => {
return;
}
const codeToTest = `
import { CogniteClient, SequenceValueType } from '@cognite/sdk';
import { CogniteClient, SequenceValueType } from '@cognite/sdk${ isBeta ? '-beta' : '' }';
const client = new CogniteClient({ appId: '[APP NAME]' });
client.loginWithApiKey({
project: '[PROJECT]',
Expand Down