Skip to content

Commit

Permalink
Merge pull request #66 from mountaindude/63-typos
Browse files Browse the repository at this point in the history
63 typos
  • Loading branch information
mountaindude authored Sep 13, 2023
2 parents bacefb7 + b18afa7 commit 058660e
Show file tree
Hide file tree
Showing 12 changed files with 706 additions and 40 deletions.
7 changes: 3 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@

## [0.2.0](https://github.com/ptarmiganlabs/ctrl-q-nr/compare/v0.1.4...v0.2.0) (2023-09-11)


### ⚠ BREAKING CHANGES

* **cloud:** Move cloud license info to msg.payload.license in out msg
- **cloud:** Move cloud license info to msg.payload.license in out msg

### Bug Fixes

* **cloud:** clientId no longer required when using API key auth ([233ede3](https://github.com/ptarmiganlabs/ctrl-q-nr/commit/233ede30e6ad408f2ace7d885de74b21835ebf3f)), closes [#59](https://github.com/ptarmiganlabs/ctrl-q-nr/issues/59)
* **cloud:** Move cloud license info to msg.payload.license in out msg ([8c95c9e](https://github.com/ptarmiganlabs/ctrl-q-nr/commit/8c95c9e775e3d07d8ebfa3f6eecf8ad177b5cc0b)), closes [#60](https://github.com/ptarmiganlabs/ctrl-q-nr/issues/60)
- **cloud:** clientId no longer required when using API key auth ([233ede3](https://github.com/ptarmiganlabs/ctrl-q-nr/commit/233ede30e6ad408f2ace7d885de74b21835ebf3f)), closes [#59](https://github.com/ptarmiganlabs/ctrl-q-nr/issues/59)
- **cloud:** Move cloud license info to msg.payload.license in out msg ([8c95c9e](https://github.com/ptarmiganlabs/ctrl-q-nr/commit/8c95c9e775e3d07d8ebfa3f6eecf8ad177b5cc0b)), closes [#60](https://github.com/ptarmiganlabs/ctrl-q-nr/issues/60)

## [0.1.4](https://github.com/ptarmiganlabs/ctrl-q-nr/compare/v0.1.3...v0.1.4) (2023-09-11)

Expand Down
149 changes: 149 additions & 0 deletions src/lib/cloud/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/* eslint-disable no-await-in-loop */
const { getAllSpaces } = require('./space');

// Function to get all apps and their associated metadata
// Parameters:
// qlik: authentication object
//
// Return
// Success: An array of app objects
// Failure: false
async function getAllApps(qlik) {
let allItems = [];
try {
const items = await qlik.items.getItems({ resourceType: 'app', limit: 100 });

// Paginate through the results
// eslint-disable-next-line no-restricted-syntax
for await (const item of items.pagination) {
allItems.push(item);
}
} catch (error) {
allItems = false;
}

// Return the apps' metadata
return allItems;
}

// Function to look up apo IDs given app names, colletion names or managed space names
// Parameters:
// node: node object
// qlik: authentication object
// lookupSource: object containing entities to look up and translate into app IDs
//
// Return
// Success: An object containing an array of unique app IDs and an array of unique app objects
// Failure: false
async function lookupAppId(node, qlik, lookupSource) {
const allAppIds = [];
const allAppObjects = [];

// Get all apps.
const allApps = await getAllApps(qlik);

// Did we get any apps? If not, report error and return
// Also make sure we got an array
if (!allApps || !Array.isArray(allApps)) {
node.log('Error getting apps in lookupAppId');
node.status({ fill: 'red', shape: 'ring', text: 'error getting apps' });
return false;
}

// Qlik SaaS APIs don't have a good filtering mechanism, so we'll have to filter the results ourselves.

// lookupSource.appName is an array of app names.
// We'll iterate through the array and look for apps with matching names.
// Save the app IDs to the allAppIds array.
if (lookupSource.appName) {
try {
// Iterate through the array of app names
lookupSource.appName.forEach((appName) => {
// Iterate through the array of apps
allApps.forEach((app) => {
// Does the app name match?
if (app.name === appName) {
// Yes. Save the app ID to the allAppIds array
allAppIds.push(app.id);
allAppObjects.push(app);
}
});
});
} catch (error) {
node.log(`Error looking up app name: ${error}`);
node.status({ fill: 'red', shape: 'ring', text: 'error looking up app name' });
return false;
}
}

// lookupSource.spaceName is an array of space names.
// We'll iterate through the array and look for apps that are in the spaces.
// Save the app IDs to the allAppIds array.
if (lookupSource.spaceName) {
try {
// Get all spaces
const allSpaces = await getAllSpaces(node, qlik);

// Did we get any spaces? If not, report error and return
// Also make sure we got an array
if (!allSpaces || !Array.isArray(allSpaces)) {
node.log('Error getting spaces in lookupAppId');
node.status({ fill: 'red', shape: 'ring', text: 'error getting spaces' });
return false;
}

// Iterate through the array of space names
lookupSource.spaceName.forEach(async (spaceName) => {
// Iterate through the array of spaces
allSpaces.forEach(async (space) => {
// Does the space name match?
if (space.name === spaceName) {
// Yes. Get the ID of this space
const spaceId = space.id;

// Get all apps in this space
// Each app object has an attribute called resourceAttributes.spaceId
// that is set to the space ID if the app is part of a space
const appsInSpace = allApps.filter((app) => app.resourceAttributes.spaceId === spaceId);

// Save the app IDs to the allAppIds array
appsInSpace.forEach((app) => {
allAppIds.push(app.id);
allAppObjects.push(app);
});
}
});
});
} catch (error) {
node.log(`Error looking up space name: ${error}`);
node.status({ fill: 'red', shape: 'ring', text: 'error looking up space name' });
return false;
}
}

// Remove duplicates from the allAppIds array
const uniqueAppIds = [...new Set(allAppIds)];

// Get the app objects for the unique app IDs
const uniqueAppObjects = [];

// Make sure we got an array
if (!uniqueAppIds || !Array.isArray(uniqueAppIds)) {
node.log('Error getting unique app IDs in lookupAppId');
node.status({ fill: 'red', shape: 'ring', text: 'error getting unique app IDs' });
return false;
}

uniqueAppIds.forEach((appId) => {
const appObject = allAppObjects.find((app) => app.id === appId);
uniqueAppObjects.push(appObject);
});

return { uniqueAppIds, uniqueAppObjects };
}

// Make the function available to other files
module.exports = {
lookupAppId,
getAllApps,
};
33 changes: 29 additions & 4 deletions src/lib/cloud/auth.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
const { Auth, AuthType } = require('@qlik/sdk');
const Qlik = require('@qlik/sdk').default;

/**
* Function to get host url for Qlik Sense Cloud
* @param {*} tenant
* @returns {string} host url
*/

// Function to get host url for Qlik Sense Cloud
function getHostUrl(tenant) {
const host = `${tenant.tenant}.${tenant.region}.qlikcloud.com`;
return host;
}

// Function to authenticate with Qlik Sense Cloud
// Return an auth object
/**
* Function to authenticate with Qlik Sense Cloud
* @param {*} node
* @param {*} done
* @returns {object} auth and qlik objects, or false if authentication fails
*/
async function authenticate(node, done) {
// Get host url
const host = getHostUrl(node.tenant);

// Which authentication type to use?
let auth;
let qlik;

if (node.tenant.authType === 'oauth2-m2m') {
// Make sure that the client ID and client secret are specified
if (node.tenant.clientId === '') {
Expand Down Expand Up @@ -42,6 +54,13 @@ async function authenticate(node, done) {
clientSecret: node.tenant.clientSecret,
});

qlik = new Qlik({
authType: AuthType.OAuth2,
host,
clientId: node.tenant.clientId,
clientSecret: node.tenant.clientSecret,
});

await auth.authorize();
} else if (node.tenant.authType === 'apikey') {
// Make sure that the API key is specified
Expand All @@ -60,6 +79,12 @@ async function authenticate(node, done) {
host,
apiKey: node.tenant.apiKey,
});

qlik = new Qlik({
authType: AuthType.APIKey,
host,
apiKey: node.tenant.apiKey,
});
} else {
// Invalid auth type. Log error and return
node.status({ fill: 'red', shape: 'ring', text: 'invalid auth type' });
Expand All @@ -70,7 +95,7 @@ async function authenticate(node, done) {
return false;
}

return auth;
return { auth, qlik };
}

module.exports = {
Expand Down
8 changes: 4 additions & 4 deletions src/lib/cloud/reloadstatus.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ class ReloadStateMachine {
// Send message to output 2
const outMsg = {
payload: {
message: 'Timer started',
message: 'timer started',
},
};
this.node.send([null, outMsg]);
Expand Down Expand Up @@ -176,7 +176,7 @@ class ReloadStateMachine {
// Send message to output 2
const outMsg = {
payload: {
message: 'Timer stopped',
message: 'timer stopped',
},
};
this.node.send([null, outMsg]);
Expand Down Expand Up @@ -238,7 +238,7 @@ class ReloadStateMachine {
this.node.log('updating reload states');

// Get reloads from Qlik Sense Cloud
const auth = await authenticate(node);
const { auth } = await authenticate(node);
if (!auth) {
// Error when authenticating
node.status({ fill: 'red', shape: 'ring', text: 'error authenticating' });
Expand Down Expand Up @@ -295,7 +295,7 @@ class ReloadStateMachine {
// Send message to output 2
const outMsg = {
payload: {
message: 'Relead states updated',
message: 'reload states updated',
numReloadsAdded,
numReloadsDeleted,
numReloadsUpdated,
Expand Down
84 changes: 84 additions & 0 deletions src/lib/cloud/space.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/* eslint-disable no-await-in-loop */

// Function to get all spaces and their associated metadata
// Parameters:
// node: node object
// qlik: authentication object
//
// Return
// Success: An array of space objects
// Failure: false
async function getAllSpaces(node, qlik) {
let allItems = [];
try {
const items = await qlik.spaces.getSpaces({ limit: 100 });

// eslint-disable-next-line no-restricted-syntax
for await (const item of items.pagination) {
allItems.push(item);
}
} catch (error) {
node.log(`Error getting spaces: ${error}`);
allItems = false;
}

// Return the spaces' metadata
return allItems;
}

// Function to get all apps that are in a space
// Parameters:
// auth: authentication object
// spaceId: the ID of the space

// Return
// Success: An array of app objects
// Failure: false
async function getAppsInSpace(auth, spaceId) {
let allItems = [];
let path = `/items?resourceType=app&spaceId=${spaceId}`;

// Loop until there are no more pages of results
// eslint-disable-next-line no-constant-condition
while (true) {
try {
// Parameters for the /items endpoint
const params = {
limit: 100,
};

const response = await auth.rest(path, params);
const allApps = await response.json();

// Push all objects to the allItems array
// eslint-disable-next-line no-loop-func
allApps.data.forEach((app) => {
allItems.push(app);
});

// Are there more pages of results?
if (!allApps.links.next || allApps.links.next === null) {
// No more pages. Break out of the loop
break;
}

// Build the path for the next page of results
// Only use the path after the ..../api/v1 part
// eslint-disable-next-line prefer-destructuring
path = allApps.links.next.href.split('/api/v1')[1];
// console.log(`next path: ${path}`);
} catch (error) {
allItems = false;
break;
}
}

// Return the apps' metadata
return allItems;
}

// Make the function available to other files
module.exports = {
getAllSpaces,
getAppsInSpace,
};
Loading

0 comments on commit 058660e

Please sign in to comment.