diff --git a/core/src/actions/build.rs b/core/src/actions/build.rs index 2f45b7ded..b8125a084 100644 --- a/core/src/actions/build.rs +++ b/core/src/actions/build.rs @@ -54,6 +54,7 @@ impl State { permissions: [(user.id.clone(), PermissionLevel::Update)] .into_iter() .collect(), + last_built_at: "never".to_string(), created_at: start_ts.clone(), updated_at: start_ts.clone(), ..Default::default() @@ -179,6 +180,7 @@ impl State { new_build.name = current_build.name.clone(); new_build.permissions = current_build.permissions.clone(); new_build.server_id = current_build.server_id.clone(); + new_build.last_built_at = String::new(); new_build.created_at = current_build.created_at.clone(); new_build.updated_at = start_ts.clone(); @@ -290,7 +292,8 @@ impl State { build_id, mungos::Update::Set(doc! { "version": to_bson(&build.version) - .context("failed at converting version to bson")? + .context("failed at converting version to bson")?, + "last_built_at": monitor_timestamp(), }), ) .await; diff --git a/core/src/api/deployment.rs b/core/src/api/deployment.rs index 5ab59f5fc..feeb758df 100644 --- a/core/src/api/deployment.rs +++ b/core/src/api/deployment.rs @@ -11,7 +11,7 @@ use helpers::handle_anyhow_error; use mungos::{Deserialize, Document, Serialize}; use types::{ traits::Permissioned, Deployment, DeploymentActionState, DeploymentWithContainerState, - DockerContainerState, Log, PermissionLevel, Server, + DockerContainerState, DockerContainerStats, Log, PermissionLevel, Server, }; use typeshare::typeshare; @@ -260,6 +260,20 @@ pub fn router() -> Router { }, ), ) + .route( + "/:id/stats", + get( + |Extension(state): StateExtension, + Extension(user): RequestUserExtension, + Path(deployment_id): Path| async move { + let stats = state + .get_deployment_container_stats(&deployment_id.id, &user) + .await + .map_err(handle_anyhow_error)?; + response!(Json(stats)) + }, + ), + ) } impl State { @@ -381,4 +395,20 @@ impl State { .await?; Ok(log) } + + async fn get_deployment_container_stats( + &self, + id: &str, + user: &RequestUser, + ) -> anyhow::Result { + let deployment = self + .get_deployment_check_permissions(&id, &user, PermissionLevel::Read) + .await?; + let server = self.db.get_server(&deployment.server_id).await?; + let stats = self + .periphery + .container_stats(&server, &deployment.name) + .await?; + Ok(stats) + } } diff --git a/frontend/src/components/build/tabs/Owners.tsx b/frontend/src/components/build/tabs/Owners.tsx index 06b43ba15..1ee6edd78 100644 --- a/frontend/src/components/build/tabs/Owners.tsx +++ b/frontend/src/components/build/tabs/Owners.tsx @@ -50,8 +50,8 @@ const Owners: Component<{}> = (p) => { !u.admin && u.enabled && u.username.includes(userSearch()) && - (build.permissions[getId(u)] === undefined || - build.permissions[getId(u)] === PermissionLevel.None) + (build.permissions![getId(u)] === undefined || + build.permissions![getId(u)] === PermissionLevel.None) ) ); let unsub = () => {}; @@ -111,52 +111,60 @@ const Owners: Component<{}> = (p) => { } menuStyle={{ width: "12rem" }} /> - + permission !== PermissionLevel.None + ) + .map(([user_id, _]) => user_id)} + > {(user_id) => { const u = () => getUser(user_id); - const permissions = () => build.permissions[user_id]; + const permissions = () => build.permissions![user_id]; return ( - -
- {u().username} - {user_id === getId(user()) && " ( you )"} -
- - { - client.update_user_permissions_on_target({ - user_id, - permission: permission as PermissionLevel, - target_type: PermissionsTarget.Build, - target_id: params.id, - }); - }} - position="bottom right" - /> - { - client.update_user_permissions_on_target({ - user_id, - permission: PermissionLevel.None, - target_type: PermissionsTarget.Build, - target_id: params.id, - }); - }} - > - remove - + + +
+ {u().username} + {user_id === getId(user()) && " ( you )"} +
+ + { + client.update_user_permissions_on_target({ + user_id, + permission: permission as PermissionLevel, + target_type: PermissionsTarget.Build, + target_id: params.id, + }); + }} + position="bottom right" + /> + { + client.update_user_permissions_on_target({ + user_id, + permission: PermissionLevel.None, + target_type: PermissionsTarget.Build, + target_id: params.id, + }); + }} + > + remove + +
-
+ ); }}
diff --git a/frontend/src/components/build/tabs/Tabs.tsx b/frontend/src/components/build/tabs/Tabs.tsx index b9e245f4d..239e69c25 100644 --- a/frontend/src/components/build/tabs/Tabs.tsx +++ b/frontend/src/components/build/tabs/Tabs.tsx @@ -4,7 +4,8 @@ import { useAppState } from "../../../state/StateProvider"; import { useUser } from "../../../state/UserProvider"; import { PermissionLevel } from "../../../types"; import { getId } from "../../../util/helpers"; -import Tabs, { Tab } from "../../shared/tabs/Tabs"; +import SimpleTabs from "../../shared/tabs/SimpleTabs"; +import { Tab } from "../../shared/tabs/Tabs"; import BuildConfig from "./build-config/BuildConfig"; import GitConfig from "./git-config/GitConfig"; import Owners from "./Owners"; @@ -15,13 +16,10 @@ const BuildTabs: Component<{}> = (p) => { const params = useParams(); const { user } = useUser(); const build = () => builds.get(params.id)!; - const userCanUpdate = () => - user().admin || - build().permissions[getId(user())] === PermissionLevel.Update; return ( - = (p) => { disabled={!userCanUpdate()} /> - 0}> - -

dockerhub account:

- { - setBuild( - "docker_account", - account === "none" ? undefined : account - ); - }} - position="bottom right" - disabled={!userCanUpdate()} - /> -
-
+ +

dockerhub account:

+ { + setBuild( + "docker_account", + account === "none" ? undefined : account + ); + }} + position="bottom right" + disabled={!userCanUpdate()} + /> +
); }; diff --git a/frontend/src/components/build/tabs/git-config/Git.tsx b/frontend/src/components/build/tabs/git-config/Git.tsx index bb4b546b4..860409081 100644 --- a/frontend/src/components/build/tabs/git-config/Git.tsx +++ b/frontend/src/components/build/tabs/git-config/Git.tsx @@ -42,28 +42,26 @@ const Git: Component<{}> = (p) => { disabled={!userCanUpdate()} /> - 0}> - -

