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(asset): add asset data source with utilization query #52

Closed

Conversation

TigranVardanyan
Copy link
Collaborator

@TigranVardanyan TigranVardanyan commented Nov 29, 2023

Pull Request

🀨 Rationale

Asset data source with utilization query

πŸ‘©β€πŸ’» Implementation

Complexity - utilization calculations based on raw data( time zone offsets, overlapping asset usage, splitting data by day/hour )

Alternative solutions - instead of using the 'niapm/v1/query-asset-utilization-history' endpoint with additional calculations, the 'niapm/v1/query-asset-utilization' endpoint can be utilized(returns only peak utilization data).

Performance impact - despite the numerous calculations in the code, performance is not compromised, as there are relatively few server requests involved.

πŸ§ͺ Testing

Manually reviewed and contrasted the output of the plugin with the data from the 'niapm/v1/query-asset-utilization' endpoint and the 'Advanced Asset Utilization [8h].ipynb' notebook.

βœ… Checklist

@cameronwaterman
Copy link
Collaborator

Please fill out the other sections of the PR template and update your PR name to comply with the required format documented in the README.

Copy link
Collaborator

@cameronwaterman cameronwaterman left a comment

Choose a reason for hiding this comment

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

Waiting until PR description and title meet requirements.

@TigranVardanyan TigranVardanyan changed the title Users/tvardany/asset utilization datasource feat: add asset data source with utilization query Nov 30, 2023
@cameronwaterman cameronwaterman changed the title feat: add asset data source with utilization query feat(asset): add asset data source with utilization query Nov 30, 2023
Copy link
Collaborator

@cameronwaterman cameronwaterman left a comment

Choose a reason for hiding this comment

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

It feels a little odd to me to have a Asset Utilization data source. Following existing patterns, I think it would fit better as simply an Asset data source that can display utilization, etc. data via switching between items in a radio button group. Thoughts @mure ?

src/datasources/asset-utilization/README.md Outdated Show resolved Hide resolved
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's get some tests written for this file.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's get some tests written for this file.

DataSourceInstanceSettings,
dateTime,
} from '@grafana/data';
import {BackendSrv, getBackendSrv, getTemplateSrv, TemplateSrv, TestingStatus} from '@grafana/runtime';
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
import {BackendSrv, getBackendSrv, getTemplateSrv, TemplateSrv, TestingStatus} from '@grafana/runtime';
import { BackendSrv, getBackendSrv, getTemplateSrv, TemplateSrv, TestingStatus } from '@grafana/runtime';

dateTime,
} from '@grafana/data';
import {BackendSrv, getBackendSrv, getTemplateSrv, TemplateSrv, TestingStatus} from '@grafana/runtime';
import {DataSourceBase} from 'core/DataSourceBase';
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
import {DataSourceBase} from 'core/DataSourceBase';
import { DataSourceBase } from 'core/DataSourceBase';

Comment on lines 279 to 283
/**
* Return smallest start and biggest end of given array of utilization intervals
* @param data {Array<Interval<any>>} - Array of utilization intervals
* @returns {[Date, Date]} -
*/
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why does this function have documentation but none of the others do?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

removed

return mergedIntervals;
}

