diff --git a/src/__tests__/resources/assetList.integration.specs.ts b/src/__tests__/resources/assetList.integration.specs.ts index 32eaf0a6b0..9d2536050b 100644 --- a/src/__tests__/resources/assetList.integration.specs.ts +++ b/src/__tests__/resources/assetList.integration.specs.ts @@ -41,10 +41,4 @@ describe('assetList', () => { await client.assets.delete(createdAssets.map(asset => ({ id: asset.id }))); await client.events.delete(createdEvents.map(event => ({ id: event.id }))); }); - - test('json stringify', async () => { - const response = await client.assets.list({ limit: 2 }); - const stringRes = JSON.stringify(response.items); - expect(typeof stringRes).toBe('string'); - }); }); diff --git a/src/__tests__/resources/assets.integration.spec.ts b/src/__tests__/resources/assets.integration.spec.ts index f402975eae..9f84cae64e 100644 --- a/src/__tests__/resources/assets.integration.spec.ts +++ b/src/__tests__/resources/assets.integration.spec.ts @@ -199,7 +199,7 @@ describe('Asset integration test', () => { const root2 = { name: 'root-2', externalId: 'root-2' + randomInt() }; const child1 = { name: 'child-1', parentExternalId: root1.externalId }; const child2 = { name: 'child-2', parentExternalId: root2.externalId }; - const [createdChild1] = await client.assets.create([ + const [createdChild1, createdRoot1] = await client.assets.create([ child1, root1, root2, @@ -217,7 +217,7 @@ describe('Asset integration test', () => { await runTestWithRetryWhenFailing(async () => { const nonRootAssetsUnderRootId = await client.assets .list({ - filter: { root: false, rootIds: [{ externalId: root1.externalId }] }, + filter: { root: false, rootIds: [{ id: createdRoot1.id }] }, limit: 2, }) .autoPagingToArray({ limit: 2 }); diff --git a/src/__tests__/resources/assets.unit.spec.ts b/src/__tests__/resources/assets.unit.spec.ts index 0c956d1483..b42db8db9c 100644 --- a/src/__tests__/resources/assets.unit.spec.ts +++ b/src/__tests__/resources/assets.unit.spec.ts @@ -1,6 +1,7 @@ // Copyright 2019 Cognite AS import * as nock from 'nock'; +import { ExternalAssetItem } from '../../'; import CogniteClient from '../../cogniteClient'; import { Node } from '../../graphUtils'; import { @@ -8,10 +9,10 @@ import { promiseAllAtOnce, promiseEachInSequence, } from '../../resources/assets/assetUtils'; -import { ExternalAssetItem } from '../../types/types'; import { mockBaseUrl, setupMockableClient } from '../testUtils'; -describe('Asset unit test', () => { +// tslint:disable-next-line:no-big-function +describe('Assets unit test', () => { let client: CogniteClient; beforeEach(() => { client = setupMockableClient(); @@ -198,4 +199,44 @@ describe('Asset unit test', () => { }); }); }); + + describe('class is not polluted with enumerable props', async () => { + const items = [ + { + id: 1, + }, + { + id: 2, + }, + ]; + + beforeEach(() => { + nock.cleanAll(); + nock(mockBaseUrl) + .post(new RegExp('/assets/list')) + .thrice() + .reply(200, { items }); + }); + + test('JSON.stringify works', async () => { + const assets = await client.assets.list().autoPagingToArray(); + expect(() => JSON.stringify(assets)).not.toThrow(); + expect(() => JSON.stringify(assets[0])).not.toThrow(); + }); + + test('change context for asset utility methods', async () => { + const assets = await client.assets.list().autoPagingToArray(); + const utilMethod = assets[0].children; + const result = await utilMethod(); + const resultAfterBind = await utilMethod.call(null); + expect({ ...result[0] }).toEqual({ ...resultAfterBind[0] }); + expect([{ ...result[0] }, { ...result[1] }]).toEqual(items); + }); + + test('spread operator receives only object data props', async () => { + const assets = await client.assets.list().autoPagingToArray(); + expect([{ ...assets[0] }, { ...assets[1] }]).toEqual(items); + expect(Object.assign({}, assets[1])).toEqual(items[1]); + }); + }); }); diff --git a/src/__tests__/resources/baseResource.unit.spec.ts b/src/__tests__/resources/baseResource.unit.spec.ts deleted file mode 100644 index 98852ff38b..0000000000 --- a/src/__tests__/resources/baseResource.unit.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019 Cognite AS - -import { CogniteClient } from '../..'; -import { BaseResource } from '../../resources/classes/baseResource'; -import { setupLoggedInClient } from '../testUtils'; - -interface TestType { - prop: string; -} - -class TestClass extends BaseResource {} - -describe('base resourse class', () => { - let client: CogniteClient; - beforeAll(async () => { - client = setupLoggedInClient(); - }); - test('to string & to json', () => { - const resource = new TestClass(client, { prop: 'test' }); - const stringRes = JSON.stringify(resource); - expect(typeof stringRes).toBe('string'); - expect(stringRes).toBe(resource.toString()); - }); -}); diff --git a/src/__tests__/resources/timeSeriesList.unit.spec.ts b/src/__tests__/resources/timeSeriesList.unit.spec.ts index 09b6467044..038cc549f9 100644 --- a/src/__tests__/resources/timeSeriesList.unit.spec.ts +++ b/src/__tests__/resources/timeSeriesList.unit.spec.ts @@ -99,4 +99,48 @@ describe('TimeSeriesList class unit test', async () => { expect(fetchedDatapoints).toHaveLength(3); expect(fetchedDatapoints[0].datapoints[0].timestamp).toBeDefined(); }); + + describe('class is not polluted with enumerable props', () => { + const items = [ + { + id: 1, + }, + { + id: 2, + }, + ]; + + beforeEach(() => { + nock.cleanAll(); + nock(mockBaseUrl) + .get(new RegExp('/timeseries/')) + .once() + .reply(200, { items }); + }); + + test('JSON.stringify works', async () => { + const timeseries = await client.timeseries.list().autoPagingToArray(); + expect(() => JSON.stringify(timeseries)).not.toThrow(); + expect(() => JSON.stringify(timeseries[0])).not.toThrow(); + }); + + test('change context for asset utility methods', async () => { + nock(mockBaseUrl) + .post(new RegExp('/timeseries/data/list')) + .twice() + .reply(200, { items }); + const timeseries = await client.timeseries.list().autoPagingToArray(); + const utilMethod = timeseries[0].getDatapoints; + const result = await utilMethod(); + const resultAfterBind = await utilMethod.call(null); + expect({ ...result[0] }).toEqual({ ...resultAfterBind[0] }); + expect([{ ...result[0] }, { ...result[1] }]).toEqual(items); + }); + + test('spread operator receives only object data props', async () => { + const timeseries = await client.timeseries.list().autoPagingToArray(); + expect([{ ...timeseries[0] }, { ...timeseries[1] }]).toEqual(items); + expect(Object.assign({}, timeseries[1])).toEqual(items[1]); + }); + }); }); diff --git a/src/resources/baseResourceApi.ts b/src/resources/baseResourceApi.ts index d7dac83e29..2df93fce5b 100644 --- a/src/resources/baseResourceApi.ts +++ b/src/resources/baseResourceApi.ts @@ -54,8 +54,8 @@ export abstract class BaseResourceAPI< return chunk(items, chunkSize); } constructor( - public readonly resourcePath: string, - public readonly httpClient: CDFHttpClient, + private readonly resourcePath: string, + protected readonly httpClient: CDFHttpClient, private map: MetadataMap ) {} diff --git a/src/resources/classes/asset.ts b/src/resources/classes/asset.ts index a378077bde..6c857c5b69 100644 --- a/src/resources/classes/asset.ts +++ b/src/resources/classes/asset.ts @@ -7,6 +7,14 @@ import { FileFilter, TimeseriesFilter, } from '../../index'; +import { + AssetDescription, + AssetName, + AssetSource, + CogniteExternalId, + CogniteInternalId, + Metadata, +} from '../../types/types'; import { AssetList } from './assetList'; import { BaseResource } from './baseResource'; @@ -17,36 +25,39 @@ export interface DeleteOptions { recursive?: boolean; } export class Asset extends BaseResource implements TypeAsset { - public get id() { - return this.props.id; - } - public get parentId() { - return this.props.parentId; - } - public get name() { - return this.props.name; - } - public get description() { - return this.props.description; - } - public get metadata() { - return this.props.metadata; - } - public get source() { - return this.props.source; - } - public get lastUpdatedTime() { - return this.props.lastUpdatedTime; - } - public get createdTime() { - return this.props.createdTime; - } - public get rootId() { - return this.props.rootId; - } + public id: CogniteInternalId; + public externalId?: CogniteExternalId; + public parentId?: CogniteInternalId; + public name: AssetName; + public description?: AssetDescription; + public metadata?: Metadata; + public source?: AssetSource; + public lastUpdatedTime: Date; + public createdTime: Date; + public rootId: CogniteInternalId; constructor(client: CogniteClient, props: TypeAsset) { - super(client, props); + super(client); + this.id = props.id; + this.externalId = props.externalId; + this.parentId = props.parentId; + this.name = props.name; + this.description = props.description; + this.metadata = props.metadata; + this.source = props.source; + this.lastUpdatedTime = props.lastUpdatedTime; + this.createdTime = props.createdTime; + this.rootId = props.rootId; + + Object.defineProperties(this, { + delete: { value: this.delete.bind(this), enumerable: false }, + parent: { value: this.parent.bind(this), enumerable: false }, + children: { value: this.children.bind(this), enumerable: false }, + subtree: { value: this.subtree.bind(this), enumerable: false }, + timeSeries: { value: this.timeSeries.bind(this), enumerable: false }, + events: { value: this.events.bind(this), enumerable: false }, + files: { value: this.files.bind(this), enumerable: false }, + }); } /** @@ -57,7 +68,7 @@ export class Asset extends BaseResource implements TypeAsset { * await asset.delete(); * ``` */ - public delete = async (options: DeleteOptions = {}) => { + public async delete(options: DeleteOptions = {}) { return this.client.assets.delete( [ { @@ -66,7 +77,7 @@ export class Asset extends BaseResource implements TypeAsset { ], options ); - }; + } /** * Retrieves the parent of the current asset @@ -74,7 +85,7 @@ export class Asset extends BaseResource implements TypeAsset { * const parentAsset = await asset.parent(); * ``` */ - public parent = async () => { + public async parent() { if (this.parentId) { const [parentAsset] = await this.client.assets.retrieve([ { id: this.parentId }, @@ -82,7 +93,7 @@ export class Asset extends BaseResource implements TypeAsset { return parentAsset; } return null; - }; + } /** * Returns an AssetList object with all children of the current asset @@ -90,7 +101,7 @@ export class Asset extends BaseResource implements TypeAsset { * const children = await asset.children(); * ``` */ - public children = async () => { + public async children() { const childAssets = await this.client.assets .list({ filter: { @@ -99,7 +110,7 @@ export class Asset extends BaseResource implements TypeAsset { }) .autoPagingToArray({ limit: Infinity }); return new AssetList(this.client, childAssets); - }; + } /** * Returns the full subtree of the current asset, including the asset itself @@ -108,13 +119,13 @@ export class Asset extends BaseResource implements TypeAsset { * const subtree = await asset.subtree(); * ``` */ - public subtree = async (options?: SubtreeOptions) => { + public async subtree(options?: SubtreeOptions) { const query: SubtreeOptions = options || {}; return this.client.assets.retrieveSubtree( { id: this.id }, query.depth || Infinity ); - }; + } /** * Returns all timeseries for the current asset @@ -123,14 +134,14 @@ export class Asset extends BaseResource implements TypeAsset { * const timeSeries = await asset.timeSeries(); * ``` */ - public timeSeries = async (filter: TimeseriesFilter = {}) => { + public async timeSeries(filter: TimeseriesFilter = {}) { return this.client.timeseries .list({ ...filter, assetIds: [this.id], }) .autoPagingToArray({ limit: Infinity }); - }; + } /** * Returns all events for the current asset @@ -139,13 +150,13 @@ export class Asset extends BaseResource implements TypeAsset { * const events = await asset.events(); * ``` */ - public events = async (filter: EventFilter = {}) => { + public async events(filter: EventFilter = {}) { return this.client.events .list({ filter: { ...filter, assetIds: [this.id] }, }) .autoPagingToArray({ limit: Infinity }); - }; + } /** * Returns all files for the current asset @@ -154,11 +165,15 @@ export class Asset extends BaseResource implements TypeAsset { * const files = await asset.files(); * ``` */ - public files = async (filter: FileFilter = {}) => { + public async files(filter: FileFilter = {}) { return this.client.files .list({ filter: { ...filter, assetIds: [this.id] }, }) .autoPagingToArray({ limit: Infinity }); - }; + } + + public toJSON() { + return { ...this }; + } } diff --git a/src/resources/classes/baseResource.ts b/src/resources/classes/baseResource.ts index d3149f5dfe..5f8dacd425 100644 --- a/src/resources/classes/baseResource.ts +++ b/src/resources/classes/baseResource.ts @@ -4,18 +4,18 @@ import { CogniteClient } from '../..'; export abstract class BaseResource { protected client: CogniteClient; - protected props: T; - constructor(client: CogniteClient, props: T) { + constructor(client: CogniteClient) { this.client = client; - this.props = props; + Object.defineProperty(this, 'client', { + value: this.client, + enumerable: false, + }); } - public toJSON() { - return this.props; - } + public abstract toJSON(): T; public toString() { - return JSON.stringify(this.props); + return JSON.stringify(this.toJSON()); } } diff --git a/src/resources/classes/timeSeries.ts b/src/resources/classes/timeSeries.ts index 22145d1a83..0c994177f7 100644 --- a/src/resources/classes/timeSeries.ts +++ b/src/resources/classes/timeSeries.ts @@ -1,53 +1,57 @@ // Copyright 2019 Cognite AS import { CogniteClient } from '../..'; import { + CogniteExternalId, + CogniteInternalId, DatapointsMultiQuery, GetTimeSeriesMetadataDTO, LatestDataPropertyFilter, + Metadata, } from '../../types/types'; import { BaseResource } from './baseResource'; export class TimeSeries extends BaseResource implements GetTimeSeriesMetadataDTO { - public get externalId() { - return this.props.externalId; - } - public get name() { - return this.props.name; - } - public get isString() { - return this.props.isString; - } - public get metadata() { - return this.props.metadata; - } - public get unit() { - return this.props.unit; - } - public get assetId() { - return this.props.assetId; - } - public get isStep() { - return this.props.isStep; - } - public get description() { - return this.props.description; - } - public get securityCategories() { - return this.props.securityCategories; - } - public get createdTime() { - return this.props.createdTime; - } - public get lastUpdatedTime() { - return this.props.lastUpdatedTime; - } - public get id() { - return this.props.id; - } + public externalId?: CogniteExternalId; + public name?: string; + public isString: boolean; + public metadata?: Metadata; + public unit?: string; + public assetId?: CogniteInternalId; + public isStep: boolean; + public description: string; + public securityCategories?: number[]; + public createdTime: Date; + public lastUpdatedTime: Date; + public id: CogniteInternalId; constructor(client: CogniteClient, props: GetTimeSeriesMetadataDTO) { - super(client, props); + super(client); + this.externalId = props.externalId; + this.name = props.name; + this.isString = props.isString; + this.metadata = props.metadata; + this.unit = props.unit; + this.assetId = props.assetId; + this.isStep = props.isStep; + this.description = props.description; + this.securityCategories = props.securityCategories; + this.createdTime = props.createdTime; + this.lastUpdatedTime = props.lastUpdatedTime; + this.id = props.id; + + Object.defineProperties(this, { + delete: { value: this.delete.bind(this), enumerable: false }, + getAsset: { value: this.getAsset.bind(this), enumerable: false }, + getDatapoints: { + value: this.getDatapoints.bind(this), + enumerable: false, + }, + getLatestDatapoints: { + value: this.getLatestDatapoints.bind(this), + enumerable: false, + }, + }); } /** @@ -57,9 +61,9 @@ export class TimeSeries extends BaseResource * await timeseries.delete(); * ``` */ - public delete = async () => { + public async delete() { return this.client.timeseries.delete([{ id: this.id }]); - }; + } /** * Retrieves the asset that the current timeseries is related to @@ -68,13 +72,13 @@ export class TimeSeries extends BaseResource * const assetList = await timeseries.getAsset(); * ``` */ - public getAsset = async () => { + public async getAsset() { if (this.assetId === undefined) { return null; } const assetList = await this.client.assets.retrieve([{ id: this.assetId }]); return assetList[0]; - }; + } /** * Retrieves all datapoints related to the current timeseries @@ -84,11 +88,11 @@ export class TimeSeries extends BaseResource * const datapoints = await timeseries.getDatapoints(); * ``` */ - public getDatapoints = async (options?: DatapointsMultiQuery) => { + public async getDatapoints(options?: DatapointsMultiQuery) { return this.client.datapoints.retrieve({ items: [{ ...options, id: this.id }], }); - }; + } /** * Retrieves the latest datapoints related to the current timeseries @@ -98,10 +102,14 @@ export class TimeSeries extends BaseResource * const latestDatapoints = await timeseries.getLatestDatapoints(); * ``` */ - public getLatestDatapoints = async ( - option: LatestDataPropertyFilter = {} - ) => { + public async getLatestDatapoints(option: LatestDataPropertyFilter = {}) { const filter: LatestDataPropertyFilter = option; return this.client.datapoints.retrieveLatest([{ ...filter, id: this.id }]); - }; + } + + public toJSON() { + return { + ...this, + }; + } }