From 890ed91099a043b73d9516ec0bdfc0fd1ab4c59a Mon Sep 17 00:00:00 2001 From: Fiona Weber Date: Mon, 22 Apr 2024 10:14:14 +0000 Subject: [PATCH] WIP: Add more fields to tasks --- Cargo.lock | 140 ++++++++++++++++++++++++++--- README.md | 88 ++++++++++++++++++ dashboard/package-lock.json | 36 ++++++++ dashboard/package.json | 3 + dashboard/src/components/icons.tsx | 12 +++ dashboard/src/components/menu.tsx | 2 +- dashboard/src/components/tag.tsx | 2 +- dashboard/src/components/task.tsx | 43 +++++++-- dashboard/src/components/tasks.tsx | 15 +++- dashboard/src/components/xterm.tsx | 4 +- vicky/Cargo.toml | 1 + vicky/src/bin/vicky/auth.rs | 11 ++- vicky/src/bin/vicky/main.rs | 2 +- vicky/src/bin/vicky/tasks.rs | 14 ++- vicky/src/lib/documents/mod.rs | 16 +++- 15 files changed, 362 insertions(+), 27 deletions(-) create mode 100644 README.md create mode 100644 dashboard/src/components/icons.tsx diff --git a/Cargo.lock b/Cargo.lock index 30b7b7f..b727216 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,6 +61,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.3.2" @@ -659,6 +674,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.48.1", +] + [[package]] name = "cipher" version = "0.4.4" @@ -1296,6 +1326,29 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "iana-time-zone" +version = "0.1.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "idna" version = "0.4.0" @@ -1677,7 +1730,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.48.1", ] [[package]] @@ -2993,6 +3046,7 @@ dependencies = [ "async-trait", "aws-config", "aws-sdk-s3", + "chrono", "clap", "env_logger", "etcd-client", @@ -3182,7 +3236,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets", + "windows-targets 0.48.1", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", ] [[package]] @@ -3191,7 +3254,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.1", ] [[package]] @@ -3200,13 +3263,28 @@ version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] @@ -3215,42 +3293,84 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "winnow" version = "0.5.15" diff --git a/README.md b/README.md new file mode 100644 index 0000000..8e9e2a3 --- /dev/null +++ b/README.md @@ -0,0 +1,88 @@ +# Vicky + +Vicky consists of three services: +- Vicky itself +- Dashboard (The frontend) +- Vicky runner + +## Dev Environment + +### Requirements + +For a dev environment we need the following services: +- S3 endpoint +- OIDC endpoint +- etcd + +There is a Dockerfile and a nix based devenv supplied to run those. + +For vicky itself we ship a nix flake that provides all the dependencies. + +### Dashboard + +``` +cd dashboard +npm start +``` + +Now the frontend listens at `http://localhost:1234/tasks` + +### Vicky + +Example Rocket.toml for **dev environments**: +```toml +[default] + +machines = [ + "abc1234" +] + +[default.etcd_config] +endpoints = [ "http://localhost:2379" ] + +[default.s3_config] +endpoint = "http://localhost:9000" +access_key_id = "minio" +secret_access_key = "aichudiKohr6aithi4ahh3aeng2eL7xo" +region = "us-east-1" +log_bucket = "vicky-logs" + +[default.oauth.github] +provider = "GitHub" +client_id = "CHANGEME" +client_secret = "CHANGEME" +redirect_uri = "http://localhost:1234/api/auth/callback/github" + + +[default.users.sdinkleberg] +full_name = "Sheldon Dinkleberg" +role = "admin" +``` + +Now you can start the service running: +``` +cargo run --bin vicky +``` + +## Create a task + +Tasks always have a display name and a `flake_ref`. +The `flake_ref` is a reference to a Nix flake that runs the code. + +Example request: +``` + curl -s --request POST \ + --url http://127.0.0.1:1234/api/tasks/ \ + --header "Authorization: abc1234" \ + --header 'Content-Type: application/json' \ + --data '{ + "display_name": "ExampleTask Turn on coffee machine", + "locks": [], + "flake_ref": { + "flake": "github:wobcom/cosmo#generate-certs", + "args": [] + } + }' +``` + + diff --git a/dashboard/package-lock.json b/dashboard/package-lock.json index 060c8f3..3175518 100644 --- a/dashboard/package-lock.json +++ b/dashboard/package-lock.json @@ -9,6 +9,9 @@ "version": "0.0.0", "license": "MIT", "dependencies": { + "@fortawesome/free-regular-svg-icons": "^6.5.1", + "@fortawesome/free-solid-svg-icons": "^6.5.1", + "@rsuite/icons": "^1.0.3", "@uidotdev/usehooks": "^2.4.1", "axios": "^1.5.0", "parcel": "^2.9.3", @@ -186,6 +189,39 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.1.tgz", + "integrity": "sha512-GkWzv+L6d2bI5f/Vk6ikJ9xtl7dfXtoRu3YGE6nq0p/FFqA1ebMOAWg3XgRyb0I6LYyYkiAo+3/KrwuBp8xG7A==", + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-regular-svg-icons": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.5.1.tgz", + "integrity": "sha512-m6ShXn+wvqEU69wSP84coxLbNl7sGVZb+Ca+XZq6k30SzuP3X4TfPqtycgUh9ASwlNh5OfQCd8pDIWxl+O+LlQ==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.5.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.1.tgz", + "integrity": "sha512-S1PPfU3mIJa59biTtXJz1oI0+KAXW6bkAb31XKhxdxtuXDiUIFsih4JR1v5BbxY7hVHsD1RKq+jRkVRaf773NQ==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.5.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@juggle/resize-observer": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", diff --git a/dashboard/package.json b/dashboard/package.json index ac04f68..b9dd97d 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -15,6 +15,9 @@ "author": "Johann Wagner ", "license": "MIT", "dependencies": { + "@fortawesome/free-regular-svg-icons": "^6.5.1", + "@fortawesome/free-solid-svg-icons": "^6.5.1", + "@rsuite/icons": "^1.0.3", "@uidotdev/usehooks": "^2.4.1", "axios": "^1.5.0", "parcel": "^2.9.3", diff --git a/dashboard/src/components/icons.tsx b/dashboard/src/components/icons.tsx new file mode 100644 index 0000000..43990f5 --- /dev/null +++ b/dashboard/src/components/icons.tsx @@ -0,0 +1,12 @@ +const FaSvgIcon = ({ faIcon, ...rest }) => { + const { width, height, svgPathData } = faIcon; + return ( + + + + ); +}; + +export { + FaSvgIcon +} diff --git a/dashboard/src/components/menu.tsx b/dashboard/src/components/menu.tsx index 6f2b9b8..0129edb 100644 --- a/dashboard/src/components/menu.tsx +++ b/dashboard/src/components/menu.tsx @@ -52,4 +52,4 @@ const Menu = () => { export { Menu -} \ No newline at end of file +} diff --git a/dashboard/src/components/tag.tsx b/dashboard/src/components/tag.tsx index 80f50a5..4107ce7 100644 --- a/dashboard/src/components/tag.tsx +++ b/dashboard/src/components/tag.tsx @@ -58,4 +58,4 @@ const TaskTag = (props: TaskTagProps) => { export { TaskTag, -} \ No newline at end of file +} diff --git a/dashboard/src/components/task.tsx b/dashboard/src/components/task.tsx index 5a728dd..be7835e 100644 --- a/dashboard/src/components/task.tsx +++ b/dashboard/src/components/task.tsx @@ -1,11 +1,21 @@ -import { Badge, Panel, Stack, Tag } from "rsuite"; -import { ITask } from "../services/api" +import { Badge, Panel, Stack, Tag, Grid, Col, IconButton, Timeline } from "rsuite"; +import { ITask } from "../services/api"; import { Terminal } from "./xterm"; import * as s from "./task.module.css"; import { useMemo } from "react"; import { TaskTag } from "./tag"; +import { Icon } from '@rsuite/icons'; +import { FaSvgIcon } from "./icons"; +import * as faPen from '@fortawesome/free-solid-svg-icons/faPen'; +import * as faCalendarPlus from '@fortawesome/free-solid-svg-icons/faCalendarPlus'; +import * as faCalendarCheck from '@fortawesome/free-solid-svg-icons/faCalendarCheck'; +import * as faBusinessTime from '@fortawesome/free-solid-svg-icons/faBusinessTime'; +import * as faCarBurst from '@fortawesome/free-solid-svg-icons/faCarBurst'; +import * as faStop from '@fortawesome/free-solid-svg-icons/faStop'; +import * as faAnglesUp from '@fortawesome/free-solid-svg-icons/faAnglesUp'; + type TaskProps = { task: ITask } @@ -16,9 +26,13 @@ const Task = (props: TaskProps) => { return ( -

{task.display_name}

+ { task.parent ? ( + +

{task.display_name}

+
+ ) :

{task.display_name}

} - + { task.locks.map(lock => { return ( @@ -31,6 +45,24 @@ const Task = (props: TaskProps) => { + + {task.author ? {task.author}{ task.creator ? " via " + task.creator : null } : null} + {task.created ? Created {new Date(task.created).toLocaleString()} : null } + {task.started ? Started {new Date(task.started).toLocaleString()} : null } + {task.closed ? + { task.status == "SUCCESS" ? + +" Finished" + : task.status == "FAILED" ? + +" Failed" + : + +" Closed" + } + {new Date(task.created).toLocaleString() + } : null } + + + {task.parent ? }>View Parent : null} +
) @@ -38,4 +70,5 @@ const Task = (props: TaskProps) => { export { Task -} \ No newline at end of file +} + diff --git a/dashboard/src/components/tasks.tsx b/dashboard/src/components/tasks.tsx index 02ae0e8..0c56089 100644 --- a/dashboard/src/components/tasks.tsx +++ b/dashboard/src/components/tasks.tsx @@ -1,6 +1,6 @@ import { useEffect, useMemo, useState } from "react"; import { Link, useParams } from "react-router-dom" -import { Col, Grid, List, Panel, Row, Stack, Tag } from "rsuite" +import { Col, Grid, List, Panel, Row, Stack, Tag, IconButton } from "rsuite" import { ITask, useAPI } from "../services/api"; import { TaskTag } from "./tag"; import { Task } from "./task"; @@ -9,6 +9,10 @@ import { Terminal } from "./xterm" import * as s from "./tasks.module.css"; import { useTask, useTasks } from "../hooks/useTasks"; +import { Icon } from '@rsuite/icons'; +import { FaSvgIcon } from "./icons"; +import * as faPlus from '@fortawesome/free-solid-svg-icons/faPlus'; + const Tasks = () => { const { taskId } = useParams(); @@ -32,7 +36,14 @@ const Tasks = () => { {t.display_name} - + + { + t.creator ? ( + {t.creator} + ): null + } + + diff --git a/dashboard/src/components/xterm.tsx b/dashboard/src/components/xterm.tsx index cc6a2f4..6775a34 100644 --- a/dashboard/src/components/xterm.tsx +++ b/dashboard/src/components/xterm.tsx @@ -55,11 +55,11 @@ const Terminal = (props: TerminalProps) => { return ( -
setRef(ref)}>
+
setRef(ref)}>
) } export { Terminal -} \ No newline at end of file +} diff --git a/vicky/Cargo.toml b/vicky/Cargo.toml index c952e82..27184b5 100644 --- a/vicky/Cargo.toml +++ b/vicky/Cargo.toml @@ -24,6 +24,7 @@ uuid = { version="1.4.1", features = ["fast-rng", "v4", "serde"] } rocket = { version="=0.5.0-rc.3", features = ["json", "secrets"] } rocket_oauth2 = "0.5.0-rc.2" reqwest = { version="0.11.20", features = ["json"]} +chrono = { version="0.4.31", features = ["serde"]} [[bin]] name = "vicky" diff --git a/vicky/src/bin/vicky/auth.rs b/vicky/src/bin/vicky/auth.rs index 0fa16f3..99588f4 100644 --- a/vicky/src/bin/vicky/auth.rs +++ b/vicky/src/bin/vicky/auth.rs @@ -23,7 +23,7 @@ pub struct User { } pub struct Machine { - + pub name: String } #[rocket::async_trait] @@ -76,11 +76,16 @@ impl<'r> request::FromRequest<'r> for Machine { .expect("request Config"); if let Some(auth_header) = request.headers().get_one("Authorization") { - let cfg_user = config.machines.iter().find(|x| *x == auth_header); + let cfg_user = config.machines.iter().find_map(|(name, key)| match key == auth_header { + true => Some(name), + _ => None + }); match cfg_user { Some(_) => { - return request::Outcome::Success(Machine {}) + return request::Outcome::Success(Machine { + name: cfg_user.unwrap().to_string(), + }) }, None => { return request::Outcome::Failure((Status::Forbidden, ())) diff --git a/vicky/src/bin/vicky/main.rs b/vicky/src/bin/vicky/main.rs index f8a6213..48f8e9b 100644 --- a/vicky/src/bin/vicky/main.rs +++ b/vicky/src/bin/vicky/main.rs @@ -55,7 +55,7 @@ pub struct S3Config { #[derive(Deserialize)] pub struct Config { users: HashMap, - machines: Vec, + machines: HashMap, etcd_config: EtcdConfig, s3_config: S3Config, diff --git a/vicky/src/bin/vicky/tasks.rs b/vicky/src/bin/vicky/tasks.rs index 9e37708..114141b 100644 --- a/vicky/src/bin/vicky/tasks.rs +++ b/vicky/src/bin/vicky/tasks.rs @@ -1,3 +1,4 @@ +use chrono::offset::Utc; use etcd_client::{Client}; use rocket::{get, post, State, serde::json::Json}; use serde::{Deserialize, Serialize}; @@ -8,7 +9,6 @@ use std::time; use tokio::sync::broadcast::{error::{TryRecvError}, self}; use rocket::{http::Status}; - use crate::{auth::{User, Machine}, errors::AppError, events::GlobalEvent}; @@ -16,6 +16,10 @@ use crate::{auth::{User, Machine}, errors::AppError, events::GlobalEvent}; #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct RoTaskNew { display_name: String, + #[serde(default)] + author: String, + #[serde(default)] + parent: String, flake_ref: FlakeRef, locks: Vec, } @@ -142,6 +146,7 @@ pub async fn tasks_claim(etcd: &State, global_events: &State { let mut task = etcd.get_task(next_task.id).await?.ok_or(AppError::HttpError(Status::NotFound))?; task.status = TaskStatus::RUNNING; + task.started = Some(Utc::now()); etcd.put_task(&task).await?; global_events.send(GlobalEvent::TaskUpdate { uuid: task.id })?; Ok(Json(Some(task))) @@ -158,6 +163,7 @@ pub async fn tasks_finish(id: String, finish: Json, etcd: &State, etcd: &State, global_event let task_manifest = Task { id: task_uuid, status: TaskStatus::NEW, + created: Some(Utc::now()), + started: None, + closed: None, + author: task.author.clone(), + parent: task.parent.clone(), + creator: _machine.name, locks: task.locks.clone(), display_name: task.display_name.clone(), flake_ref: FlakeRef { flake: task.flake_ref.flake.clone(), args: task.flake_ref.args.clone() }, diff --git a/vicky/src/lib/documents/mod.rs b/vicky/src/lib/documents/mod.rs index 1f2401b..dddc1a3 100644 --- a/vicky/src/lib/documents/mod.rs +++ b/vicky/src/lib/documents/mod.rs @@ -2,6 +2,8 @@ use async_trait::async_trait; use etcd_client::GetOptions; use serde::{Serialize, Deserialize}; +use chrono::DateTime; +use chrono::offset::Utc; use uuid::Uuid; @@ -50,6 +52,18 @@ pub struct Task { pub id: Uuid, pub display_name: String, pub status: TaskStatus, + #[serde(default)] + pub created: Option>, + #[serde(default)] + pub started: Option>, + #[serde(default)] + pub closed: Option>, + #[serde(default)] + pub author: String, + #[serde(default)] + pub creator: String, + #[serde(default)] + pub parent: String, pub locks: Vec, pub flake_ref: FlakeRef, } @@ -84,4 +98,4 @@ impl DocumentClient for etcd_client::Client { kv.put_yaml(key, &task, None).await?; Ok(()) } -} \ No newline at end of file +}