From 40fc911af7cbe68fc5f994cc6c2d15b10b6e673f Mon Sep 17 00:00:00 2001 From: Ali Naqvi Date: Thu, 18 Dec 2025 14:37:40 +0800 Subject: [PATCH] feat: [PPT-2327] Add MS Planner API --- drivers/microsoft/graph_api_advanced.cr | 118 ++++++++++++++++++++++-- 1 file changed, 112 insertions(+), 6 deletions(-) diff --git a/drivers/microsoft/graph_api_advanced.cr b/drivers/microsoft/graph_api_advanced.cr index 6d6ce04290e..fb4766eb10d 100644 --- a/drivers/microsoft/graph_api_advanced.cr +++ b/drivers/microsoft/graph_api_advanced.cr @@ -22,14 +22,16 @@ class Microsoft::GraphAPIAdvanced < PlaceOS::Driver client_secret: String, ) + getter! client : Office365::Client + def on_update credentials = setting(GraphParams, :credentials) @client = Office365::Client.new(**credentials) end private def get(path : String, query_params : URI::Params? = nil) - @client.not_nil!.graph_request( - @client.not_nil!.graph_http_request( + client.graph_request( + client.graph_http_request( request_method: "GET", path: path, query: query_params @@ -43,8 +45,8 @@ class Microsoft::GraphAPIAdvanced < PlaceOS::Driver end private def post(path : String, query_params : URI::Params? = nil, body : String? = nil) - @client.not_nil!.graph_request( - @client.not_nil!.graph_http_request( + client.graph_request( + client.graph_http_request( request_method: "POST", path: path, data: body, @@ -59,8 +61,8 @@ class Microsoft::GraphAPIAdvanced < PlaceOS::Driver end private def put(path : String, query_params : URI::Params? = nil, body : String? = nil) - @client.not_nil!.graph_request( - @client.not_nil!.graph_http_request( + client.graph_request( + client.graph_http_request( request_method: "PUT", path: path, data: body, @@ -89,4 +91,108 @@ class Microsoft::GraphAPIAdvanced < PlaceOS::Driver ) response.body["value"] end + + # ===================== + # Planner API + # ===================== + + # List plans for a group + # https://learn.microsoft.com/en-us/graph/api/plannergroup-list-plans + def list_plans(group_id : String) + response = get("/v1.0/groups/#{group_id}/planner/plans") + JSON.parse(response.body).as_h["value"] + end + + # Get a plan by ID + # https://learn.microsoft.com/en-us/graph/api/plannerplan-get + def get_plan(plan_id : String) + response = get("/v1.0/planner/plans/#{plan_id}") + JSON.parse(response.body) + end + + # Create a new plan + # https://learn.microsoft.com/en-us/graph/api/planner-post-plans + def create_plan(group_id : String, title : String) + body = { + container: { + url: "https://graph.microsoft.com/v1.0/groups/#{group_id}", + }, + title: title, + }.to_json + response = post("/v1.0/planner/plans", body: body) + JSON.parse(response.body) + end + + # List buckets for a plan + # https://learn.microsoft.com/en-us/graph/api/plannerplan-list-buckets + def list_buckets(plan_id : String) + response = get("/v1.0/planner/plans/#{plan_id}/buckets") + JSON.parse(response.body).as_h["value"] + end + + # Create a bucket in a plan + # https://learn.microsoft.com/en-us/graph/api/planner-post-buckets + def create_bucket(plan_id : String, name : String, order_hint : String? = nil) + body = { + name: name, + planId: plan_id, + orderHint: order_hint || " !", + }.to_json + response = post("/v1.0/planner/buckets", body: body) + JSON.parse(response.body) + end + + # List tasks for a plan + # https://learn.microsoft.com/en-us/graph/api/plannerplan-list-tasks + def list_tasks(plan_id : String) + response = get("/v1.0/planner/plans/#{plan_id}/tasks") + JSON.parse(response.body).as_h["value"] + end + + # Create a task in a plan + # https://learn.microsoft.com/en-us/graph/api/planner-post-tasks + # assigned_to_user_ids: array of user IDs to assign the task to + # priority: 0-10 (0=highest, 10=lowest). 1=urgent, 3=important, 5=medium, 9=low + def create_task( + plan_id : String, + title : String, + bucket_id : String? = nil, + assigned_to_user_ids : Array(String)? = nil, + due_date_time : String? = nil, + start_date_time : String? = nil, + percent_complete : Int32? = nil, + priority : Int32? = nil, + order_hint : String? = nil, + ) + body = JSON.build do |json| + json.object do + json.field "planId", plan_id + json.field "title", title + json.field "bucketId", bucket_id if bucket_id + json.field "dueDateTime", due_date_time if due_date_time + json.field "startDateTime", start_date_time if start_date_time + json.field "percentComplete", percent_complete if percent_complete + json.field "priority", priority if priority + json.field "orderHint", order_hint if order_hint + + if assigned_to_user_ids && !assigned_to_user_ids.empty? + json.field "assignments" do + json.object do + assigned_to_user_ids.each do |user_id| + json.field user_id do + json.object do + json.field "@odata.type", "#microsoft.graph.plannerAssignment" + json.field "orderHint", " !" + end + end + end + end + end + end + end + end + + response = post("/v1.0/planner/tasks", body: body) + JSON.parse(response.body) + end end