polyester/codecs/decode/
admin.rs1use crate::codecs::decode::enums::enum_proto_name;
4use crate::codecs::scalars::format_uint64_id;
5use crate::models::{ApiKeySummary, ApiKeysList, ResolvedAccount, ResolvedAccountsList};
6use crate::proto::auth::v1::{
7 ApiKey as ProtoApiKey, CreateApiKeyResponse, GetApiKeyResponse, ListApiKeysResponse,
8 ResolveAccountResponse, ResolvedAccount as ProtoResolvedAccount, UpdateApiKeyResponse,
9};
10
11pub fn api_key_from_proto(msg: &ProtoApiKey) -> ApiKeySummary {
12 let status = enum_proto_name(&msg.status);
13 ApiKeySummary {
14 key_id: msg.key_id.clone(),
15 label: msg.label.clone(),
16 status,
17 public_key_ed25519: hex::encode(&msg.public_key_ed25519),
18 created_at: msg.created_at.as_option().cloned(),
19 last_used_at: msg.last_used_at.as_option().cloned(),
20 updated_at: msg.updated_at.as_option().cloned(),
21 revision: msg.revision,
22 }
23}
24
25pub fn api_keys_list_from_proto(msg: &ListApiKeysResponse) -> ApiKeysList {
26 ApiKeysList {
27 keys: msg.api_keys.iter().map(api_key_from_proto).collect(),
28 }
29}
30
31pub fn api_key_from_get_proto(msg: &GetApiKeyResponse) -> Option<ApiKeySummary> {
32 msg.api_key.as_option().map(api_key_from_proto)
33}
34
35pub fn api_key_from_create_proto(msg: &CreateApiKeyResponse) -> Option<ApiKeySummary> {
36 msg.api_key.as_option().map(api_key_from_proto)
37}
38
39pub fn api_key_from_update_proto(msg: &UpdateApiKeyResponse) -> Option<ApiKeySummary> {
40 msg.api_key.as_option().map(api_key_from_proto)
41}
42
43pub fn resolved_account_from_proto(msg: &ProtoResolvedAccount) -> ResolvedAccount {
44 let username = if !msg.subaccount_label.is_empty() {
45 msg.subaccount_label.clone()
46 } else {
47 msg.root_username.clone()
48 };
49 ResolvedAccount {
50 account_id: format_uint64_id(msg.account_id),
51 username,
52 smart_account_address: msg.smart_account_address.clone(),
53 }
54}
55
56pub fn resolved_accounts_from_proto(msg: &ResolveAccountResponse) -> ResolvedAccountsList {
57 ResolvedAccountsList {
58 accounts: msg
59 .matches
60 .iter()
61 .map(resolved_account_from_proto)
62 .collect(),
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use crate::proto::auth::v1::ApiKeyStatus;
70 use buffa_types::google::protobuf::Timestamp;
71
72 #[test]
73 fn api_keys_list_maps_status() {
74 let msg = ListApiKeysResponse {
75 api_keys: vec![ProtoApiKey {
76 key_id: "key-1".into(),
77 label: "bot".into(),
78 status: ApiKeyStatus::Active.into(),
79 created_at: Timestamp {
80 seconds: 1,
81 ..Default::default()
82 }
83 .into(),
84 last_used_at: Timestamp {
85 seconds: 2,
86 ..Default::default()
87 }
88 .into(),
89 updated_at: Timestamp {
90 seconds: 3,
91 nanos: 123_456_000,
92 ..Default::default()
93 }
94 .into(),
95 revision: 9,
96 ..Default::default()
97 }],
98 ..Default::default()
99 };
100 let result = api_keys_list_from_proto(&msg);
101 assert_eq!(result.keys.len(), 1);
102 assert_eq!(result.keys[0].key_id, "key-1");
103 assert_eq!(result.keys[0].status, "ACTIVE");
104 assert_eq!(result.keys[0].created_at.as_ref().unwrap().seconds, 1);
105 assert_eq!(result.keys[0].last_used_at.as_ref().unwrap().seconds, 2);
106 assert_eq!(
107 result.keys[0].updated_at.as_ref().unwrap().nanos,
108 123_456_000
109 );
110 assert_eq!(result.keys[0].revision, 9);
111 }
112
113 #[test]
114 fn resolved_accounts_formats_id() {
115 let msg = ResolveAccountResponse {
116 matches: vec![ProtoResolvedAccount {
117 smart_account_address: "0x123".into(),
118 kind: "subaccount".into(),
119 account_id: 99,
120 ..Default::default()
121 }],
122 ..Default::default()
123 };
124 let result = resolved_accounts_from_proto(&msg);
125 assert_eq!(result.accounts.len(), 1);
126 assert_eq!(result.accounts[0].account_id, format_uint64_id(99));
127 assert_eq!(result.accounts[0].smart_account_address, "0x123");
128 }
129}