Skip to content

Commit

Permalink
Add dynamic selects to Sharepoint block
Browse files Browse the repository at this point in the history
  • Loading branch information
pawelsierant committed Oct 16, 2024
1 parent 4ac75b9 commit fab929a
Show file tree
Hide file tree
Showing 5 changed files with 313 additions and 38 deletions.
83 changes: 45 additions & 38 deletions apps/api/lib/buildel/blocks/sharepoint_client.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,42 +29,49 @@ defmodule Buildel.Blocks.SharepointClient do
"name" => name_schema(),
"opts" =>
options_schema(%{
"required" => ["client_secret", "client_id", "tenant_id"],
"properties" => %{
"client_secret" =>
secret_schema(%{
"title" => "Client secret",
"description" => "Azure app client secret"
}),
"client_id" => %{
"type" => "string",
"title" => "Client ID",
"description" => "Azure app client ID",
"default" => "",
"minLength" => 1
},
"tenant_id" => %{
"type" => "string",
"title" => "Tenant ID",
"description" => "Azure app tenant ID",
"default" => "",
"minLength" => 1
},
"site_id" => %{
"type" => "string",
"title" => "Site ID",
"description" => "SharePoint site ID",
"default" => "",
"minLength" => 1
},
"drive_id" => %{
"type" => "string",
"title" => "Drive ID",
"description" => "SharePoint drive ID",
"default" => "",
"minLength" => 1
}
}
"required" => ["client_secret", "client_id", "tenant_id", "site", "drive"],
"properties" =>
Jason.OrderedObject.new(
client_secret:
secret_schema(%{
"title" => "Client secret",
"description" => "Azure app client secret"
}),
client_id: %{
"type" => "string",
"title" => "Client ID",
"description" => "Azure app client ID",
"default" => "",
"minLength" => 1
},
tenant_id: %{
"type" => "string",
"title" => "Tenant ID",
"description" => "Azure app tenant ID",
"default" => "",
"minLength" => 1
},
site: %{
"type" => "string",
"title" => "Site",
"description" => "SharePoint site ID",
"url" =>
"/api/organizations/{{organization_id}}/tools/sharepoint/sites?client_id={{opts.client_id}}&secret_name={{opts.client_secret}}&tenant_id={{opts.tenant_id}}",
"presentAs" => "async-select",
"minLength" => 1,
"readonly" => true
},
drive: %{
"type" => "string",
"title" => "Drive",
"description" => "SharePoint drive ID",
"url" =>
"/api/organizations/{{organization_id}}/tools/sharepoint/drives?client_id={{opts.client_id}}&secret_name={{opts.client_secret}}&tenant_id={{opts.tenant_id}}&site_id={{opts.site}}",
"presentAs" => "async-select",
"minLength" => 1,
"readonly" => true
}
)
})
}
}
Expand All @@ -86,8 +93,8 @@ defmodule Buildel.Blocks.SharepointClient do
{:ok,
state
|> Map.put(:access_token, access_token)
|> Map.put(:site_id, opts.site_id)
|> Map.put(:drive_id, opts.drive_id)}
|> Map.put(:site_id, opts.site)
|> Map.put(:drive_id, opts.drive)}
end

defp get_access_token(client_id, client_secret, tenant_id) do
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
defmodule BuildelWeb.OrganizationToolSharepointController do
use BuildelWeb, :controller
use OpenApiSpex.ControllerSpecs

import BuildelWeb.UserAuth

alias Buildel.Organizations

action_fallback(BuildelWeb.FallbackController)

plug(:fetch_current_user)
plug(:require_authenticated_user)

plug OpenApiSpex.Plug.CastAndValidate,
json_render_error_v2: true,
render_error: BuildelWeb.ErrorRendererPlug

tags ["sharepoint"]

operation :list_sites,
summary: "List sites",
parameters: [
organization_id: [
in: :path,
description: "Organization ID",
type: :integer,
required: true
],
client_id: [
in: :query,
description: "Client ID",
type: :string,
required: true
],
secret_name: [
in: :query,
description: "Client secret name",
type: :string,
required: true
],
tenant_id: [
in: :query,
description: "Tenant ID",
type: :string,
required: true
]
],
request_body: nil,
responses: [
ok: {"organizations", "application/json", BuildelWeb.Schemas.Sharepoint.ListSitesResponse},
unauthorized:
{"unauthorized", "application/json", BuildelWeb.Schemas.Errors.UnauthorizedResponse},
forbidden: {"forbidden", "application/json", BuildelWeb.Schemas.Errors.ForbiddenResponse}
],
security: [%{"authorization" => []}]

def list_sites(conn, _) do
%{
client_id: client_id,
secret_name: secret_name,
tenant_id: tenant_id,
organization_id: organization_id
} = conn.params

user = conn.assigns.current_user

with {:ok, organization} <- Organizations.get_user_organization(user, organization_id),
{:ok, %{value: client_secret}} <-
Buildel.Organizations.get_organization_secret(organization, secret_name),
{:ok, access_token} <- get_access_token(client_id, client_secret, tenant_id),
{:ok, sites} <- get_sites(access_token) do
render(conn, :list_sites, sites: sites["value"])
end
end

