Skip to content

Commit

Permalink
[i18n-Audio] Add DB schemas and shared API/store stubs (#3987)
Browse files Browse the repository at this point in the history
  • Loading branch information
kofi-q authored Sep 21, 2023
1 parent 6c454a5 commit d9a9d06
Show file tree
Hide file tree
Showing 26 changed files with 438 additions and 22 deletions.
24 changes: 24 additions & 0 deletions apps/mark-scan/backend/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,27 @@ create table system_settings (
id integer primary key check (id = 1),
data text not null -- JSON blob
);

create table languages (
code text primary key
);

create table ui_strings (
language_code text primary key,
data text not null, -- JSON blob - see libs/types/UiStringTranslationsSchema
foreign key (language_code) references languages(code)
);

create table audio_clips (
id text not null,
language_code text not null,
data_base64 text not null, -- Base64-encoded audio bytes
primary key (language_code, id),
foreign key (language_code) references languages(code)
);

create table ui_string_audio_keys (
language_code text primary key,
data text not null, -- JSON blob - see libs/types/UiStringAudioKeysSchema
foreign key (language_code) references languages(code)
);
1 change: 1 addition & 0 deletions apps/mark-scan/backend/src/app.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ beforeEach(async () => {
afterEach(() => {
stateMachine.stopMachineService();
server?.close();
jest.resetAllMocks();
});

async function setUpUsbAndConfigureElection(
Expand Down
14 changes: 12 additions & 2 deletions apps/mark-scan/backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ import {
singlePrecinctSelectionFor,
} from '@votingworks/utils';

import { Usb, readBallotPackageFromUsb } from '@votingworks/backend';
import {
createUiStringsApi,
Usb,
readBallotPackageFromUsb,
} from '@votingworks/backend';
import { Logger } from '@votingworks/logging';
import { electionGeneralDefinition } from '@votingworks/fixtures';
import { useDevDockRouter } from '@votingworks/dev-dock-backend';
Expand All @@ -36,7 +40,8 @@ const debug = makeDebug('mark-scan:app-backend');

const defaultMediaMountDir = '/media';

function buildApi(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function buildApi(
auth: InsertedSmartCardAuthApi,
usb: Usb,
logger: Logger,
Expand Down Expand Up @@ -232,6 +237,11 @@ function buildApi(

stateMachine.confirmInvalidateBallot();
},

...createUiStringsApi({
logger,
store: workspace.store.getUiStringsStore(),
}),
});
}

Expand Down
26 changes: 26 additions & 0 deletions apps/mark-scan/backend/src/app.ui_strings.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import tmp from 'tmp';

import { createMockUsb, runUiStringApiTests } from '@votingworks/backend';
import { fakeLogger } from '@votingworks/logging';

import { buildMockInsertedSmartCardAuth } from '@votingworks/auth';
import { Store } from './store';
import { createWorkspace } from './util/workspace';
import { buildApi } from './app';

const store = Store.memoryStore();
const workspace = createWorkspace(tmp.dirSync().name, { store });

afterAll(() => {
workspace.reset();
});

runUiStringApiTests({
api: buildApi(
buildMockInsertedSmartCardAuth(),
createMockUsb().mock,
fakeLogger(),
workspace
),
store: store.getUiStringsStore(),
});
18 changes: 15 additions & 3 deletions apps/mark-scan/backend/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The durable datastore for configuration info.
//

import { UiStringsStore, createUiStringStore } from '@votingworks/backend';
import { Optional } from '@votingworks/basics';
import { Client as DbClient } from '@votingworks/db';
import {
Expand All @@ -21,7 +22,10 @@ const SchemaPath = join(__dirname, '../schema.sql');
* Manages a data store for imported election definition and system settings
*/
export class Store {
private constructor(private readonly client: DbClient) {}
private constructor(
private readonly client: DbClient,
private readonly uiStringsStore: UiStringsStore
) {}

getDbPath(): string {
return this.client.getDatabasePath();
Expand All @@ -31,14 +35,18 @@ export class Store {
* Builds and returns a new store whose data is kept in memory.
*/
static memoryStore(): Store {
return new Store(DbClient.memoryClient(SchemaPath));
const client = DbClient.memoryClient(SchemaPath);
const uiStringsStore = createUiStringStore(client);
return new Store(client, uiStringsStore);
}

/**
* Builds and returns a new store at `dbPath`.
*/
static fileStore(dbPath: string): Store {
return new Store(DbClient.fileClient(dbPath, SchemaPath));
const client = DbClient.fileClient(dbPath, SchemaPath);
const uiStringsStore = createUiStringStore(client);
return new Store(client, uiStringsStore);
}

/**
Expand Down Expand Up @@ -184,4 +192,8 @@ export class Store {
if (!result) return undefined;
return safeParseSystemSettings(result.data).unsafeUnwrap();
}

getUiStringsStore(): UiStringsStore {
return this.uiStringsStore;
}
}
7 changes: 5 additions & 2 deletions apps/mark-scan/backend/src/util/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,15 @@ export function constructAuthMachineState(
};
}

export function createWorkspace(root: string): Workspace {
export function createWorkspace(
root: string,
options: { store?: Store } = {}
): Workspace {
const resolvedRoot = resolve(root);
ensureDirSync(resolvedRoot);

const dbPath = join(resolvedRoot, 'mark.db');
const store = Store.fileStore(dbPath);
const store = options.store || Store.fileStore(dbPath);

return {
path: resolvedRoot,
Expand Down
24 changes: 24 additions & 0 deletions apps/mark/backend/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,27 @@ create table system_settings (
id integer primary key check (id = 1),
data text not null -- JSON blob
);

create table languages (
code text primary key
);

create table ui_strings (
language_code text primary key,
data text not null, -- JSON blob - see libs/types/UiStringTranslationsSchema
foreign key (language_code) references languages(code)
);

create table audio_clips (
id text not null,
language_code text not null,
data_base64 text not null, -- Base64-encoded audio bytes
primary key (language_code, id),
foreign key (language_code) references languages(code)
);

create table ui_string_audio_keys (
language_code text primary key,
data text not null, -- JSON blob - see libs/types/UiStringAudioKeysSchema
foreign key (language_code) references languages(code)
);
14 changes: 12 additions & 2 deletions apps/mark/backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ import {
} from '@votingworks/types';
import { isElectionManagerAuth } from '@votingworks/utils';

import { Usb, readBallotPackageFromUsb } from '@votingworks/backend';
import {
createUiStringsApi,
Usb,
readBallotPackageFromUsb,
} from '@votingworks/backend';
import { Logger } from '@votingworks/logging';
import { electionGeneralDefinition } from '@votingworks/fixtures';
import { useDevDockRouter } from '@votingworks/dev-dock-backend';
Expand All @@ -37,7 +41,8 @@ function constructAuthMachineState(
};
}

function buildApi(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function buildApi(
auth: InsertedSmartCardAuthApi,
usb: Usb,
logger: Logger,
Expand Down Expand Up @@ -139,6 +144,11 @@ function buildApi(

return ok(electionDefinition);
},

...createUiStringsApi({
logger,
store: workspace.store.getUiStringsStore(),
}),
});
}

Expand Down
22 changes: 22 additions & 0 deletions apps/mark/backend/src/app.ui_strings.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import tmp from 'tmp';

import { createMockUsb, runUiStringApiTests } from '@votingworks/backend';
import { fakeLogger } from '@votingworks/logging';

import { buildMockInsertedSmartCardAuth } from '@votingworks/auth';
import { Store } from './store';
import { createWorkspace } from './util/workspace';
import { buildApi } from './app';

const store = Store.memoryStore();
const workspace = createWorkspace(tmp.dirSync().name, { store });

runUiStringApiTests({
api: buildApi(
buildMockInsertedSmartCardAuth(),
createMockUsb().mock,
fakeLogger(),
workspace
),
store: store.getUiStringsStore(),
});
18 changes: 15 additions & 3 deletions apps/mark/backend/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The durable datastore for configuration info.
//

import { UiStringsStore, createUiStringStore } from '@votingworks/backend';
import { Client as DbClient } from '@votingworks/db';
import {
ElectionDefinition,
Expand All @@ -17,7 +18,10 @@ const SchemaPath = join(__dirname, '../schema.sql');
* Manages a data store for imported election definition and system settings
*/
export class Store {
private constructor(private readonly client: DbClient) {}
private constructor(
private readonly client: DbClient,
private readonly uiStringsStore: UiStringsStore
) {}

getDbPath(): string {
return this.client.getDatabasePath();
Expand All @@ -27,14 +31,18 @@ export class Store {
* Builds and returns a new store whose data is kept in memory.
*/
static memoryStore(): Store {
return new Store(DbClient.memoryClient(SchemaPath));
const client = DbClient.memoryClient(SchemaPath);
const uiStringsStore = createUiStringStore(client);
return new Store(client, uiStringsStore);
}

/**
* Builds and returns a new store at `dbPath`.
*/
static fileStore(dbPath: string): Store {
return new Store(DbClient.fileClient(dbPath, SchemaPath));
const client = DbClient.fileClient(dbPath, SchemaPath);
const uiStringsStore = createUiStringStore(client);
return new Store(client, uiStringsStore);
}

/**
Expand Down Expand Up @@ -139,4 +147,8 @@ export class Store {
if (!result) return undefined;
return safeParseSystemSettings(result.data).unsafeUnwrap();
}

getUiStringsStore(): UiStringsStore {
return this.uiStringsStore;
}
}
7 changes: 5 additions & 2 deletions apps/mark/backend/src/util/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@ export interface Workspace {
reset(): void;
}

export function createWorkspace(root: string): Workspace {
export function createWorkspace(
root: string,
options: { store?: Store } = {}
): Workspace {
const resolvedRoot = resolve(root);
ensureDirSync(resolvedRoot);

const dbPath = join(resolvedRoot, 'mark.db');
const store = Store.fileStore(dbPath);
const store = options.store || Store.fileStore(dbPath);

return {
path: resolvedRoot,
Expand Down
24 changes: 24 additions & 0 deletions apps/scan/backend/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,27 @@ create unique index idx_cvr_hashes ON cvr_hashes (
cvr_id_level_2_prefix,
cvr_id
);

create table languages (
code text primary key
);

create table ui_strings (
language_code text primary key,
data text not null, -- JSON blob - see libs/types/UiStringTranslationsSchema
foreign key (language_code) references languages(code)
);

create table audio_clips (
id text not null,
language_code text not null,
data_base64 text not null, -- Base64-encoded audio bytes
primary key (language_code, id),
foreign key (language_code) references languages(code)
);

create table ui_string_audio_keys (
language_code text primary key,
data text not null, -- JSON blob - see libs/types/UiStringAudioKeysSchema
foreign key (language_code) references languages(code)
);
9 changes: 8 additions & 1 deletion apps/scan/backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
} from '@votingworks/utils';
import express, { Application } from 'express';
import {
createUiStringsApi,
ExportDataError,
exportCastVoteRecordReportToUsbDrive,
ExportCastVoteRecordReportToUsbDriveError,
Expand Down Expand Up @@ -55,7 +56,8 @@ function constructAuthMachineState(
};
}

function buildApi(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function buildApi(
auth: InsertedSmartCardAuthApi,
machine: PrecinctScannerStateMachine,
workspace: Workspace,
Expand Down Expand Up @@ -367,6 +369,11 @@ function buildApi(
supportsUltrasonic(): boolean {
return machine.supportsUltrasonic();
},

...createUiStringsApi({
logger,
store: workspace.store.getUiStringsStore(),
}),
});
}

Expand Down
Loading

0 comments on commit d9a9d06

Please sign in to comment.