This blog post is part of a series of blogs I published about @sap/datasphere-cli. Find all blog posts related to this topic in my overview blog post here.
The @sap/datasphere-cli Node.js module is available since wave 2021.20. In another blog "New Command-Line Interface for SAP Datasphere – code your way to the cloud!" I describe how to install and use the command-line interface (CLI). With this blog I'm introducing you to one of the most asked use cases for the CLI: automated mass-member assignment to a space in SAP Datasphere. Wow! 🙂
This blog is part of a blog post series about the Command-Line Interface (CLI) for SAP Datasphere. Find the full overview of all available blogs in the overview blog post here.
$ node -v
v12.21.0
$ mkdir cli-add-users
$ cd cli-add-users
$ npm init --yes
{
"name": "dwc-cli-add-users",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
$ touch index.js
cli-add-users
|- package.json
|- index.js
// .gitignore
node_modules
$ npm install @sap/datasphere-cli fs-extra puppeteer path
$ npm install -g @sap/datasphere-cli
// index.js
(async () => {
console.log('I was called!');
})();
$ node index.js
I was called!
const MEMBERS_FILE = 'members.json';
const SPACE_DEFINITION_FILE = 'space.json';
const DWC_URL = 'https://<prefix>.<region>.hcs.cloud.sap/'; // eg https://mytenant.eu10.hcs.cloud.sap/
const DWC_PASSCODE_URL = 'https://<prefix>.authentication.<region>.hana.ondemand.com/passcode'; // eg https://mytenant.authentication.eu10.hana.ondemand.com/passcode
const SPACE = '<space ID>';
const USERNAME = '<username>'; // eg firstname.lastname@company.com
const PASSWORD = '<password>';
(async () => {
console.log('I was called!');
})();
const puppeteer = require("puppeteer");
const MEMBERS_FILE = 'members.json';
[...]
const PASSWORD = '<password>';
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(DWC_PASSCODE_URL);
await page.waitForSelector('#logOnForm', {visible: true, timeout: 5000});
if (await page.$('#logOnForm') !== null) {
await page.type('#j_username', USERNAME);
await page.type('#j_password', PASSWORD);
await page.click('#logOnFormSubmit');
}
await page.waitForSelector('div.island > h1 + h2', {visible: true, timeout: 5000});
const passcode = await page.$eval('h2', el => el.textContent);
console.log('passcode', passcode);
await browser.close();
})();
$ node index.js
passcode qjm4Zlrza2
[...]
const PASSWORD = '<password>';
let page;
const getPasscode = async () => {
await page.waitForSelector('div.island > h1 + h2', {visible: true, timeout: 5000});
await page.reload();
return await page.$eval('h2', el => el.textContent);
}
(async () => {
const browser = await puppeteer.launch({ headless: false });
page = await browser.newPage();
await page.goto(DWC_PASSCODE_URL);
await page.waitForSelector('#logOnForm', {visible: true, timeout: 5000});
if (await page.$('#logOnForm') !== null) {
await page.type('#j_username', USERNAME);
await page.type('#j_password', PASSWORD);
await page.click('#logOnFormSubmit');
}
const passcode = await getPasscode();
console.log('passcode', passcode);
await browser.close();
})();
const puppeteer = require("puppeteer");
const path = require('path');
const exec = require("child_process").exec;
[...]
return await page.$eval('h2', el => el.textContent);
}
const execCommand = async (command) => new Promise(async (res, rej) => {
const passcode = await getPasscode();
const cmd = `node ${path.join(process.cwd(), 'node_modules', '@sap', 'dwc-cli', 'index.js')} ${command} -H ${DWC_URL} -p ${passcode}`;
exec(cmd, (error, stdout, stderr) => {
if (error) {
rej({ error, stdout, stderr });
}
res({ error, stdout, stderr });
});
});
(async () => {
const browser = await puppeteer.launch({ headless: false });
[...]
Please note: With version 2022.2.0 the call to the main.js file changed. Previously, it looked like this (the dist segment got removed and main.js was renamed to index.js with 2022.2.0): const cmd = `node ${path.join(process.cwd(), 'node_modules', '@sap', 'datasphere-cli', 'dist', 'main.js')} ${command} -H ${DWC_URL} -p ${passcode}`;
[...]
await page.click('#logOnFormSubmit');
}
await execCommand(`spaces read -S ${SPACE} -o ${SPACE_DEFINITION_FILE}`)
await browser.close();
})();
{
"<Space ID>": {
"spaceDefinition": {
"version": "1.0.4",
[...]
"members": [],
[...]
}
}
}
[
{
"name": "<User ID 1>",
"type": "user"
},
{
"name": "<User ID 2>",
"type": "user"
},
{
"name": "<User ID 3>",
"type": "user"
},
[...]
]
[...]
const exec = require("child_process").exec;
const fs = require('fs-extra');
[...]
await execCommand(`spaces read -S ${SPACE} -o ${SPACE_DEFINITION_FILE}`);
const spaceDefinition = JSON.parse(await fs.readFile(SPACE_DEFINITION_FILE, 'utf-8'));
const additionalMembers = JSON.parse(await fs.readFile(MEMBERS_FILE, 'utf-8'));
await browser.close();
})();
[...]
const spaceDefinition = JSON.parse(await fs.readFile(SPACE_DEFINITION_FILE, 'utf-8'));
const additionalMembers = JSON.parse(await fs.readFile(MEMBERS_FILE, 'utf-8'));
spaceDefinition[SPACE].spaceDefinition.members = spaceDefinition[SPACE].spaceDefinition.members.concat(additionalMembers);
await fs.writeFile(SPACE_DEFINITION_FILE, JSON.stringify(spaceDefinition, null, 2), 'utf-8');
await browser.close();
})();
[...]
await fs.writeFile(SPACE_DEFINITION_FILE, JSON.stringify(spaceDefinition, null, 2), 'utf-8');
await execCommand(`spaces create -f ${SPACE_DEFINITION_FILE}`);
await browser.close();
})();
const puppeteer = require("puppeteer");
const path = require('path');
const exec = require("child_process").exec;
const fs = require('fs-extra');
const MEMBERS_FILE = 'members.json';
const SPACE_DEFINITION_FILE = 'space.json';
const DWC_URL = 'https://<prefix>.<region>.hcs.cloud.sap/'; // eg https://mytenant.eu10.hcs.cloud.sap/
const DWC_PASSCODE_URL = 'https://<prefix>.authentication.<region>.hana.ondemand.com/passcode'; // eg https://mytenant.authentication.eu10.hana.ondemand.com/passcode
const SPACE = '<space ID>';
const USERNAME = '<username>'; // eg firstname.lastname@company.com
const PASSWORD = '<password>';
let page;
const getPasscode = async () => {
await page.waitForSelector('div.island > h1 + h2', {visible: true, timeout: 5000});
await page.reload();
return await page.$eval('h2', el => el.textContent);
}
const execCommand = async (command) => new Promise(async (res, rej) => {
const passcode = await getPasscode();
const cmd = `node ${path.join(process.cwd(), 'node_modules', '@sap', 'dwc-cli', 'index.js')} ${command} -H ${DWC_URL} -p ${passcode} -V`;
exec(cmd, (error, stdout, stderr) => {
if (error) {
rej({ error, stdout, stderr });
}
res({ error, stdout, stderr });
});
});
(async () => {
const browser = await puppeteer.launch({ headless: true });
page = await browser.newPage();
await page.goto(DWC_PASSCODE_URL);
await page.waitForSelector('#logOnForm', {visible: true, timeout: 5000});
if (await page.$('#logOnForm') !== null) {
await page.type('#j_username', USERNAME);
await page.type('#j_password', PASSWORD);
await page.click('#logOnFormSubmit');
}
await execCommand(`spaces read -S ${SPACE} -o ${SPACE_DEFINITION_FILE}`);
const spaceDefinition = JSON.parse(await fs.readFile(SPACE_DEFINITION_FILE, 'utf-8'));
const additionalMembers = JSON.parse(await fs.readFile(MEMBERS_FILE, 'utf-8'));
spaceDefinition[SPACE].spaceDefinition.members = spaceDefinition[SPACE].spaceDefinition.members.concat(additionalMembers);
await fs.writeFile(SPACE_DEFINITION_FILE, JSON.stringify(spaceDefinition, null, 2), 'utf-8');
await execCommand(`spaces create -f ${SPACE_DEFINITION_FILE}`);
await browser.close();
})();
Please note: With version 2022.2.0 the call to the main.js file changed. Previously, it looked like this (the dist segment got removed and main.js was renamed to index.js with 2022.2.0): const cmd = `node ${path.join(process.cwd(), 'node_modules', '@sap', 'datasphere-cli', 'dist', 'main.js')} ${command} -H ${DWC_URL} -p ${passcode}`;
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
14 | |
12 | |
11 | |
10 | |
9 | |
9 | |
9 | |
9 | |
8 | |
8 |