operation :list_drives,
summary: "List drives",
parameters: [
organization_id: [
in: :path,
description: "Organization ID",
type: :integer,
required: true
],
client_id: [
in: :query,
description: "Client ID",
type: :string,
required: true
],
secret_name: [
in: :query,
description: "Client secret name",
type: :string,
required: true
],
tenant_id: [
in: :query,
description: "Tenant ID",
type: :string,
required: true
],
site_id: [
in: :query,
description: "Site ID",
type: :string,
required: true
]
],
request_body: nil,
responses: [
ok: {"organizations", "application/json", BuildelWeb.Schemas.Sharepoint.ListSitesResponse},
unauthorized:
{"unauthorized", "application/json", BuildelWeb.Schemas.Errors.UnauthorizedResponse},
forbidden: {"forbidden", "application/json", BuildelWeb.Schemas.Errors.ForbiddenResponse}
],
security: [%{"authorization" => []}]

def list_drives(conn, _) do
%{
client_id: client_id,
secret_name: secret_name,
tenant_id: tenant_id,
organization_id: organization_id,
site_id: site_id
} = conn.params

user = conn.assigns.current_user

with {:ok, organization} <- Organizations.get_user_organization(user, organization_id),
{:ok, %{value: client_secret}} <-
Buildel.Organizations.get_organization_secret(organization, secret_name),
{:ok, access_token} <- get_access_token(client_id, client_secret, tenant_id),
{:ok, drives} <- get_drives(access_token, site_id) do
render(conn, :list_drives, drives: drives["value"])
end
end

defp get_sites(access_token) do
url =
"https://graph.microsoft.com/v1.0/sites?search=*"

headers = [
{"Authorization", "Bearer #{access_token}"}
]

case Req.new(url: url) |> Req.get(headers: headers) do
{:ok, %Req.Response{status: 200, body: body}} ->
{:ok, body}

{:error, %Req.Response{body: reason}} ->
{:ok, reason}

{:error, reason} ->
{:error, reason}
end
end

defp get_drives(access_token, site_id) do
url =
"https://graph.microsoft.com/v1.0/sites/#{site_id}/drives"

headers = [
{"Authorization", "Bearer #{access_token}"}
]

case Req.new(url: url) |> Req.get(headers: headers) do
{:ok, %Req.Response{status: 200, body: body}} ->
{:ok, body}

{:error, %Req.Response{body: reason}} ->
{:ok, reason}

{:error, reason} ->
{:error, reason}
end
end

defp get_access_token(client_id, client_secret, tenant_id) do
url = "https://login.microsoftonline.com/#{tenant_id}/oauth2/v2.0/token"

body = %{
"client_id" => client_id,
"client_secret" => client_secret,
"scope" => "https://graph.microsoft.com/.default",
"grant_type" => "client_credentials"
}

headers = [{"Content-Type", "application/x-www-form-urlencoded"}]

case Req.new(url: url) |> Req.post(form: body, headers: headers) do
{:ok, %Req.Response{status: 200, body: body}} ->
{:ok, body["access_token"]}

{:ok, %Req.Response{body: reason}} ->
{:error, reason}

{:error, reason} ->
{:error, reason}
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
defmodule BuildelWeb.OrganizationToolSharepointJSON do
def list_sites(%{sites: sites}) do
%{data: for(site <- sites, do: site(site))}
end

def list_drives(%{drives: drives}) do
%{data: for(drive <- drives, do: drive(drive))}
end

defp drive(drive) do
%{
id: drive["id"],
name: drive["name"]
}
end

defp site(site) do
%{
id: site["id"],
name: site["displayName"]
}
end
end
12 changes: 12 additions & 0 deletions apps/api/lib/buildel_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,18 @@ defmodule BuildelWeb.Router do
:create
)

get(
"/organizations/:organization_id/tools/sharepoint/sites",
OrganizationToolSharepointController,
:list_sites
)

get(
"/organizations/:organization_id/tools/sharepoint/drives",
OrganizationToolSharepointController,
:list_drives
)

get("/users/me", UserController, :me)
put("/users", UserController, :update)
post("/users/log_in", UserSessionController, :create)
Expand Down
31 changes: 31 additions & 0 deletions apps/api/lib/buildel_web/schemas/organizations/tools/sharepoint.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
defmodule BuildelWeb.Schemas.Sharepoint do
alias OpenApiSpex.Schema

defmodule ListSitesResponse do
require OpenApiSpex

OpenApiSpex.schema(%{
title: "SharepointListSitesResponse",
type: :object,
properties: %{
id: %Schema{type: :string, description: "Site ID"},
name: %Schema{type: :string, description: "Site name"}
},
required: [:id, :name]
})
end

defmodule ListDrivesResponse do
require OpenApiSpex

OpenApiSpex.schema(%{
title: "SharepointListDrivesResponse",
type: :object,
properties: %{
id: %Schema{type: :string, description: "Drive ID"},
name: %Schema{type: :string, description: "Drive name"}
},
required: [:id, :name]
})
end
end

0 comments on commit fab929a

Please sign in to comment.