Skip to content

Instantly share code, notes, and snippets.

@khpeet
Created February 6, 2024 22:43
Show Gist options
  • Save khpeet/961f4b1657a03980997fa68afa8758e3 to your computer and use it in GitHub Desktop.
Save khpeet/961f4b1657a03980997fa68afa8758e3 to your computer and use it in GitHub Desktop.
Fetches partial keys from graphql, combines that data with usage data via NrComputeUsage (across many accounts), and inserts into NRDB as queryable events.
//Configure as synthetic SCRIPTED_API monitor
var got = require('got');
//** REQUIRED INPUTS **//
var API_KEY = $secure.KEY_USAGE; // GraphQL User Key
var INGEST_KEY = $secure.INGEST_KEY; //Ingest key
var ACCOUNT_ID = 1 //account to insert events into
//** REQUIRED INPUTS **//
var GRAPH_API = 'https://api.newrelic.com/graphql';
var HEADERS = { 'Content-Type': 'application/json', 'API-Key': API_KEY };
async function main() {
let accounts = await getAccounts();
let keys = await getKeyDetail();
for (let account of accounts) {
let anAccountResult = await getApiUsage(account.id);
let processed = await processResults(keys, anAccountResult);
console.log('Writing results for account: ' + account.name);
if (processed.length > 1000) {
let chunks = await chunkData(processed, 200);
for (var ck=0; ck < chunks.length; ck++) {
console.log('Writing chunk #' + ck.toString());
await writeToNRDB(chunks[ck]);
}
} else {
await writeToNRDB(processed);
}
}
}
function chunkData(d, size) {
return new Promise((resolve, reject) => {
let chunked = d.reduceRight((r,i,_,s) => (r.push(s.splice(0,size)),r),[]);
resolve(chunked)
})
}
async function writeToNRDB(payload) {
var h = {
'Content-Type': 'application/json',
'X-Insert-Key': INGEST_KEY
//'Content-Encoding': 'gzip'
};
var opts = {
url: `https://insights-collector.newrelic.com/v1/accounts/${ACCOUNT_ID.toString()}/events`,
headers: h,
json: payload //if payload is compressed (gzip), set this to `body` instead of `json`
};
let resp = await got.post(opts);
if (resp.statusCode == 200) {
console.log('done');
} else {
console.log('Error posting to NRDB ' + resp.statusCode);
console.log(resp.body);
throw new Error('Failed to post to NRDB');
}
}
async function processResults(k, r) {
let formatted = [];
let eventType = `NrUserKeyUsage`;
for (let res of r) {
for (let key of k) {
if (key.type == 'USER') {
let user = key.user ? key.user.email : null;
if (user !== null) {
if (user.toLowerCase() == res.facet[0].toLowerCase() && key.id == res.facet[2]) {
let apiKeyId = res.facet[2];
let executedQuery = res.facet[1];
if (res.facet[1].length >= 4096) {
executedQuery = res.facet[1].substring(0, 4095);
}
formatted.push({'eventType': eventType, 'keyCount': res.count, 'userEmail': user.toLowerCase(), 'query': executedQuery, 'partialKey': key.obfuscatedKey, 'keyId': res.facet[2], 'userId': key.user.id, 'userName': key.user.name, 'accountName': key.account.name, 'acct_id': key.account.id})
break;
}
}
}
}
}
return formatted;
}
async function getAccounts() {
let q = `{
actor {
accounts {
id
name
}
}
}
`;
var opts = {
url: GRAPH_API,
headers: HEADERS,
json: {'query': q, 'variables':{}}
};
let resp = await got.post(opts).json();
return resp.data.actor.accounts;
}
async function getApiUsage(account) {
let q = `{
actor {
nrql(
accounts: ${account}
query: "FROM NrComputeUsage SELECT count(*) as 'count' where dimension_productCapability = 'NerdGraph API' facet dimension_email, dimension_query, dimension_userApiKeyId since 1 hour ago LIMIT MAX"
timeout: 90
) {
results
}
}
}
`;
var opts = {
url: GRAPH_API,
headers: HEADERS,
json: {'query': q, 'variables':{}}
};
let resp = await got.post(opts).json();
return resp.data.actor.nrql.results;
}
async function getKeyDetail() {
let q = `{
actor {
apiAccess {
keySearch(query: {types: [USER]}) {
keys {
... on ApiAccessUserKey {
id
name
obfuscatedKey
user {
id
name
email
}
account {
id
name
}
type
}
}
nextCursor
}
}
}
}`;
var opts = {
url: GRAPH_API,
headers: HEADERS,
json: {'query': q, 'variables':{}}
};
let resp = await got.post(opts).json();
return resp.data.actor.apiAccess.keySearch.keys;
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment