Skip to content

Commit 3a20dd5

Browse files
authored
Merge pull request #3 from dxworks/chr-puppet
Chr puppet
2 parents ff272f6 + 083b2bb commit 3a20dd5

8 files changed

Lines changed: 183 additions & 12 deletions

File tree

.eslintrc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@
2121
}
2222
],
2323
"quotes": [
24-
"error",
25-
"single",
24+
"off",
2625
{
2726
"avoidEscape": true
2827
}

.github/workflows/release.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ jobs:
3737
run: |
3838
npm install
3939
npm run build
40+
env:
41+
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
42+
4043
- name: Setup Version
4144
run: |
4245
node_modules/.bin/json -I -f package.json -e "this.version='$VERSION'"

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,4 @@ dist
118118

119119
.idea
120120
*.iml
121+
results

package-lock.json

Lines changed: 44 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,18 @@
4141
"@dxworks/ktextensions": "^0.1.0",
4242
"chalk": "^4.1.2",
4343
"commander": "^8.2.0",
44-
"puppeteer": "^13.1.3"
44+
"node-fetch": "^2.6.7",
45+
"puppeteer": "^13.1.3",
46+
"yaml": "^1.10.2"
4547
},
4648
"devDependencies": {
4749
"@types/cli-progress": "^3.9.2",
4850
"@types/dockerode": "^3.3.0",
4951
"@types/fs-extra": "^9.0.13",
5052
"@types/inquirer": "^8.1.3",
5153
"@types/jest": "^27.0.2",
52-
"@types/node": "^16.10.2",
54+
"@types/node": "^16.11.22",
55+
"@types/node-fetch": "^2.5.12",
5356
"@types/string-template": "^1.0.2",
5457
"@typescript-eslint/eslint-plugin": "^4.32.0",
5558
"@typescript-eslint/parser": "^4.32.0",

src/chr.ts

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import {Command} from 'commander'
2+
import {_package} from './utils'
3+
import fetch from 'node-fetch'
4+
import fs from 'fs'
5+
import path from 'path'
6+
import YAML from 'yaml'
7+
import puppeteer from 'puppeteer/lib/cjs/puppeteer/node-puppeteer-core'
8+
import {log} from '@dxworks/cli-common'
9+
import chalk from 'chalk'
10+
11+
export const chr = new Command()
12+
.name('chr')
13+
.description(_package.description)
14+
.argument('<url>', 'Chronos base url')
15+
.argument('<visualisationsPath>', 'Path to visualisations yaml')
16+
.option('-w, --width <width>', 'The width of the viewport', parseInt, 2000)
17+
.option('-h, --height <height>', 'The height of the viewport', parseInt, 2000)
18+
.option('-o, --output [output]', 'The path of the output file', path.resolve(process.cwd(), 'results'))
19+
.action(extractVis)
20+
21+
22+
export async function extractVis(url: string, visualisationsPath: string, options: { width: number, height: number, output: string }): Promise<void> {
23+
log.info('Starting Chronos Vis Puppet')
24+
mkdir(path.resolve(options.output))
25+
mkdir(path.resolve(options.output, 'sysmap'))
26+
mkdir(path.resolve(options.output, 'chart'))
27+
mkdir(path.resolve(options.output, 'graph'))
28+
29+
const visualizations = YAML.parse(fs.readFileSync(visualisationsPath, 'utf8')) as Visualisations
30+
31+
const projectsUrl = 'api/analysis/projects'
32+
let sysmaps = await getVisData(url, `${projectsUrl}/sysmaps`, 'analysis/system-map', ['svg#CreateSystemMap'], 'sysmap')
33+
sysmaps = sysmaps.filter(sysmap => visualizations.sysmaps.some(it => sysmap.name.match(it)))
34+
35+
const charts = (await getVisData(url, `${projectsUrl}/graphs`, 'analysis/chart/create', ['svg.charts-container'], 'chart', response => response.graphs))
36+
.filter(chart => visualizations.charts.some(it => chart.name.match(it)))
37+
38+
const graphs = (await getVisData(url, `${projectsUrl}/couplings/views`, 'analysis/coupling', ['svg#edge-bundle-graph', 'svg#force_layout'], 'graph', res => res.map((it: any) => ({
39+
name: it.name,
40+
id: it.viewId,
41+
}))))
42+
.filter(graph => visualizations.graphs.some(it => graph.name.match(it)))
43+
44+
await saveVisualisations(sysmaps, url, options)
45+
await saveVisualisations(charts, url, options)
46+
await saveVisualisations(graphs, url, options)
47+
48+
log.info(`Pictures saved at ${options.output}`)
49+
}
50+
51+
async function saveVisualisations(viss: Vis[], url: string, options: { width: number; height: number; output: string }) {
52+
const browser = await puppeteer.launch()
53+
const page = await browser.newPage()
54+
await page.setViewport({width: options.width, height: options.height})
55+
56+
for (const vis of viss) {
57+
const elements = []
58+
log.info(`Getting ${chalk.yellow(vis.name)}`)
59+
await page.goto(`${url}/${vis.url}`, {waitUntil: 'networkidle2'})
60+
for (const selector of vis.selectors) {
61+
const svg = await page.$(selector)
62+
if (svg) {
63+
elements.push(svg)
64+
}
65+
}
66+
if (elements.length) {
67+
if (elements.length == 1) {
68+
await elements[0]?.screenshot({path: path.resolve(options.output, vis.type, `${vis.name}-${vis.id}.png`)})
69+
} else {
70+
for (let i = 0; i < elements.length; i++) {
71+
await elements[i].screenshot({path: path.resolve(options.output, vis.type, `${vis.name}-${vis.id}-${i}.png`)})
72+
}
73+
}
74+
}
75+
}
76+
77+
await browser.close()
78+
}
79+
80+
function mkdir(dir: string) {
81+
if (!fs.existsSync(dir)) {
82+
fs.mkdirSync(dir)
83+
}
84+
}
85+
86+
async function getVisData(url: string,
87+
visDataUrlPath: string,
88+
visUrlPath: string,
89+
selectors: string[],
90+
type: string,
91+
visArrayGetter: (response: any) => any[] = r => r): Promise<Vis[]> {
92+
93+
const response = await fetch(`${url.removeSuffix('/')}/${visDataUrlPath}`)
94+
const responseData = await response.json()
95+
return visArrayGetter(responseData).map((vis: { id: number; name: string }) => {
96+
return {
97+
id: vis.id,
98+
name: vis.name,
99+
url: `${visUrlPath}/${vis.id}`,
100+
selectors,
101+
type,
102+
}
103+
})
104+
}
105+
106+
interface Vis {
107+
id: number
108+
name: string
109+
url: string
110+
selectors: string[]
111+
type: string
112+
}
113+
114+
interface Visualisations {
115+
charts: string[]
116+
sysmaps: string[]
117+
graphs: string[]
118+
}

src/vis-puppet.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
1+
import '@dxworks/ktextensions'
2+
13
import {Command} from 'commander'
24
import {_package} from './utils'
35
import puppeteer from 'puppeteer/lib/cjs/puppeteer/node-puppeteer-core'
6+
import {chr} from './chr'
47

58
export const mainCommand = new Command()
69
.name('vis-puppet')
10+
.alias('vis')
711
.description(_package.description)
812
.argument('<url>', 'The url of the page to load')
913
.argument('<selector>', 'The unique selector of the element to screenshot')
1014
.option('-w, --width <width>', 'The width of the viewport', parseInt, 2000)
1115
.option('-h, --height <height>', 'The height of the viewport', parseInt, 2000)
1216
.option('-o, --output <output>', 'The path of the output file')
1317
.version(_package.version, '-v, -version, --version, -V')
18+
.addCommand(chr)
1419
.action(extractVis)
1520

1621
export async function extractVis(url: string, selector: string, options: { width: number, height: number, output: string }): Promise<void> {

visualizations.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
charts:
2+
- 'OVR:.*'
3+
sysmaps:
4+
- 'OVR:.*'
5+
graphs:
6+
- '.*'

0 commit comments

Comments
 (0)