From 6a4b9be6a4c1a3b664888609b4bebd625eb4e896 Mon Sep 17 00:00:00 2001 From: Anshul Garg Date: Fri, 13 Mar 2026 16:36:39 +0530 Subject: [PATCH 1/3] feat(calendar): add --conference flag to +insert for Google Meet links Closes #419. Closes #461. Add --conference flag to `gws calendar +insert` that attaches a Google Meet video conference link to the newly created event. Implementation: - Add conferenceData.createRequest with type "hangoutsMeet" to the event body when --conference is set - Set conferenceDataVersion=1 query parameter so the Calendar API processes the conference creation request - Generate a unique requestId (gws-) for idempotency Add tests for both --conference and no-conference paths. --- src/helpers/calendar.rs | 85 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 6 deletions(-) diff --git a/src/helpers/calendar.rs b/src/helpers/calendar.rs index 8b07fbd5..e8acbc85 100644 --- a/src/helpers/calendar.rs +++ b/src/helpers/calendar.rs @@ -80,14 +80,21 @@ impl Helper for CalendarHelper { .value_name("EMAIL") .action(ArgAction::Append), ) + .arg( + Arg::new("conference") + .long("conference") + .help("Add a Google Meet video conference link") + .action(ArgAction::SetTrue), + ) .after_help("\ EXAMPLES: gws calendar +insert --summary 'Standup' --start '2026-06-17T09:00:00-07:00' --end '2026-06-17T09:30:00-07:00' gws calendar +insert --summary 'Review' --start ... --end ... --attendee alice@example.com + gws calendar +insert --summary 'Sync' --start ... --end ... --conference TIPS: Use RFC3339 format for times (e.g. 2026-06-17T09:00:00-07:00). - For recurring events or conference links, use the raw API instead."), + Use --conference to attach a Google Meet link automatically."), ); cmd = cmd.subcommand( Command::new("+agenda") @@ -420,6 +427,7 @@ fn build_insert_request( let location = matches.get_one::("location"); let description = matches.get_one::("description"); let attendees_vals = matches.get_many::("attendee"); + let conference = matches.get_flag("conference"); // Find method: events.insert checks let events_res = doc @@ -450,13 +458,31 @@ fn build_insert_request( body["attendees"] = json!(attendees_list); } + if conference { + // Generate a unique requestId for the conference creation + let request_id = format!("gws-{:016x}", rand::random::()); + body["conferenceData"] = json!({ + "createRequest": { + "requestId": request_id, + "conferenceSolutionKey": { + "type": "hangoutsMeet" + } + } + }); + } + let body_str = body.to_string(); let scopes: Vec = insert_method.scopes.iter().map(|s| s.to_string()).collect(); - // events.insert requires 'calendarId' path parameter - let params = json!({ + // events.insert requires 'calendarId' path parameter. + // When creating a conference, conferenceDataVersion=1 tells the API + // to process the conferenceData.createRequest and generate a Meet link. + let mut params = json!({ "calendarId": calendar_id }); + if conference { + params["conferenceDataVersion"] = json!(1); + } let params_str = params.to_string(); Ok((params_str, body_str, scopes)) @@ -494,6 +520,11 @@ mod tests { Arg::new("attendee") .long("attendee") .action(ArgAction::Append), + ) + .arg( + Arg::new("conference") + .long("conference") + .action(ArgAction::SetTrue), ); cmd.try_get_matches_from(args).unwrap() } @@ -564,9 +595,6 @@ mod tests { let tomorrow_start = today_start_local + chrono::Duration::days(1); let tomorrow_rfc = tomorrow_start.to_rfc3339(); - // The local offset should appear in the RFC3339 string (e.g. -07:00, +05:30). - // If the code were using UTC, the string would end with +00:00 (unless - // the machine is actually in UTC, in which case this test is a no-op). let local_offset = local_now.format("%:z").to_string(); assert!( today_rfc.contains(&local_offset), @@ -577,4 +605,49 @@ mod tests { "tomorrow boundary should carry local offset {local_offset}, got {tomorrow_rfc}" ); } + + #[test] + fn test_build_insert_request_with_conference() { + let doc = make_mock_doc(); + let matches = make_matches_insert(&[ + "test", + "--summary", + "Sync", + "--start", + "2024-01-01T10:00:00Z", + "--end", + "2024-01-01T11:00:00Z", + "--conference", + ]); + let (params, body, _) = build_insert_request(&matches, &doc).unwrap(); + + let body_json: serde_json::Value = serde_json::from_str(&body).unwrap(); + let create_req = &body_json["conferenceData"]["createRequest"]; + assert_eq!( + create_req["conferenceSolutionKey"]["type"], + "hangoutsMeet" + ); + let request_id = create_req["requestId"].as_str().unwrap(); + assert!(request_id.starts_with("gws-")); + + assert!(params.contains("conferenceDataVersion")); + } + + #[test] + fn test_build_insert_request_without_conference() { + let doc = make_mock_doc(); + let matches = make_matches_insert(&[ + "test", + "--summary", + "Meeting", + "--start", + "2024-01-01T10:00:00Z", + "--end", + "2024-01-01T11:00:00Z", + ]); + let (params, body, _) = build_insert_request(&matches, &doc).unwrap(); + + assert!(!body.contains("conferenceData")); + assert!(!params.contains("conferenceDataVersion")); + } } From 6f847f17a6dce9d4002776819a81b9163d231a38 Mon Sep 17 00:00:00 2001 From: Anshul Garg Date: Sat, 14 Mar 2026 01:50:00 +0530 Subject: [PATCH 2/3] fix: restore explanatory comments in timezone test --- src/helpers/calendar.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/helpers/calendar.rs b/src/helpers/calendar.rs index e8acbc85..356e7918 100644 --- a/src/helpers/calendar.rs +++ b/src/helpers/calendar.rs @@ -595,6 +595,9 @@ mod tests { let tomorrow_start = today_start_local + chrono::Duration::days(1); let tomorrow_rfc = tomorrow_start.to_rfc3339(); + // The local offset should appear in the RFC3339 string (e.g. -07:00, +05:30). + // If the code were using UTC, the string would end with +00:00 (unless + // the machine is actually in UTC, in which case this test is a no-op). let local_offset = local_now.format("%:z").to_string(); assert!( today_rfc.contains(&local_offset), From 580957ec0c847b2fc30f454db46af93eff1ab59e Mon Sep 17 00:00:00 2001 From: Anshul Garg Date: Sat, 14 Mar 2026 02:01:37 +0530 Subject: [PATCH 3/3] chore: add changeset --- .changeset/feat-calendar-conference.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/feat-calendar-conference.md diff --git a/.changeset/feat-calendar-conference.md b/.changeset/feat-calendar-conference.md new file mode 100644 index 00000000..b287aa46 --- /dev/null +++ b/.changeset/feat-calendar-conference.md @@ -0,0 +1,5 @@ +--- +"@googleworkspace/cli": minor +--- + +Add --conference flag to calendar +insert for Google Meet links