Skip to content

Commit

Permalink
Merge pull request #1102 from votingworks/caro/logging/poc
Browse files Browse the repository at this point in the history
Initial application logging setup
  • Loading branch information
carolinemodic committed Oct 28, 2021
2 parents 2ee2d31 + ba6b0d7 commit 3277387
Show file tree
Hide file tree
Showing 24 changed files with 908 additions and 750 deletions.
19 changes: 19 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,24 @@ jobs:
command: |
pnpm --dir libs/fixtures test
test-logging:
executor: nodejs
resource_class: xlarge
steps:
- checkout-and-install
- run:
name: Build
command: |
pnpm --dir libs/logging build
- run:
name: Lint
command: |
pnpm --dir libs/logging lint
- run:
name: Test
command: |
pnpm --dir libs/logging test
test-hmpb-interpreter:
executor: nodejs
resource_class: xlarge
Expand Down Expand Up @@ -407,6 +425,7 @@ workflows:
- test-hmpb-interpreter
- test-integration-testing-bsd
- test-integration-testing-election-manager
- test-logging
- test-lsd
- test-module-converter-ms-sems
- test-module-scan
Expand Down
1 change: 1 addition & 0 deletions apps/election-manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
"@types/styled-components": "^5.1.7",
"@votingworks/ballot-encoder": "workspace:*",
"@votingworks/fixtures": "workspace:*",
"@votingworks/logging": "workspace:*",
"@votingworks/qrcode.react": "^1.0.2",
"@votingworks/types": "workspace:*",
"@votingworks/ui": "workspace:*",
Expand Down
26 changes: 22 additions & 4 deletions apps/election-manager/src/AppRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import React, { useState, useRef, useEffect, useCallback } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import 'normalize.css';
import { sha256 } from 'js-sha256';

