Skip to content

Commit

Permalink
feat: push objects on client sync (#81)
Browse files Browse the repository at this point in the history
  • Loading branch information
eventualbuddha authored Mar 11, 2024
1 parent 534875b commit d451fce
Show file tree
Hide file tree
Showing 22 changed files with 1,331 additions and 146 deletions.
39 changes: 38 additions & 1 deletion apps/cacvote-jx-terminal/backend/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use base64_serde::base64_serde_type;
use sqlx::postgres::PgPoolOptions;
use sqlx::{Connection, PgPool};
use tracing::Level;
use types_rs::cacvote::{JournalEntry, JurisdictionCode};
use types_rs::cacvote::{JournalEntry, JurisdictionCode, SignedObject};

use crate::config::Config;

Expand Down Expand Up @@ -81,3 +81,40 @@ pub(crate) async fn get_latest_journal_entry(
.fetch_optional(&mut *connection)
.await?)
}

pub(crate) async fn get_unsynced_objects(
executor: &mut sqlx::PgConnection,
) -> color_eyre::eyre::Result<Vec<SignedObject>> {
Ok(sqlx::query_as!(
SignedObject,
r#"
SELECT
id,
payload,
certificates,
signature
FROM objects
WHERE server_synced_at IS NULL
"#,
)
.fetch_all(&mut *executor)
.await?)
}

pub(crate) async fn mark_object_synced(
executor: &mut sqlx::PgConnection,
id: uuid::Uuid,
) -> color_eyre::eyre::Result<()> {
sqlx::query!(
r#"
UPDATE objects
SET server_synced_at = now()
WHERE id = $1
"#,
id
)
.execute(&mut *executor)
.await?;

Ok(())
}
23 changes: 23 additions & 0 deletions apps/cacvote-jx-terminal/backend/src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ pub(crate) async fn sync(
) -> color_eyre::eyre::Result<()> {
client.check_status().await?;

push_objects(executor, client).await?;
pull_journal_entries(executor, client).await?;

Ok(())
}

async fn pull_journal_entries(
executor: &mut sqlx::PgConnection,
client: &Client,
) -> color_eyre::eyre::Result<()> {
let latest_journal_entry_id = db::get_latest_journal_entry(executor)
.await?
.map(|entry| entry.id);
Expand All @@ -54,3 +64,16 @@ pub(crate) async fn sync(

Ok(())
}

async fn push_objects(
executor: &mut sqlx::PgConnection,
client: &Client,
) -> color_eyre::eyre::Result<()> {
let objects = db::get_unsynced_objects(executor).await?;
for object in objects {
let object_id = client.create_object(object).await?;
db::mark_object_synced(executor, object_id).await?;
}

Ok(())
}
17 changes: 17 additions & 0 deletions apps/cacvote-mark/backend/bin/create-object
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env node

require('esbuild-runner/register');

require('../src/bin/create-object/main')
.main(process.argv, {
stdin: process.stdin,
stdout: process.stdout,
stderr: process.stderr,
})
.then((code) => {
process.exitCode = code;
})
.catch((err) => {
console.error(err.stack);
process.exit(1);
});
1 change: 1 addition & 0 deletions apps/cacvote-mark/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"@types/uuid": "9.0.5",
"@types/xml": "^1.0.9",
"@votingworks/test-utils": "workspace:*",
"esbuild-runner": "2.2.2",
"eslint": "8.51.0",
"eslint-config-prettier": "^9.0.0",
"eslint-import-resolver-node": "^0.3.9",
Expand Down
55 changes: 55 additions & 0 deletions apps/cacvote-mark/backend/src/bin/create-object/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Buffer } from 'buffer';
import { readFile } from 'fs/promises';
import { cryptography } from '@votingworks/auth';
import { v4 } from 'uuid';
import { Readable } from 'stream';
import { unsafeParse } from '@votingworks/types';
import { join } from 'path';
import { Payload, SignedObject, UuidSchema } from '../../cacvote-server/types';
import { resolveWorkspace } from '../../workspace';

const DEV_CERTS_PATH = join(__dirname, '../../../../../../libs/auth/certs/dev');
const PRIVATE_KEY_PATH = join(DEV_CERTS_PATH, 'vx-admin-private-key.pem');
const VX_ADMIN_CERT_AUTHORITY_CERT_PATH = join(
DEV_CERTS_PATH,
'vx-admin-cert-authority-cert.pem'
);

export async function main(): Promise<void> {
const workspace = await resolveWorkspace();

interface TestObject {
name: string;
description: string;
value: number;
}

const object: TestObject = {
name: 'Test Object',
description: 'This is a test object',
value: 42,
};

const payload = new Payload(
'TestObject',
Buffer.from(JSON.stringify(object))
);

const certificatesPem = await readFile(VX_ADMIN_CERT_AUTHORITY_CERT_PATH);
const payloadBuffer = Buffer.from(JSON.stringify(payload));
const signature = await cryptography.signMessage({
message: Readable.from(payloadBuffer),
signingPrivateKey: {
source: 'file',
path: PRIVATE_KEY_PATH,
},
});
const signedObject = new SignedObject(
unsafeParse(UuidSchema, v4()),
payloadBuffer,
certificatesPem,
signature
);

console.log(await workspace.store.addObject(signedObject));
}
Loading

0 comments on commit d451fce

Please sign in to comment.