github account:

- { - setBuild( - "github_account", - account === "none" ? undefined : account - ); - }} - position="bottom right" - disabled={!userCanUpdate()} - /> -
-
+ +

github account:

+ { + setBuild( + "github_account", + account === "none" ? undefined : account + ); + }} + position="bottom right" + disabled={!userCanUpdate()} + /> +
); }; diff --git a/frontend/src/components/deployment/Updates.tsx b/frontend/src/components/deployment/Updates.tsx index 716d0b226..c86db2009 100644 --- a/frontend/src/components/deployment/Updates.tsx +++ b/frontend/src/components/deployment/Updates.tsx @@ -6,6 +6,8 @@ import { useAppState } from "../../state/StateProvider"; import { combineClasses } from "../../util/helpers"; import { useParams } from "@solidjs/router"; import { Operation } from "../../types"; +import Flex from "../shared/layout/Flex"; +import Loading from "../shared/loading/Loading"; const Updates: Component<{}> = (p) => { const { ws, deployments } = useAppState(); @@ -28,26 +30,27 @@ const Updates: Component<{}> = (p) => { }); onCleanup(() => unsub()); return ( - 0 - } - > - -

updates

+ +

updates

+ + + }> {(update) => } - -
-
+
+ ); }; diff --git a/frontend/src/components/deployment/tabs/Tabs.tsx b/frontend/src/components/deployment/tabs/Tabs.tsx index 2348e25b0..94989e642 100644 --- a/frontend/src/components/deployment/tabs/Tabs.tsx +++ b/frontend/src/components/deployment/tabs/Tabs.tsx @@ -5,7 +5,7 @@ import { onCleanup, Show, } from "solid-js"; -import Tabs, { Tab } from "../../shared/tabs/Tabs"; +import { Tab } from "../../shared/tabs/Tabs"; import Config from "./config/Config"; import Log from "./log/Log"; import { useAppState } from "../../../state/StateProvider"; @@ -19,6 +19,7 @@ import { Operation, } from "../../../types"; import { client } from "../../.."; +import SimpleTabs from "../../shared/tabs/SimpleTabs"; const DeploymentTabs: Component<{}> = () => { const { deployments, ws } = useAppState(); @@ -63,7 +64,7 @@ const DeploymentTabs: Component<{}> = () => { ); return ( - = () => { const { deployment, reset, save, userCanUpdate } = useConfig(); @@ -31,10 +32,10 @@ const Config: Component<{}> = () => { return ( - = (p) => { .then(setDockerAccounts); }); return ( - 0}> - -

docker account

- - setDeployment("docker_run_args", { - docker_account: account === "none" ? undefined : account, - }) - } - position="bottom right" - disabled={!userCanUpdate()} - /> -
-
+ +

docker account

+ + setDeployment("docker_run_args", { + docker_account: account === "none" ? undefined : account, + }) + } + position="bottom right" + disabled={!userCanUpdate()} + /> +
); }; diff --git a/frontend/src/components/deployment/tabs/config/container/Image.tsx b/frontend/src/components/deployment/tabs/config/container/Image.tsx index e031c5b01..eccf5e351 100644 --- a/frontend/src/components/deployment/tabs/config/container/Image.tsx +++ b/frontend/src/components/deployment/tabs/config/container/Image.tsx @@ -21,7 +21,6 @@ const Image: Component<{}> = (p) => { placeholder="image" spellcheck={false} value={deployment.docker_run_args.image || ""} - style={{ width: userCanUpdate() ? "12rem" : undefined }} onEdit={(image) => setDeployment("docker_run_args", { image })} disabled={!userCanUpdate()} /> diff --git a/frontend/src/components/deployment/tabs/config/container/PostImage.tsx b/frontend/src/components/deployment/tabs/config/container/PostImage.tsx index cdd87d740..0f898240e 100644 --- a/frontend/src/components/deployment/tabs/config/container/PostImage.tsx +++ b/frontend/src/components/deployment/tabs/config/container/PostImage.tsx @@ -16,7 +16,6 @@ const PostImage: Component<{}> = (p) => { placeholder="post image" spellcheck={false} value={deployment.docker_run_args.post_image || ""} - style={{ width: userCanUpdate() ? "16rem" : undefined }} onEdit={(post_image) => setDeployment("docker_run_args", { post_image }) } diff --git a/frontend/src/components/deployment/tabs/config/mount-repo/Git.tsx b/frontend/src/components/deployment/tabs/config/mount-repo/Git.tsx index 6b33aa67f..e2279554e 100644 --- a/frontend/src/components/deployment/tabs/config/mount-repo/Git.tsx +++ b/frontend/src/components/deployment/tabs/config/mount-repo/Git.tsx @@ -1,4 +1,4 @@ -import { Component, createEffect, createSignal, Show } from "solid-js"; +import { Component, createEffect, createSignal } from "solid-js"; import { client } from "../../../../.."; import { combineClasses } from "../../../../../util/helpers"; import Input from "../../../../shared/Input"; @@ -42,28 +42,26 @@ const Git: Component<{}> = (p) => { disabled={!userCanUpdate()} /> - 0}> - -

github account:

- { - setDeployment( - "github_account", - account === "none" ? undefined : account - ); - }} - position="bottom right" - disabled={!userCanUpdate()} - /> -
-
+ +

github account:

+ { + setDeployment( + "github_account", + account === "none" ? undefined : account + ); + }} + position="bottom right" + disabled={!userCanUpdate()} + /> +
); }; diff --git a/frontend/src/components/home/Tree/Build.tsx b/frontend/src/components/home/Tree/Build.tsx index 6da714903..688760b27 100644 --- a/frontend/src/components/home/Tree/Build.tsx +++ b/frontend/src/components/home/Tree/Build.tsx @@ -3,8 +3,9 @@ import { Component, Show } from "solid-js"; import { useAppState } from "../../../state/StateProvider"; import { useUser } from "../../../state/UserProvider"; import { PermissionLevel } from "../../../types"; -import { combineClasses, getId } from "../../../util/helpers"; +import { combineClasses, getId, readableMonitorTimestamp } from "../../../util/helpers"; import Icon from "../../shared/Icon"; +import Flex from "../../shared/layout/Flex"; import HoverMenu from "../../shared/menu/HoverMenu"; import s from "../home.module.scss"; @@ -13,23 +14,37 @@ const Build: Component<{ id: string }> = (p) => { const { user } = useUser(); const build = () => builds.get(p.id)!; const permissionLevel = () => { - const level = build().permissions[getId(user())]; + const level = build().permissions![getId(user())]; return level ? level : PermissionLevel.None; }; + const version = () => { + return `v${build().version.major}.${build().version.minor}.${build().version.patch}` + } + const lastBuiltAt = () => { + if (build().last_built_at === "never") { + return "not built" + } else { + return readableMonitorTimestamp(build().last_built_at!); + } + } return (

{build().name}

- - } - content="you are a collaborator" - padding="0.5rem" - position="bottom right" - /> - + + + } + content="you are a collaborator" + padding="0.5rem" + position="bottom right" + /> + +
{version()}
+
{lastBuiltAt()}
+
); diff --git a/frontend/src/components/home/Updates/Update.tsx b/frontend/src/components/home/Updates/Update.tsx index bdae00615..1e96bea12 100644 --- a/frontend/src/components/home/Updates/Update.tsx +++ b/frontend/src/components/home/Updates/Update.tsx @@ -1,6 +1,6 @@ -import { Component } from "solid-js"; +import { Component, Show } from "solid-js"; import { useAppState } from "../../../state/StateProvider"; -import { Operation, Update as UpdateType } from "../../../types"; +import { Operation, Update as UpdateType, UpdateStatus } from "../../../types"; import { combineClasses, readableMonitorTimestamp, @@ -38,13 +38,18 @@ const Update: Component<{ update: UpdateType }> = (p) => { >

{name()}

-
- {operation()} -
+ +
+ {operation()} +
+ +
(in progress)
+
+
diff --git a/frontend/src/components/server/tabs/Owners.tsx b/frontend/src/components/server/tabs/Owners.tsx index b43eef01b..d7dd1794b 100644 --- a/frontend/src/components/server/tabs/Owners.tsx +++ b/frontend/src/components/server/tabs/Owners.tsx @@ -111,52 +111,60 @@ const Owners: Component<{}> = (p) => { } menuStyle={{ width: "12rem" }} /> - + permission !== PermissionLevel.None + ) + .map(([user_id, _]) => user_id)} + > {(user_id) => { const u = () => getUser(user_id); const permissions = () => server.permissions![user_id]; return ( - -
- {u().username} - {user_id === getId(user()) && " ( you )"} -
- - { - client.update_user_permissions_on_target({ - user_id, - permission: permission as PermissionLevel, - target_type: PermissionsTarget.Server, - target_id: params.id, - }); - }} - position="bottom right" - /> - { - client.update_user_permissions_on_target({ - user_id, - permission: PermissionLevel.None, - target_type: PermissionsTarget.Server, - target_id: params.id, - }); - }} - > - remove - + + +
+ {u().username} + {user_id === getId(user()) && " ( you )"} +
+ + { + client.update_user_permissions_on_target({ + user_id, + permission: permission as PermissionLevel, + target_type: PermissionsTarget.Server, + target_id: params.id, + }); + }} + position="bottom right" + /> + { + client.update_user_permissions_on_target({ + user_id, + permission: PermissionLevel.None, + target_type: PermissionsTarget.Server, + target_id: params.id, + }); + }} + > + remove + +
-
+
); }}
diff --git a/frontend/src/components/server/tabs/Tabs.tsx b/frontend/src/components/server/tabs/Tabs.tsx index c78396bef..eecb355f9 100644 --- a/frontend/src/components/server/tabs/Tabs.tsx +++ b/frontend/src/components/server/tabs/Tabs.tsx @@ -2,7 +2,8 @@ import { useParams } from "@solidjs/router"; import { Component, Show } from "solid-js"; import { useAppState } from "../../../state/StateProvider"; import { useUser } from "../../../state/UserProvider"; -import Tabs, { Tab } from "../../shared/tabs/Tabs"; +import SimpleTabs from "../../shared/tabs/SimpleTabs"; +import { Tab } from "../../shared/tabs/Tabs"; import Config from "./config/Config"; import { ConfigProvider } from "./config/Provider"; import Owners from "./Owners"; @@ -16,7 +17,7 @@ const ServerTabs: Component<{}> = (p) => { return ( - = (p) => { const { usernames } = useAppState(); const operation = () => { - return p.update.operation.replaceAll("_", " ") + if (p.update.operation === Operation.BuildBuild) { + return "build"; + } + return p.update.operation.replaceAll("_", " "); }; return ( -
- {operation()} + +
+ {operation()} +
+ +
(in progress)
+
+
+
+ {readableMonitorTimestamp(p.update.start_ts)}
-
{readableMonitorTimestamp(p.update.start_ts)}
{usernames.get(p.update.operator)}
@@ -37,4 +44,4 @@ const Update: Component<{ update: UpdateType }> = (p) => { ); }; -export default Update; \ No newline at end of file +export default Update; diff --git a/frontend/src/state/hooks.ts b/frontend/src/state/hooks.ts index 7048440e0..cb0a9ff2e 100644 --- a/frontend/src/state/hooks.ts +++ b/frontend/src/state/hooks.ts @@ -50,8 +50,12 @@ export function useUsernames() { {} ); const load = async (userID: string) => { - const username = await client.get_username(userID); - set((s) => ({ ...s, [userID]: username })); + if (userID === "github") { + set((s) => ({ ...s, [userID]: "github" })); + } else { + const username = await client.get_username(userID); + set((s) => ({ ...s, [userID]: username })); + } }; const loading: Record = {}; return { diff --git a/frontend/src/state/ws.ts b/frontend/src/state/ws.ts index b4083cb7f..9373039aa 100644 --- a/frontend/src/state/ws.ts +++ b/frontend/src/state/ws.ts @@ -40,7 +40,10 @@ function connectToWs(state: State) { }); return { - subscribe: (operations: Operation[], callback: (update: Update) => void) => { + subscribe: ( + operations: Operation[], + callback: (update: Update) => void + ) => { const listener = ({ data }: { data: string }) => { if (data === "PONG") return; if (data === "LOGGED_IN") return; @@ -99,21 +102,6 @@ async function handleMessage( const deployment = await client.get_deployment(update.target.id!); deployments.update(deployment); } - } - - // build - else if (update.operation === Operation.CreateBuild) { - const build = await client.get_build(update.target.id!); - builds.add(build); - } else if (update.operation === Operation.DeleteBuild) { - if (update.status === UpdateStatus.Complete) { - builds.delete(update.target.id!); - } - } else if (update.operation === Operation.UpdateBuild) { - if (update.status === UpdateStatus.Complete) { - const build = await client.get_build(update.target.id!); - builds.update(build); - } } else if ( [ Operation.DeployContainer, @@ -126,6 +114,23 @@ async function handleMessage( deployments.update(deployment); } + // build + else if (update.operation === Operation.CreateBuild) { + const build = await client.get_build(update.target.id!); + builds.add(build); + } else if (update.operation === Operation.DeleteBuild) { + if (update.status === UpdateStatus.Complete) { + builds.delete(update.target.id!); + } + } else if ( + [Operation.UpdateBuild, Operation.BuildBuild].includes(update.operation) + ) { + if (update.status === UpdateStatus.Complete) { + const build = await client.get_build(update.target.id!); + builds.update(build); + } + } + // server else if (update.operation === Operation.CreateServer) { const server = await client.get_server(update.target.id!); diff --git a/frontend/src/style/app.scss b/frontend/src/style/app.scss index eab522afe..04be87668 100644 --- a/frontend/src/style/app.scss +++ b/frontend/src/style/app.scss @@ -125,17 +125,20 @@ $anim-time: 500ms; .config { height: 100%; place-items: start center; - grid-template-rows: 70vh auto; + width: 100%; + // grid-template-rows: 70vh auto; } .config-items { height: fit-content; - max-height: 65vh; + // max-height: 65vh; + width: 100%; + max-width: 700px; padding: 0rem 0.5rem; } .config-item { - width: 450px; + width: 100%; flex-wrap: wrap; background-color: c.$lightgrey; box-sizing: border-box; diff --git a/frontend/src/style/index.scss b/frontend/src/style/index.scss index 2e30472ae..cdd654fce 100644 --- a/frontend/src/style/index.scss +++ b/frontend/src/style/index.scss @@ -84,6 +84,7 @@ a:focus { } input { + width: 300px; font-family: inherit; font-size: 1.05rem; color: inherit; diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 8d50f36a1..d5d7ed64b 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -17,6 +17,7 @@ export interface Build { pre_build?: Command; docker_build_args?: DockerBuildArgs; docker_account?: string; + last_built_at?: string; created_at?: string; updated_at?: string; } diff --git a/frontend/src/util/client.ts b/frontend/src/util/client.ts index f997e165d..c8f3851c8 100644 --- a/frontend/src/util/client.ts +++ b/frontend/src/util/client.ts @@ -8,6 +8,7 @@ import { Deployment, DeploymentActionState, DeploymentWithContainerState, + DockerContainerStats, Group, HistoricalStatsQuery, Log, @@ -135,6 +136,10 @@ export class Client { return this.get(`/api/deployment/${id}/log${generateQuery({ tail })}`); } + get_deployment_container_stats(id: string): Promise { + return this.get(`/api/deployment/${id}/stats`); + } + create_deployment(body: CreateDeploymentBody): Promise { return this.post("/api/deployment/create", body); } diff --git a/lib/monitor_client/src/deployment.rs b/lib/monitor_client/src/deployment.rs index fdf351041..76ea367e5 100644 --- a/lib/monitor_client/src/deployment.rs +++ b/lib/monitor_client/src/deployment.rs @@ -50,6 +50,15 @@ impl MonitorClient { .context("failed at get_deployment_container_log") } + pub async fn get_deployment_container_stats(&self, deployment_id: &str) -> anyhow::Result { + self.get( + &format!("/api/deployment/{deployment_id}/stats"), + Option::<()>::None, + ) + .await + .context("failed at get_deployment_container_log") + } + pub async fn create_deployment( &self, name: &str, diff --git a/lib/periphery_client/src/container.rs b/lib/periphery_client/src/container.rs index 30189ab72..13f7a969d 100644 --- a/lib/periphery_client/src/container.rs +++ b/lib/periphery_client/src/container.rs @@ -86,7 +86,7 @@ impl PeripheryClient { &self, server: &Server, container_name: &str, - ) -> anyhow::Result> { + ) -> anyhow::Result { self.get_json(server, &format!("/container/stats/{container_name}")) .await .context("failed to get container stats from periphery") diff --git a/lib/types/src/build.rs b/lib/types/src/build.rs index b4b445f41..cdb12eee4 100644 --- a/lib/types/src/build.rs +++ b/lib/types/src/build.rs @@ -62,7 +62,12 @@ pub struct Build { #[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))] pub docker_account: Option, - #[serde(default)] + #[serde(default, skip_serializing_if = "String::is_empty")] + #[diff(attr(#[serde(skip)]))] + #[builder(setter(skip))] + pub last_built_at: String, + + #[serde(default, skip_serializing_if = "String::is_empty")] #[diff(attr(#[serde(skip)]))] #[builder(setter(skip))] pub created_at: String, diff --git a/lib/types/src/deployment.rs b/lib/types/src/deployment.rs index 47f016296..585519190 100644 --- a/lib/types/src/deployment.rs +++ b/lib/types/src/deployment.rs @@ -68,7 +68,7 @@ pub struct Deployment { #[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))] pub repo_mount: Option, - #[serde(default)] + #[serde(default, skip_serializing_if = "String::is_empty")] #[diff(attr(#[serde(skip)]))] #[builder(setter(skip))] pub created_at: String, diff --git a/lib/types/src/group.rs b/lib/types/src/group.rs index 954945c2c..998d80c9f 100644 --- a/lib/types/src/group.rs +++ b/lib/types/src/group.rs @@ -43,7 +43,7 @@ pub struct Group { #[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))] pub groups: Vec, - #[serde(default)] + #[serde(default, skip_serializing_if = "String::is_empty")] #[diff(attr(#[serde(skip)]))] #[builder(setter(skip))] pub created_at: String, diff --git a/lib/types/src/procedure.rs b/lib/types/src/procedure.rs index 22567398b..dce3505e6 100644 --- a/lib/types/src/procedure.rs +++ b/lib/types/src/procedure.rs @@ -28,7 +28,7 @@ pub struct Procedure { #[builder(setter(skip))] pub permissions: PermissionsMap, - #[serde(default)] + #[serde(default, skip_serializing_if = "String::is_empty")] #[diff(attr(#[serde(skip)]))] #[builder(setter(skip))] pub created_at: String, diff --git a/lib/types/src/server.rs b/lib/types/src/server.rs index 8ba6119ec..5fed80539 100644 --- a/lib/types/src/server.rs +++ b/lib/types/src/server.rs @@ -65,7 +65,7 @@ pub struct Server { #[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))] pub instance_id: Option, - #[serde(default)] + #[serde(default, skip_serializing_if = "String::is_empty")] #[diff(attr(#[serde(skip)]))] #[builder(setter(skip))] pub created_at: String, diff --git a/lib/types/src/user.rs b/lib/types/src/user.rs index 80209ab1d..40fcca2b2 100644 --- a/lib/types/src/user.rs +++ b/lib/types/src/user.rs @@ -51,7 +51,7 @@ pub struct User { #[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))] pub google_id: Option, - #[serde(default)] + #[serde(default, skip_serializing_if = "String::is_empty")] #[diff(attr(#[serde(skip)]))] pub created_at: String, #[serde(default)]