export const getBusinessHours = (
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is a super long function, could it be split up?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

changed to another super long function with some description

Comment on lines 146 to 147


Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change

* @returns {[Date, Date]} -
*/
export const getStartEndDates = (data: Array<Interval<any>>) => {

Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change

BOTH = 'BOTH'
}

export enum IsPeak {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is an enum but reads like a boolean to me.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

let's leave everything as it is
maybe in the future we will add an option to show all without exception

@cameronwaterman
Copy link
Collaborator

It's probably not worth it now, but going forward it would be preferable that a change of this size gets split in to several PRs.

@mure
Copy link
Collaborator

mure commented Dec 7, 2023

It feels a little odd to me to have a Asset Utilization data source. Following existing patterns, I think it would fit better as simply an Asset data source that can display utilization, etc. data via switching between items in a radio button group. Thoughts @mure ?

Agreed, this is feedback I had also provided

return Boolean(query.assetIdentifier && query.minionId)
}

processQuery(query: AssetUtilizationQuery): ValidAssetUtilizationQuery {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This function isn't needed - the base class will take care of applying the defaults.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

removed

Copy link
Collaborator

Choose a reason for hiding this comment

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

We have a prettier configuration in the repo that should take care of this. There's not an NPM script for it, but you should be able to run npx prettier [file] --write. There's also a prettier extension for VSCode.

Comment on lines 55 to 56
const from = (new Date(range!.from.valueOf())).toISOString();
const to = (new Date(range!.to.valueOf())).toISOString();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why are we creating a new Date object both here and when we pass it to onLoadUtilizationHistory? Can we simplify this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

removed

TigranVardanyan and others added 4 commits March 19, 2024 05:01
Add asset metadata section
Relocate certain functions to the core section for better modularity
Fixed paddings
Add missing return types of functions
Correct the calculation algorithm
Minor improvements
minor improvements
@mure
Copy link
Collaborator

mure commented Apr 4, 2024

@TigranVardanyan I updated your branch from main to pull in an update of the plugin toolkit and fixed a type error

@mure
Copy link
Collaborator

mure commented Apr 4, 2024

Looks like one of the error tests is failing. Not sure if it's related to the merge from main?

import { DateTime, dateTime } from "@grafana/data";

export interface AssetQuery extends DataQuery {
assetQueryType: AssetQueryType,
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: Just call this property type since it's already under the AssetQuery interface

Suggested change
assetQueryType: AssetQueryType,
type: AssetQueryType,

Comment on lines +67 to +76
export type ColumnDataType = 'BOOL' | 'INT32' | 'INT64' | 'FLOAT32' | 'FLOAT64' | 'STRING' | 'TIMESTAMP';

export interface Column {
name: string;
dataType: ColumnDataType;
columnType: 'INDEX' | 'NULLABLE' | 'NORMAL';
properties: Record<string, string>;
}

export type NumberTuple = [number, number]
Copy link
Collaborator

Choose a reason for hiding this comment

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

These seem like types from the data frame plugin, can we delete? Also I don't see the NumberTuple type used anywhere.

Comment on lines +101 to +102
'startTimestamp': T,
'endTimestamp': T
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
'startTimestamp': T,
'endTimestamp': T
startTimestamp: T,
endTimestamp: T

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm surprised the linter doesn't catch this. We don't use quotes in the property names of interfaces or object literals usually. There's a few places this applies to.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done in all places

Comment on lines +146 to +153
export interface SystemLinkError {
error: {
args: string[];
code: number;
message: string;
name: string;
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is duplicated in the core utils, please delete

Comment on lines +165 to +170
export interface TableMetadata {
columns: Column[];
id: string;
name: string;
workspace: string;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

More data frame types that should be deleted

if (query.workspace) {
filterString += `workspace = "${query.workspace}"`;
}
let systemMetadata = await this.post<{ data: SystemMetadata[] }>(this.sysmgmtUrl + '/query-systems', {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This should use the shared function for querying systemsβ€”add more params to the function if needed.

} catch (error) {
throw new Error(`An error occurred while retrieving utilization history: ${error}`);
}
} while (continuationToken && (maxDataPoints && requestCount < maxDataPoints));
Copy link
Collaborator

Choose a reason for hiding this comment

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

options.maxDataPoints defaults to the width of the panel, which is usually 100s of pixels. Are we ever actually making that many requests here? That seems weird to me.

'endTime': momentToTime(dateTime(query.nonPeakStart))
}
const utilizationFilter: string = utilizationCategory === UtilizationCategory.TEST ? `Category = \"Test\" or Category = \"test\"` : '';
let arrayOfEntityIds: string[];
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
let arrayOfEntityIds: string[];
let entityIds: string[];

We don't usually put the type of a variable in its name


const buildQuery = getQueryBuilder<AssetQuery>()({});

mockTimers();
Copy link
Collaborator

Choose a reason for hiding this comment

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

This isn't necessaryβ€”you only need this if you're testing code that relies on window.setTimeout


await expect(ds.query(buildQuery(assetUtilizationQueryMock))).rejects.toThrow()
})
})
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should have tests that verify the output of the utilization queries. There's a lot of logic in the data source for building up filters and parsing responses that needs coverage. You can use Jest's snapshot testing to make it really easy to assert on the output. That's what we do in the tag data source:

expect(result.data).toMatchSnapshot();

I would also want to see a test that verifies that we replace template variables in queries correctly.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you add an entry to provisioning/example.yaml for the assets plugin?

@mure
Copy link
Collaborator

mure commented Jul 16, 2024

Closing this PR as it's being split up into separate changes

@mure mure closed this Jul 16, 2024
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.

3 participants