Skip to content

Commit 9e9cdd8

Browse files
authored
Add audit page, SQLite followup fixes, fixed side navigation, fix stdout/stderr (#208)
* Fix for insert * Fixes * Add navigation * Add all routes * Clean up navigation * Final fixes * Try reoderd * Drop settings from desktop menu * Move to nanoid * Work on positions * Move to nanoid * Break out editor page * More progress on state tests * More progress on views and panelPositions * Hook up eslint * Fin for eslint * Fixes for file panel * Some stuff working * Fix for reorder test * Drop console logs, update dark mode * Fixes for ca certs * Add new panel dropdown * Fix graph/table shortcut button spacing * No more usedebouncedstate * Fix for onblur * Fixes for format * Fix for gofmt * Switch to useDebouncedCallback * Fix for eslint * Add dashboard/export header * Make navigation a function of input pages * History basic showing * Fixes for eslint * Fix for fmt * Eval tests are failing * Fixes for tests * Start react ee tests * Bump coverage * Start react ee tests * Rename file * Fix for copyright * Fix for some more tests * Fix for test * Fixes for in-memory mode * Fix for localstorage store * Fix for new project * Attempt at supporting resize on create * http.location * Fixes for tsc * Add notification icon to stdout/stderr when present * Smaller * Delete results file before evalling * Always close file * No need for deleting results * Fixes for errors * Try using apckage managers for go * Fix for mac * Fix for error check * Continue manual install of go on mac * Fix for EOL on windows * Fix for new project on desktop * Its about the pathname * Set loading
1 parent f508b17 commit 9e9cdd8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+4528
-1330
lines changed

.eslintignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ui/scripts/*
2+
ui/state.test.js

.eslintrc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"root": true,
3+
"parser": "@typescript-eslint/parser",
4+
"plugins": [
5+
"@typescript-eslint",
6+
"jest"
7+
],
8+
"extends": [
9+
"eslint:recommended",
10+
"plugin:react/recommended",
11+
"plugin:@typescript-eslint/eslint-recommended",
12+
"plugin:@typescript-eslint/recommended",
13+
"plugin:react-hooks/recommended"
14+
],
15+
"rules": {
16+
"react/jsx-no-target-blank": 'off',
17+
"react/no-children-prop": 'off',
18+
"@typescript-eslint/no-explicit-any": 'off',
19+
'@typescript-eslint/no-unused-vars': [
20+
'error',
21+
{
22+
argsIgnorePattern: '^_',
23+
varsIgnorePattern: '^_',
24+
caughtErrorsIgnorePattern: '^_',
25+
},
26+
]
27+
}
28+
}

.github/workflows/pull_requests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ jobs:
2828
2929
- run: ./scripts/ci/prepare_linux.sh --integration-tests
3030
- run: yarn format
31+
- run: yarn eslint ui
3132
- run: yarn tsc
3233
- run: cd runner && gofmt -w .
3334
- run: yarn build-language-definitions

GOOD_FIRST_PROJECTS.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,9 @@ and dsq for these tasks to make sense.
2121
* Make sure there’s a library for every language
2222
* Figure out how to embed the library inside DataStation
2323
* More databases
24-
* IBM DB2, Neo4j, Apache Presto/Trino, Meilisearch, Apache Hive, Apache Druid, Apache Pinot
24+
* IBM DB2, Neo4j, Apache Presto/Trino, Meilisearch, Apache Hive, Apache Druid, Apache Pinot, Quickwit
2525
* Add a new supported log format
2626
* Example: logfmt
27-
* Fakegen help text
2827

2928
## Medium
3029

desktop/bridge.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron';
2+
import { RPC_ASYNC_REQUEST, RPC_ASYNC_RESPONSE } from '../shared/constants';
3+
import log from '../shared/log';
4+
import { Endpoint, IPCRendererResponse, WindowAsyncRPC } from '../shared/rpc';
5+
6+
let messageNumber = -1;
7+
8+
export function bridgeAsyncRPC() {
9+
const asyncRPC: WindowAsyncRPC = async function <
10+
Request,
11+
Response = void,
12+
EndpointT extends string = Endpoint
13+
>(resource: EndpointT, projectId: string, body: Request): Promise<Response> {
14+
const payload = {
15+
// Assign a new message number
16+
messageNumber: ++messageNumber,
17+
resource,
18+
body,
19+
projectId,
20+
};
21+
ipcRenderer.send(RPC_ASYNC_REQUEST, payload);
22+
23+
const result = await new Promise<IPCRendererResponse<Response>>(
24+
(resolve, reject) => {
25+
try {
26+
ipcRenderer.once(
27+
`${RPC_ASYNC_RESPONSE}:${payload.messageNumber}`,
28+
(e: IpcRendererEvent, response: IPCRendererResponse<Response>) =>
29+
resolve(response)
30+
);
31+
} catch (e) {
32+
reject(e);
33+
}
34+
}
35+
);
36+
37+
if (result.kind === 'error') {
38+
try {
39+
throw result.error;
40+
} catch (e) {
41+
// The result.error object isn't a real Error at this point with
42+
// prototype after going through serialization. So throw it to get
43+
// a real Error instance that has full info for logs.
44+
log.error(e);
45+
throw e;
46+
}
47+
}
48+
49+
return result.body;
50+
};
51+
52+
contextBridge.exposeInMainWorld('asyncRPC', asyncRPC);
53+
}

desktop/panel/eval.ts

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
import { execFile } from 'child_process';
22
import fs from 'fs';
33
import jsesc from 'jsesc';
4+
import circularSafeStringify from 'json-stringify-safe';
45
import { EOL } from 'os';
56
import path from 'path';
67
import { preview } from 'preview';
78
import { shape, Shape } from 'shape';
89
import { file as makeTmpFile } from 'tmp-promise';
9-
import * as uuid from 'uuid';
1010
import {
1111
Cancelled,
1212
EVAL_ERRORS,
1313
InvalidDependentPanelError,
14+
NoResultError,
1415
} from '../../shared/errors';
1516
import log from '../../shared/log';
17+
import { newId } from '../../shared/object';
1618
import { PanelBody } from '../../shared/rpc';
1719
import {
1820
ConnectorInfo,
@@ -131,9 +133,7 @@ export async function evalInSubprocess(
131133
ensureFile(path.join(CODE_ROOT, 'coverage', 'fake.cov'));
132134
args.unshift('-test.run');
133135
args.unshift('^TestRunMain$');
134-
args.unshift(
135-
'-test.coverprofile=coverage/gorunner.' + uuid.v4() + '.cov'
136-
);
136+
args.unshift('-test.coverprofile=coverage/gorunner.' + newId() + '.cov');
137137
}
138138

139139
log.info(`Launching "${base} ${args.join(' ')}"`);
@@ -189,20 +189,31 @@ export async function evalInSubprocess(
189189
}
190190
});
191191

192-
const resultMeta = fs.readFileSync(tmp.path).toString();
193-
let parsePartial = !resultMeta;
192+
const resultMeta = JSON.parse(fs.readFileSync(tmp.path).toString());
193+
let parsePartial = typeof resultMeta.preview === 'undefined';
194194
if (!parsePartial) {
195-
const rm: Partial<PanelResult> = JSON.parse(resultMeta);
195+
const rm: Partial<PanelResult> = resultMeta;
196196
// Case of existing Node.js runner
197197
return [rm, stderr];
198198
}
199199

200200
// Case of new Go runner
201201
const projectResultsFile = getProjectResultsFile(projectName);
202-
const rm: Partial<PanelResult> = parsePartialJSONFile(
203-
projectResultsFile + panel.id
204-
);
205-
return [rm, stderr];
202+
let rm: Partial<PanelResult>;
203+
try {
204+
rm = parsePartialJSONFile(projectResultsFile + panel.id);
205+
} catch (e) {
206+
if (!e.stdout) {
207+
e.stdout = resultMeta.stdout;
208+
}
209+
210+
if (e instanceof NoResultError && resultMeta.exception) {
211+
// do nothing. The underlying exception is more interesting
212+
} else {
213+
throw e;
214+
}
215+
}
216+
return [{ ...rm, ...resultMeta }, stderr];
206217
} finally {
207218
try {
208219
if (pid) {
@@ -352,20 +363,36 @@ export const makeEvalHandler = (subprocessEval?: {
352363
dispatch,
353364
subprocessEval
354365
);
355-
356-
// This is to handle "exceptions" within the runner.
357-
if (res.exception) {
358-
throw res.exception;
359-
}
360-
// The outer try-catch is to handle exceptions within this Node code
361366
} catch (e) {
362367
log.error(e);
363368
res.exception = e;
364-
if (!EVAL_ERRORS.find((ee) => ee.name === e.name) && stderr) {
365-
// Just a generic exception, we already caught all info in `stderr`, so just throw that.
366-
res.exception = stderr;
369+
if (res.exception.stdout) {
370+
res.stdout = res.exception.stdout;
371+
delete res.exception.stdout;
367372
}
368373
}
374+
375+
if (
376+
res.exception &&
377+
!EVAL_ERRORS.find((ee) => ee.name === res.exception.name) &&
378+
stderr
379+
) {
380+
// Just a generic exception, we already caught all info in `stderr`, so just store that.
381+
res.exception = new Error(res.stdout || stderr);
382+
}
383+
384+
// I'm not really sure why this is necessary but sometimes the
385+
// exception comes out in an object that can't be stringified. And
386+
// yet I don't believe there's any throwing of errors from the Go
387+
// code.
388+
if (res.exception && circularSafeStringify(res.exception) === '{}') {
389+
res.exception = {
390+
name: res.exception.name,
391+
message: res.exception.message,
392+
...res.exception,
393+
};
394+
}
395+
369396
res.lastRun = start;
370397
res.loading = false;
371398
res.elapsed = new Date().valueOf() - start.valueOf();

desktop/panel/program.test.js

Lines changed: 81 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const path = require('path');
1+
const os = require('os');
22
const { LANGUAGES } = require('../../shared/languages');
33
const {
44
InvalidDependentPanelError,
@@ -7,9 +7,6 @@ const {
77
const { getProjectResultsFile } = require('../store');
88
const fs = require('fs');
99
const { LiteralPanelInfo, ProgramPanelInfo } = require('../../shared/state');
10-
const { updateProjectHandler } = require('../store');
11-
const { CODE_ROOT } = require('../constants');
12-
const { makeEvalHandler } = require('./eval');
1310
const { inPath, withSavedPanels, RUNNERS, VERBOSE } = require('./testutil');
1411

1512
const TESTS = [
@@ -485,4 +482,84 @@ for (const subprocessName of RUNNERS) {
485482
}
486483
});
487484
});
485+
486+
describe('stdout/stderr capture', function () {
487+
test('it captures print in successful program', async () => {
488+
const pp = new ProgramPanelInfo(null, {
489+
type: 'python',
490+
content: 'print(1002)\nDM_setPanel(1)',
491+
});
492+
493+
let finished = false;
494+
const panels = [pp];
495+
await withSavedPanels(
496+
panels,
497+
async (project, dispatch) => {
498+
const savedProject = await dispatch({
499+
resource: 'getProject',
500+
projectId: project.projectName,
501+
body: {
502+
projectId: project.projectName,
503+
},
504+
});
505+
expect(savedProject.pages[0].panels[0].resultMeta.stdout).toBe(
506+
'1002' + os.EOL
507+
);
508+
finished = true;
509+
},
510+
{
511+
evalPanels: true,
512+
subprocessName,
513+
}
514+
);
515+
516+
if (!finished) {
517+
throw new Error('Callback did not finish');
518+
}
519+
});
520+
521+
test('it captures print in unsuccessful program', async () => {
522+
const pp = new ProgramPanelInfo(null, {
523+
type: 'python',
524+
content: 'print("hey there")\nraise Exception(1)',
525+
});
526+
527+
let finished = false;
528+
const panels = [pp];
529+
try {
530+
await withSavedPanels(
531+
panels,
532+
async (project, dispatch) => {
533+
finished = true;
534+
},
535+
{
536+
evalPanels: true,
537+
subprocessName,
538+
}
539+
);
540+
} catch (e) {
541+
const savedProject = await e.dispatch({
542+
resource: 'getProject',
543+
projectId: e.project.projectName,
544+
body: {
545+
projectId: e.project.projectName,
546+
},
547+
});
548+
expect(
549+
savedProject.pages[0].panels[0].resultMeta.stdout.startsWith(
550+
'hey there' + os.EOL + 'Traceback'
551+
)
552+
).toBe(true);
553+
if (e instanceof Error || e.name === 'Error') {
554+
finished = true;
555+
} else {
556+
throw e;
557+
}
558+
}
559+
560+
if (!finished) {
561+
throw new Error('Callback did not finish');
562+
}
563+
});
564+
});
488565
}

desktop/panel/testutil.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ exports.updateProject = async function (project, opts) {
6363
body: {
6464
data: page,
6565
position: i,
66+
insert: true,
6667
},
6768
},
6869
true
@@ -78,6 +79,7 @@ exports.updateProject = async function (project, opts) {
7879
body: {
7980
data: panels[j],
8081
position: j,
82+
insert: true,
8183
},
8284
},
8385
true
@@ -94,6 +96,7 @@ exports.updateProject = async function (project, opts) {
9496
body: {
9597
data: server,
9698
position: i,
99+
insert: true,
97100
},
98101
},
99102
true
@@ -109,6 +112,7 @@ exports.updateProject = async function (project, opts) {
109112
body: {
110113
data: connector,
111114
position: i,
115+
insert: true,
112116
},
113117
},
114118
true
@@ -191,6 +195,9 @@ exports.withSavedPanels = async function (
191195
dispatch
192196
);
193197
if (res.exception) {
198+
// So that callers can get access to project data if needed
199+
res.exception.project = project;
200+
res.exception.dispatch = dispatch;
194201
throw res.exception;
195202
}
196203

0 commit comments

Comments
 (0)