import {
Logger,
LogSource,
LogEventId,
LogDispositionStandardTypes,
} from '@votingworks/logging';
import {
ElectionDefinition,
parseElection,
Expand Down Expand Up @@ -80,6 +85,7 @@ function AppRoot({
hardware,
machineConfigProvider,
}: Props): JSX.Element {
const logger = new Logger(LogSource.VxAdminApp, window.kiosk);
const printBallotRef = useRef<HTMLDivElement>(null);

const getElectionDefinition = useCallback(async (): Promise<
Expand Down Expand Up @@ -310,6 +316,13 @@ function AppRoot({

const saveElection: SaveElection = useCallback(
async (electionJSON) => {
const previousElection = electionDefinition;
if (previousElection) {
void logger.log(LogEventId.ElectionUnconfigured, 'admin', {
disposition: LogDispositionStandardTypes.Success,
previousElectionHash: previousElection.electionHash,
});
}
// we set a new election definition, reset everything
await storage.clear();
setIsOfficialResults(false);
Expand All @@ -322,6 +335,9 @@ function AppRoot({
const electionData = electionJSON;
const electionHash = sha256(electionData);
const election = safeParseElection(electionData).unsafeUnwrap();
// Temporarily bootstrap an authenticated user session. This will be removed
// once we have a full story for how to bootstrap the auth process.
bootstrapAuthenticatedAdminSession();

setElectionDefinition({
electionData,
Expand All @@ -331,20 +347,22 @@ function AppRoot({

const newConfiguredAt = new Date().toISOString();
setConfiguredAt(newConfiguredAt);
// Temporarily bootstrap an authenticated user session. This will be removed
// once we have a full story for how to bootstrap the auth process.
bootstrapAuthenticatedAdminSession();

await storage.set(configuredAtStorageKey, newConfiguredAt);
await storage.set(electionDefinitionStorageKey, {
election,
electionData,
electionHash,
});
await logger.log(LogEventId.ElectionConfigured, 'admin', {
disposition: LogDispositionStandardTypes.Success,
newElectionHash: electionHash,
});
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[
electionDefinition,
storage,
setIsOfficialResults,
setCastVoteRecordFiles,
Expand Down
1 change: 1 addition & 0 deletions apps/election-manager/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
{ "path": "../../libs/test-utils" },
{ "path": "../../libs/types" },
{ "path": "../../libs/utils" },
{ "path": "../../libs/logging" },
{ "path": "../../libs/ui" }
]
}
155 changes: 78 additions & 77 deletions libs/@types/kiosk-browser/kiosk-browser.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
declare namespace KioskBrowser {
export interface BatteryInfo {
discharging: boolean
level: number // Number between 0–1
discharging: boolean;
level: number; // Number between 0–1
}

export type PrintSides =
Expand All @@ -22,57 +22,57 @@ declare namespace KioskBrowser {
* that a right-side up portrait sheet flipped over on the short edge remains
* right-side up, i.e. a bound-at-the-top ring binder.
*/
| 'two-sided-short-edge'
| 'two-sided-short-edge';

export interface PrintOptions {
deviceName?: string
paperSource?: string
copies?: number
sides?: PrintSides
deviceName?: string;
paperSource?: string;
copies?: number;
sides?: PrintSides;
}

export interface PrinterInfo {
// Docs: http://electronjs.org/docs/api/structures/printer-info
description: string
isDefault: boolean
name: string
status: number
description: string;
isDefault: boolean;
name: string;
status: number;
// Added via kiosk-browser
connected: boolean
options?: { [key: string]: string }
connected: boolean;
options?: { [key: string]: string };
}

export interface Device {
locationId: number
vendorId: number
productId: number
deviceName: string
manufacturer: string
serialNumber: string
deviceAddress: number
locationId: number;
vendorId: number;
productId: number;
deviceName: string;
manufacturer: string;
serialNumber: string;
deviceAddress: number;
}

export interface UsbDrive {
deviceName: string
mountPoint?: string
deviceName: string;
mountPoint?: string;
}

export interface SaveAsOptions {
title?: string
defaultPath?: string
buttonLabel?: string
filters?: FileFilter[]
title?: string;
defaultPath?: string;
buttonLabel?: string;
filters?: FileFilter[];
}

export interface MakeDirectoryOptions {
recursive?: boolean
mode?: number
recursive?: boolean;
mode?: number;
}

export interface FileFilter {
// Docs: http://electronjs.org/docs/api/structures/file-filter
extensions: string[]
name: string
extensions: string[];
name: string;
}

export enum FileSystemEntryType {
Expand All @@ -86,120 +86,121 @@ declare namespace KioskBrowser {
}

export interface FileSystemEntry {
readonly name: string
readonly path: string
readonly type: FileSystemEntryType
readonly size: number
readonly mtime: Date
readonly atime: Date
readonly ctime: Date
readonly name: string;
readonly path: string;
readonly type: FileSystemEntryType;
readonly size: number;
readonly mtime: Date;
readonly atime: Date;
readonly ctime: Date;
}

export interface FileWriter {
/**
* Writes a chunk to the file. May be called multiple times. Data will be
* written in the order of calls to `write`.
*/
write(data: Uint8Array | string): Promise<void>
write(data: Uint8Array | string): Promise<void>;

/**
* Finishes writing to the file and closes it. Subsequent calls to `write`
* will fail. Resolves when the file is successfully closed.
*/
end(): Promise<void>
end(): Promise<void>;

filename: string
filename: string;
}

export interface SetClockParams {
isoDatetime: string
IANAZone: string
isoDatetime: string;
IANAZone: string;
}

export interface TotpInfo {
isoDatetime: string
code: string
isoDatetime: string;
code: string;
}

export interface SignParams {
signatureType: string
payload: string
signatureType: string;
payload: string;
}

export interface Kiosk {
print(options?: PrintOptions): Promise<void>
getPrinterInfo(): Promise<PrinterInfo[]>
print(options?: PrintOptions): Promise<void>;
getPrinterInfo(): Promise<PrinterInfo[]>;

/**
* Prints the current page to PDF and resolves with the PDF file bytes.
*/
printToPDF(): Promise<Uint8Array>
printToPDF(): Promise<Uint8Array>;
log(message: string): Promise<void>;

getBatteryInfo(): Promise<BatteryInfo>
devices: import('rxjs').Observable<Iterable<Device>>
printers: import('rxjs').Observable<Iterable<PrinterInfo>>
quit(): void
getBatteryInfo(): Promise<BatteryInfo>;
devices: import('rxjs').Observable<Iterable<Device>>;
printers: import('rxjs').Observable<Iterable<PrinterInfo>>;
quit(): void;

/**
* Opens a Save Dialog to allow the user to choose a destination for a file.
* Once chosen, resolves with a handle to the file to write data to it.
*/
saveAs(options?: SaveAsOptions): Promise<FileWriter | undefined>
saveAs(options?: SaveAsOptions): Promise<FileWriter | undefined>;

/**
* Writes a file to a specified file path
*/
writeFile(path: string): Promise<FileWriter>
writeFile(path: string, content: Uint8Array | string): Promise<void>
writeFile(path: string): Promise<FileWriter>;
writeFile(path: string, content: Uint8Array | string): Promise<void>;

/*
* Creates a directory at the specified path.
*/
makeDirectory(path: string, options?: MakeDirectoryOptions): Promise<void>
makeDirectory(path: string, options?: MakeDirectoryOptions): Promise<void>;

// USB sticks
getUsbDrives(): Promise<UsbDrive[]>
mountUsbDrive(device: string): Promise<void>
unmountUsbDrive(device: string): Promise<void>
getUsbDrives(): Promise<UsbDrive[]>;
mountUsbDrive(device: string): Promise<void>;
unmountUsbDrive(device: string): Promise<void>;

/**
* Writes a file to a specified file path
*/
writeFile(path: string): Promise<FileWriter>
writeFile(path: string, content: Uint8Array | string): Promise<void>
writeFile(path: string): Promise<FileWriter>;
writeFile(path: string, content: Uint8Array | string): Promise<void>;

/**
* Creates a directory at the specified path.
*/
makeDirectory(path: string, options?: MakeDirectoryOptions): Promise<void>
makeDirectory(path: string, options?: MakeDirectoryOptions): Promise<void>;

/**
* Reads the list of files at a specified directory path
*/
getFileSystemEntries(path: string): Promise<FileSystemEntry[]>
getFileSystemEntries(path: string): Promise<FileSystemEntry[]>;

/**
* Reads a file from a specified path
*/
readFile(path: string): Promise<Uint8Array>
readFile(path: string, encoding: string): Promise<string>
readFile(path: string): Promise<Uint8Array>;
readFile(path: string, encoding: string): Promise<string>;

// storage
storage: {
set(key: string, value: unknown): Promise<void>
get(key: string): Promise<unknown | undefined>
remove(key: string): Promise<void>
clear(): Promise<void>
}
set(key: string, value: unknown): Promise<void>;
get(key: string): Promise<unknown | undefined>;
remove(key: string): Promise<void>;
clear(): Promise<void>;
};

setClock(params: SetClockParams): Promise<void>
setClock(params: SetClockParams): Promise<void>;

totp: {
get(): Promise<TotpInfo|undefined>
}
get(): Promise<TotpInfo | undefined>;
};

sign(params: SignParams): Promise<string>
sign(params: SignParams): Promise<string>;
}
}

declare var kiosk: KioskBrowser.Kiosk | undefined
declare var kiosk: KioskBrowser.Kiosk | undefined;
3 changes: 3 additions & 0 deletions libs/logging/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
build
coverage
jest.config.js
6 changes: 6 additions & 0 deletions libs/logging/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"parserOptions": {
"project": "./tsconfig.test.json"
},
"extends": ["plugin:vx/recommended"]
}
1 change: 1 addition & 0 deletions libs/logging/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/lib
Loading

0 comments on commit 3277387

Please sign in to comment.