Skip to content

Commit

Permalink
Add Group & GroupAuditLogs model & query (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
ShayBox authored Feb 4, 2024
1 parent 539cc7d commit 74054bb
Show file tree
Hide file tree
Showing 10 changed files with 239 additions and 12 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ serde = { version = "1", features = ["derive"] }
serde_with = { version = "3", features = ["time_0_3"] }
time = { version = "0.3", default-features = false, features = ["macros", "serde-well-known"] }
serde_json = { version = "1" }
strum = { version = "0.25", features = ["derive"] }
strum = { version = "0.26", features = ["derive"] }

# API client specifics
racal = "0.3"
Expand All @@ -40,7 +40,7 @@ percent-encoding = { version = "2", optional = true }
base64 = { version = "0.21", optional = true }

async-trait = { version = "0.1", optional = true }
# either = { version = "1", features = ["serde"] }
either = { version = "1", features = ["serde"] }
url = { version = "2", features = ["serde"] }
[dependencies.reqwest]
optional = true
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ There's quite a bit missing, PRs are welcome to improve the situation, even beyo
| None | Favorites | Eventual implementation |
| None | Files | None |
| Partial | Friends | Implementation soon |
| None | Groups | None, at least in the near term |
| Partial | Groups | More testing |
| None | Invites | Listing invites only |
| Partial | Instances | Implementation soon |
| None | Notifications | Eventual implementation |
Expand Down
20 changes: 12 additions & 8 deletions src/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,11 @@ macro_rules! add_id {
}

add_id!(Avatar);
add_id!(User);
add_id!(Group);
add_id!(Instance);
add_id!(World);
add_id!(UnityPackage);
add_id!(User);
add_id!(World);

/// Offline or the id of the world or whatever type T is
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
Expand Down Expand Up @@ -137,14 +138,16 @@ impl<T> OfflineOrPrivateOr<T> {
pub enum Any {
/// An avatar ID
Avatar(Avatar),
/// An user ID
User(User),
/// An group ID
Group(Group),
/// An instance ID
Instance(Instance),
/// A world ID
World(World),
/// An ID for an Unity package
UnityPackage(UnityPackage),
/// An user ID
User(User),
/// A world ID
World(World),
}

impl AsRef<str> for Any {
Expand All @@ -153,10 +156,11 @@ impl AsRef<str> for Any {
fn as_ref(&self) -> &str {
match self {
Self::Avatar(v) => v.as_ref(),
Self::User(v) => v.as_ref(),
Self::Group(v) => v.as_ref(),
Self::Instance(v) => v.as_ref(),
Self::World(v) => v.as_ref(),
Self::UnityPackage(v) => v.as_ref(),
Self::User(v) => v.as_ref(),
Self::World(v) => v.as_ref(),
}
}
}
Expand Down
116 changes: 116 additions & 0 deletions src/model/groups.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
use either::Either;
use serde::{Deserialize, Serialize};

use crate::id::User;

#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
/// Details about a VRC group
pub struct Group {
/// The unique identifier for the group.
pub id: String,
/// The name of the group.
pub name: String,
/// The short code associated with the group.
pub short_code: String,
/// The discriminator for the group.
pub discriminator: String,
/// The description of the group.
pub description: String,
/// The unique identifier for the group's icon.
pub icon_id: String,
/// The URL of the group's icon.
pub icon_url: String,
/// The unique identifier for the group's banner.
pub banner_id: String,
/// The URL of the group's banner.
pub banner_url: String,
/// The privacy setting of the group.
pub privacy: String,
/// The unique identifier of the owner of the group.
pub owner_id: String,
/// The rules associated with the group.
pub rules: String,
/// The list of links associated with the group.
pub links: Vec<String>,
/// The list of languages associated with the group.
pub languages: Vec<String>,
/// The count of members in the group.
pub member_count: i64,
/// The times tamp when the member count was last synchronized.
pub member_count_synced_at: String,
/// Indicates whether the group is verified.
pub is_verified: bool,
/// The join state of the group.
pub join_state: String,
// pub tags: Vec<Value>,
// pub galleries: Vec<Value>,
/// The time stamp when the group was created.
pub created_at: String,
/// The count of online members in the group.
pub online_member_count: i64,
/// The membership status of the user.
pub membership_status: String,
}

#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
/// Represents a collection of group audit logs.
pub struct GroupAuditLogs {
/// The list of group audit logs.
pub results: Vec<GroupAuditLog>,
/// The total count of audit logs.
pub total_count: u32,
/// Indicates whether there are more audit logs.
pub has_next: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
/// Represents a single group audit log entry.
pub struct GroupAuditLog {
/// The unique identifier for the audit log entry.
pub id: String,
/// The time stamp when the audit log entry was created.
#[serde(rename = "created_at")]
pub created_at: String,
/// The unique identifier of the group associated with the audit log.
pub group_id: String,
/// The unique identifier of the actor who performed the action.
pub actor_id: User,
/// The display name of the actor.
pub actor_displayname: Option<String>,
/// The unique identifier of the target of the action.
pub target_id: Option<User>,
/// The type of event captured in the audit log.
pub event_type: String,
/// The description of the event captured in the audit log.
pub description: String,
/// Additional data associated with the audit log entry.
pub data: GroupAuditLogData,
}

#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
/// Represents additional data associated with a group audit log entry.
pub struct GroupAuditLogData {
/// The description change associated with the audit log entry.
#[serde(default, with = "either::serde_untagged_optional")]
pub description: Option<Either<GroupAuditLogDataChange<String>, String>>,
/// The join state change associated with the audit log entry.
#[serde(default, with = "either::serde_untagged_optional")]
pub join_state: Option<Either<GroupAuditLogDataChange<String>, String>>,
/// The order change associated with the audit log entry.
#[serde(default, with = "either::serde_untagged_optional")]
pub order: Option<Either<GroupAuditLogDataChange<u32>, u32>>,
}

#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
/// Represents a change in field associated with a group audit log entry.
pub struct GroupAuditLogDataChange<T> {
/// The old field before the change.
pub old: T,
/// The new field after the change.
pub new: T,
}
2 changes: 2 additions & 0 deletions src/model/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ mod assets;
pub use assets::*;
mod auth;
pub use auth::*;
mod groups;
pub use groups::*;
mod instances;
pub use instances::*;
mod notifications;
Expand Down
3 changes: 2 additions & 1 deletion src/model/users.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use either::Either;
use serde::{Deserialize, Serialize};
use time::{serde::rfc3339, OffsetDateTime};
use url::Url;
Expand Down Expand Up @@ -280,7 +281,7 @@ pub struct CurrentAccountData {
pub obfuscated_pending_email: String,
/// Can be empty
#[serde(default)]
pub past_display_names: Vec<PastDisplayName>,
pub past_display_names: Vec<Either<PastDisplayName, String>>,
/// If hasn't set status yet
pub status_first_time: bool,
/// History of statuses (VRC pre-populates some for new accounts)
Expand Down
53 changes: 53 additions & 0 deletions src/query/groups.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use racal::Queryable;
use serde::{Deserialize, Serialize};

use super::Authentication;

/// Gets information about a specific group
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
pub struct Group {
/// The ID of the group to get information about
pub id: crate::id::Group,
}

impl Queryable<Authentication, crate::model::Group> for Group {
fn url(&self, _: &Authentication) -> String {
format!("{}/groups/{}", crate::API_BASE_URI, self.id.as_ref())
}
}

/// Gets audit logs from a specific group
#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
pub struct GroupAuditLogs {
/// The ID of the group to get audit logs from
pub id: crate::id::Group,
/// The count of how many logs to get (1 - 100)
pub n: Option<u8>,
/// The offset of how many logs to get
pub offset: Option<usize>,
// TODO: startDate & endDate
}

impl Queryable<Authentication, crate::model::GroupAuditLogs>
for GroupAuditLogs
{
fn url(&self, _: &Authentication) -> String {
let base_query =
format!("{}/groups/{}/auditLogs", crate::API_BASE_URI, self.id.as_ref());

let mut params = Vec::new();
if let Some(n) = self.n {
params.push(format!("n={n}"));
}

if let Some(offset) = self.offset {
params.push(format!("offset={offset}"));
}

if params.is_empty() {
base_query
} else {
format!("{}?{}", base_query, params.join("&"))
}
}
}
2 changes: 2 additions & 0 deletions src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ mod auth;
pub use auth::*;
mod friends;
pub use friends::*;
mod groups;
pub use groups::*;
mod instances;
pub use instances::*;
mod users;
Expand Down
2 changes: 2 additions & 0 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub struct TestConfig {
pub friend_id: Option<vrc::id::User>,
pub self_id: Option<vrc::id::User>,
pub world_id: Option<vrc::id::World>,
pub group_id: Option<vrc::id::Group>,
pub moderating_group_id: Option<vrc::id::Group>,
}

pub static TEST_CONFIG: Lazy<TestConfig> = Lazy::new(|| {
Expand Down
47 changes: 47 additions & 0 deletions tests/groups.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#![cfg(feature = "api_client")]

use std::default;

Check warning on line 3 in tests/groups.rs

View workflow job for this annotation

GitHub Actions / Test

unused import: `std::default`

use vrc::{
api_client::{ApiClient, ApiError},
model::{Group, GroupAuditLogs},
};

mod common;

#[tokio::test]
#[ignore]
async fn group() -> Result<(), ApiError> {
let group_id = match &common::TEST_CONFIG.group_id {
Some(v) => v,
None => return Ok(()),
};

let api_client = common::api_client()?;

let query = vrc::query::Group { id: group_id.clone() };
let group: Group = api_client.query(query).await?;

dbg!(&group);

Ok(())
}

#[tokio::test]
#[ignore]
async fn group_audit_logs() -> Result<(), ApiError> {
let group_id = match &common::TEST_CONFIG.moderating_group_id {
Some(v) => v,
None => return Ok(()),
};

let api_client = common::api_client()?;

let query =
vrc::query::GroupAuditLogs { id: group_id.clone(), n: None, offset: None };
let audit_logs: GroupAuditLogs = api_client.query(query).await?;

dbg!(&audit_logs);

Ok(())
}

0 comments on commit 74054bb

Please sign in to comment.