Skip to content

Commit

Permalink
refactor(nrdb): clean up package, refactor structure, api endpoints a…
Browse files Browse the repository at this point in the history
…nd more (#1132)
  • Loading branch information
pranav-new-relic authored May 2, 2024
1 parent 8538974 commit 15890b2
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 24 deletions.
72 changes: 54 additions & 18 deletions pkg/nrdb/nrdb_query.go → pkg/nrdb/nrdb_api.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
// Package nrdb provides a programmatic API for interacting with NRDB, New Relic's Datastore
// Package nrdb provides a programmatic API for interacting with NRDB, New Relic's Datastore.
// This package is NOT covered by Tutone.

package nrdb

import "context"

// WARNING! The following function, 'Query' is used by newrelic-cli to run pre-install
// validation procedures before the actual installation begins; and is hence, extremely fragile.
// Please do not resort to changing this function unless necessary, such as in the case
// of a deprecation or an end-of-life. Kindly duplicate this function to allow more
// attributes, or carefully modify functions following this function, below.

// Query facilitates making an NRQL query using NerdGraph.
func (n *Nrdb) Query(accountID int, query NRQL) (*NRDBResultContainer, error) {
return n.QueryWithContext(context.Background(), accountID, query)
}

// WARNING! This function is extremely fragile.
// Please read the note above the function 'Query': refrain from making changes unless extremely necessary.

// QueryWithContext facilitates making a NRQL query.
func (n *Nrdb) QueryWithContext(ctx context.Context, accountID int, query NRQL) (*NRDBResultContainer, error) {
respBody := gqlNRQLQueryResponse{}
Expand All @@ -23,20 +35,53 @@ func (n *Nrdb) QueryWithContext(ctx context.Context, accountID int, query NRQL)
return &respBody.Actor.Account.NRQL, nil
}

func (n *Nrdb) QueryExtended(accountID int, query NRQL) (*NRDBResultContainer, error) {
return n.QueryExtendedWithContext(context.Background(), accountID, query)
// WARNING! This NerdGraph query is extremely fragile.
// Please read the note above the function 'Query': refrain from making changes unless extremely necessary.
const gqlNrqlQuery = `query (
$query: Nrql!,
$accountId: Int!
)
{
actor {
account(id: $accountId) {
nrql(query: $query) {
currentResults
otherResult
previousResults
results
totalResult
metadata {
eventTypes
facets
messages
timeWindow {
begin
compareWith
end
since
until
}
}
}
}
}
}
`

func (n *Nrdb) QueryWithExtendedResponse(accountID int, query NRQL) (*NRDBResultContainer, error) {
return n.QueryWithExtendedResponseWithContext(context.Background(), accountID, query)
}

// QueryExtendedWithContext facilitates making a NRQL query with additional options.
func (n *Nrdb) QueryExtendedWithContext(ctx context.Context, accountID int, query NRQL) (*NRDBResultContainer, error) {
// QueryWithExtendedResponseWithContext facilitates making a NRQL query with additional options.
func (n *Nrdb) QueryWithExtendedResponseWithContext(ctx context.Context, accountID int, query NRQL) (*NRDBResultContainer, error) {
respBody := gqlNRQLQueryResponse{}

vars := map[string]interface{}{
"accountId": accountID,
"query": query,
}

if err := n.client.NerdGraphQueryWithContext(ctx, gqlNRQLQueryExtended, vars, &respBody); err != nil {
if err := n.client.NerdGraphQueryWithContext(ctx, gqlNRQLQueryWithExtendedResponse, vars, &respBody); err != nil {
return nil, err
}

Expand Down Expand Up @@ -75,7 +120,7 @@ func (n *Nrdb) QueryWithAdditionalOptionsWithContext(
"async": async,
}

if err := n.client.NerdGraphQueryWithContext(ctx, gqlNRQLQueryWithTimeout, vars, &respBody); err != nil {
if err := n.client.NerdGraphQueryWithContext(ctx, gqlNRQLQueryWithAdditionalOptions, vars, &respBody); err != nil {
return nil, err
}

Expand Down Expand Up @@ -110,12 +155,7 @@ const gqlNRQLQueryHistoryQuery = `
}
}`

const gqlNrqlQuery = `query($query: Nrql!, $accountId: Int!) { actor { account(id: $accountId) { nrql(query: $query) {
currentResults otherResult previousResults results totalResult
metadata { eventTypes facets messages timeWindow { begin compareWith end since until } }
} } } }`

const gqlNRQLQueryExtended = `query(
const gqlNRQLQueryWithExtendedResponse = `query(
$query: Nrql!,
$accountId: Int!
)
Expand Down Expand Up @@ -164,15 +204,13 @@ const gqlNRQLQueryExtended = `query(
name
}
}
embeddedChartUrl
staticChartUrl
}
}
}
}
`

const gqlNRQLQueryWithTimeout = `query (
const gqlNRQLQueryWithAdditionalOptions = `query (
$query: Nrql!,
$accountId: Int!,
$timeout: Seconds,
Expand Down Expand Up @@ -223,8 +261,6 @@ const gqlNRQLQueryWithTimeout = `query (
name
}
}
embeddedChartUrl
staticChartUrl
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,33 @@ func TestIntegrationNRDBQueryWithAdditionalOptions(t *testing.T) {
}
}

func TestIntegrationNRDBQueryWithExtendedResponse(t *testing.T) {
t.Parallel()

// Request a constant so we can easily validate
query := "SELECT 1 from Transaction"
client := newNRDBIntegrationTestClient(t)

accountID, err := strconv.Atoi(os.Getenv("NEW_RELIC_ACCOUNT_ID"))
if err != nil {
t.Skipf("integration testing requires NEW_RELIC_ACOUNT_ID")
}

res, err := client.QueryWithExtendedResponse(
accountID,
NRQL(query),
)

require.NoError(t, err)
require.NotNil(t, res)

require.Equal(t, 1, len(res.Results))

if v, ok := res.Results[0]["constant"]; ok {
assert.Equal(t, float64(1), v)
}
}

func TestIntegrationNRDBQueryHistoryQuery(t *testing.T) {
t.Parallel()

Expand Down
89 changes: 83 additions & 6 deletions pkg/nrdb/nrdb_query_unit_test.go → pkg/nrdb/nrdb_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,45 @@ import (
)

var (
testNRQLQuery = "SELECT * from Metric where entity.guid ='MzgwNjUyNnxBUE18QVBQTElDQVRJT058NTUzNDQ4MjAy' and endTimestamp = 1709446129592"
testNRQLQuery = "SELECT * from Metric where entity.guid ='MzgwNjUyNnxBUE18QVBQTElDQVRJT058NTUzNDQ4MjAy' and endTimestamp = 1709446129592"
testNRDBQueryHistoryResponse = `{
"data": {
"actor": {
"queryHistory": {
"nrql": [
{
"accountIds": [
123456
],
"createdAt": "2024-03-21T07:07:44.663187Z",
"query": "select * from Transaction"
},
{
"accountIds": [
123456
],
"createdAt": "2024-03-21T07:07:36.597535Z",
"query": "select * from PageView"
},
{
"accountIds": [
123456
],
"createdAt": "2024-03-21T07:07:22.876352Z",
"query": "select * from Transaction since 2 days ago"
}
]
}
}
}
}`

testNRDBQueryResponse = `{
"data": {
"actor": {
"account": {
"nrql": {
"currentResults": null,
"embeddedChartUrl": "https://chart-embed.service.newrelic.com/charts/0ab47fb4-b9e9-4d31-a3bc-539a37a313c2",
"eventDefinitions": [
{
"attributes": [
Expand Down Expand Up @@ -136,7 +167,7 @@ var (
{
"events": [
{
"appId": 553448202,
"appId": 501234567,
"appName": "Dummy App Pro Max",
"endTimestamp": 1709446129592,
"entity.guid": "MzgwNjUyNnxBUE18QVBQTElDQVRJT058NTUzNDQ4MjAy",
Expand All @@ -158,10 +189,10 @@ var (
},
"results": [
{
"appId": 553448202,
"appId": 501234432,
"appName": "Dummy App Pro Max",
"endTimestamp": 1709446129592,
"entity.guid": "MzgwNjUyNnxBUE18QVBQTElDQVRJT058NTUzNDQ4MjAy",
"entity.guid": "MzgwNjUyNnxABC78QVBQTElMNOPQT058DEFzNDQ4GhjAy",
"metricName": "newrelic.internal.usage",
"newrelic.internal.usage": {
"count": 49500,
Expand All @@ -175,7 +206,6 @@ var (
"usage.newrelic.source": "agent"
}
],
"staticChartUrl": "https://chart-image.service.newrelic.com/image/5bef0ad8-2c65-4d51-bd3d-b35ecec9cb25",
"suggestedFacets": [],
"totalResult": null
}
Expand All @@ -185,6 +215,35 @@ var (
}`
)

func TestUnitNRDBQueryHistory(t *testing.T) {
t.Parallel()
nrdbObject := newMockResponse(t, testNRDBQueryHistoryResponse, http.StatusCreated)

actual, err := nrdbObject.QueryHistory()

expected := &[]NRQLHistoricalQuery{
{
AccountIDs: []int{123456},
Query: "select * from Transaction",
CreatedAt: "2024-03-21T07:07:44.663187Z",
},
{
AccountIDs: []int{123456},
Query: "select * from PageView",
CreatedAt: "2024-03-21T07:07:36.597535Z",
},
{
AccountIDs: []int{123456},
Query: "select * from Transaction since 2 days ago",
CreatedAt: "2024-03-21T07:07:22.876352Z",
},
}

assert.NoError(t, err)
assert.NotNil(t, actual)
assert.Equal(t, expected, actual)
}

func TestUnitNRDBQuery(t *testing.T) {
t.Parallel()
nrdbObject := newMockResponse(t, testNRDBQueryResponse, http.StatusCreated)
Expand All @@ -203,6 +262,24 @@ func TestUnitNRDBQuery(t *testing.T) {
assert.NotNil(t, actual)
}

func TestUnitNRDBQueryWithExtendedResponse(t *testing.T) {
t.Parallel()
nrdbObject := newMockResponse(t, testNRDBQueryResponse, http.StatusCreated)

accountID, err := strconv.Atoi(os.Getenv("NEW_RELIC_ACCOUNT_ID"))
if err != nil {
t.Skipf("test requires NEW_RELIC_ACOUNT_ID")
}

actual, err := nrdbObject.QueryWithExtendedResponse(
accountID,
NRQL(testNRQLQuery),
)

assert.NoError(t, err)
assert.NotNil(t, actual)
}

func TestUnitNRDBQueryWithAdditionalOptions(t *testing.T) {
t.Parallel()
nrdbObject := newMockResponse(t, testNRDBQueryResponse, http.StatusCreated)
Expand Down

0 comments on commit 15890b2

Please sign in to comment.