Skip to content

Commit 9ee6c0f

Browse files
author
Erik Guzman
committed
Thin Notion API Begins!!!
1 parent fa22922 commit 9ee6c0f

File tree

13 files changed

+404
-0
lines changed

13 files changed

+404
-0
lines changed

.formatter.exs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Used by "mix format"
2+
[
3+
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
4+
]

.gitignore

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# The directory Mix will write compiled artifacts to.
2+
/_build/
3+
4+
# If you run "mix test --cover", coverage assets end up here.
5+
/cover/
6+
7+
# The directory Mix downloads your dependencies sources to.
8+
/deps/
9+
10+
# Where third-party dependencies like ExDoc output generated docs.
11+
/doc/
12+
13+
# Ignore .fetch files in case you like to edit your project deps locally.
14+
/.fetch
15+
16+
# If the VM crashes, it generates a dump, let's ignore it too.
17+
erl_crash.dump
18+
19+
# Also ignore archive artifacts (built via "mix archive.build").
20+
*.ez
21+
22+
# Ignore package tarball (built via "mix hex.build").
23+
thin_notion_api-*.tar
24+
25+
26+
# Temporary files for e.g. tests
27+
/tmp

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# ThinNotionApi
2+
3+
**TODO: Add description**
4+
5+
TODO:
6+
7+
- Need to start adding tests
8+
- List database query param support
9+
- Query database with options
10+
- Support all Page object actions
11+
- Support all Block actions
12+
- Support all User actions
13+
- Support Search
14+
15+
## Installation
16+
17+
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
18+
by adding `thin_notion_api` to your list of dependencies in `mix.exs`:
19+
20+
```elixir
21+
def deps do
22+
[
23+
{:thin_notion_api, "~> 0.1.0"}
24+
]
25+
end
26+
```
27+
28+
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
29+
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
30+
be found at [https://hexdocs.pm/thin_notion_api](https://hexdocs.pm/thin_notion_api).
31+

config/config.exs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
use Mix.Config
2+
3+
config :thin_notion_api, :api_key, System.get_env("NOTION_API_KEY")
4+
config :thin_notion_api, :notion_version, System.get_env("NOTION_VERSION")

lib/thin_notion_api.ex

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
defmodule ThinNotionApi do
2+
@moduledoc """
3+
Documentation for `ThinNotionApi`.
4+
5+
A thin wrapper for the Notion API
6+
"""
7+
8+
@doc """
9+
Hello world.
10+
11+
## Examples
12+
13+
iex> ThinNotionApi.hello()
14+
:world
15+
16+
"""
17+
def hello do
18+
:world
19+
end
20+
end

lib/thin_notion_api/base.ex

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
defmodule ThinNotionApi.Base do
2+
@moduledoc false
3+
4+
alias ThinNotionApi.Parser
5+
import ThinNotionApi.Utils
6+
7+
@base_url "https://api.notion.com/v1"
8+
9+
def get(path_arg, query_params \\ %{}) do
10+
path_arg
11+
|> build_url(query_params)
12+
|> HTTPoison.get(request_headers())
13+
|> Parser.parse()
14+
end
15+
16+
def post(path_arg, body \\ %{}) do
17+
json_body = Jason.encode!(body)
18+
IO.puts(json_body)
19+
path_arg
20+
|> build_url()
21+
|> HTTPoison.post(json_body, post_request_headers())
22+
|> Parser.parse()
23+
end
24+
25+
defp build_url(path_arg, query_params \\ %{}) do
26+
query_params = process_params(query_params)
27+
28+
"#{@base_url}/#{path_arg}?#{URI.encode_query(query_params)}"
29+
end
30+
31+
defp process_params(params) do
32+
%{}
33+
|> Map.merge(params)
34+
|> Map.delete(:__struct__)
35+
end
36+
end

lib/thin_notion_api/databases.ex

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
defmodule ThinNotionApi.Databases do
2+
import ThinNotionApi.Base
3+
4+
@doc """
5+
Retrieves a Database object using the ID specified.
6+
7+
## Examples
8+
9+
iex> ThinNotionApi.Databases.retrieve_database(database_id)
10+
{:ok,
11+
%{
12+
"created_time" => "2021-06-11T20:34:00.000Z",
13+
"id" => "a4ef92b2-a798-4bae-8211-4817678cd2f4",
14+
"last_edited_time" => "2021-07-16T20:44:00.000Z",
15+
"object" => "database",
16+
"parent" => %{
17+
"page_id" => "9b4a624d-5a18-482a-b218-7e54166edda7",
18+
"type" => "page_id"
19+
},
20+
"properties" => %{
21+
"Name" => %{"id" => "title", "title" => %{}, "type" => "title"},
22+
"Tags" => %{
23+
"id" => "@Tnd",
24+
"multi_select" => %{"options" => []},
25+
"type" => "multi_select"
26+
}
27+
},
28+
"title" => [
29+
%{
30+
"annotations" => %{
31+
"bold" => false,
32+
"code" => false,
33+
"color" => "default",
34+
"italic" => false,
35+
"strikethrough" => false,
36+
"underline" => false
37+
},
38+
"href" => nil,
39+
"plain_text" => "Test Database",
40+
"text" => %{"content" => "Test Database", "link" => nil},
41+
"type" => "text"
42+
}
43+
]
44+
}}
45+
"""
46+
def retrieve_database(database_id) do
47+
get("databases/" <> database_id)
48+
end
49+
50+
@doc """
51+
List all Databases shared with the authenticated integration. The response may contain fewer than page_size of results.
52+
53+
> Use search pages for more details.
54+
55+
> This endpoint is no longer recommended, use search instead. This endpoint will only return explicitly shared pages, while search will also return child pages within explicitly shared pages. This endpoint's results cannot be filtered, while search can be used to match on page title.
56+
57+
## Examples:
58+
iex> ThinNotionApi.Databases.list_databases(query_params)
59+
60+
"""
61+
def list_databases(query_params \\ %{}) do
62+
get("databases", query_params)
63+
end
64+
65+
@doc """
66+
Gets a list of Pages contained in the database, filtered and ordered according to the filter conditions and sort criteria provided in the request. The response may contain fewer than page_size of results.
67+
68+
## Examples:
69+
iex> ThinNotionApi.Databases.query_database(database_id)
70+
{:ok,
71+
%{
72+
"has_more" => false,
73+
"next_cursor" => nil,
74+
"object" => "list",
75+
"results" => [
76+
%{
77+
"archived" => false,
78+
"created_time" => "2021-06-11T20:34:00.000Z",
79+
"id" => "0a8f7171-00b7-4f7e-a0b5-155f006186bb",
80+
"last_edited_time" => "2021-07-16T21:04:00.000Z",
81+
"object" => "page",
82+
"parent" => %{
83+
"database_id" => "a4ef92b2-a798-4bae-8211-4817678cd2f4",
84+
"type" => "database_id"
85+
},
86+
"properties" => %{
87+
"Name" => %{
88+
"id" => "title",
89+
"title" => [
90+
%{
91+
"annotations" => %{
92+
"bold" => false,
93+
"code" => false,
94+
"color" => "default",
95+
"italic" => false,
96+
"strikethrough" => false,
97+
"underline" => false
98+
},
99+
"href" => nil,
100+
"plain_text" => "Hello World",
101+
"text" => %{"content" => "Hello World", "link" => nil},
102+
"type" => "text"
103+
}
104+
],
105+
"type" => "title"
106+
}
107+
},
108+
"url" => "https://www.notion.so/Hello-World-0a8f717100b74f7ea0b5155f006186bb"
109+
},
110+
...
111+
]
112+
}
113+
}
114+
115+
iex> ThinNotionApi.Databases.query_database("a4ef92b2a7984bae82114817678cd2f4", %{ "page_size" => 1})
116+
{:ok,
117+
%{
118+
"has_more" => true,
119+
"next_cursor" => "700b4e34-697c-4fad-b97d-d9741d1fbaeb",
120+
"object" => "list",
121+
"results" => [
122+
%{
123+
"archived" => false,
124+
"created_time" => "2021-06-11T20:34:00.000Z",
125+
"id" => "0a8f7171-00b7-4f7e-a0b5-155f006186bb",
126+
"last_edited_time" => "2021-07-16T21:04:00.000Z",
127+
"object" => "page",
128+
"parent" => %{
129+
"database_id" => "a4ef92b2-a798-4bae-8211-4817678cd2f4",
130+
"type" => "database_id"
131+
},
132+
"properties" => %{
133+
"Name" => %{
134+
"id" => "title",
135+
"title" => [
136+
%{
137+
"annotations" => %{
138+
"bold" => false,
139+
"code" => false,
140+
"color" => "default",
141+
"italic" => false,
142+
"strikethrough" => false,
143+
"underline" => false
144+
},
145+
"href" => nil,
146+
"plain_text" => "Hello World",
147+
"text" => %{"content" => "Hello World", "link" => nil},
148+
"type" => "text"
149+
}
150+
],
151+
"type" => "title"
152+
}
153+
},
154+
"url" => "https://www.notion.so/Hello-World-0a8f717100b74f7ea0b5155f006186bb"
155+
}
156+
]
157+
}}
158+
"""
159+
def query_database(database_id, body_params \\ %{}) do
160+
post("databases/" <> database_id <> "/query", body_params)
161+
end
162+
163+
@spec create_database(any, any, any) :: any
164+
def create_database(parent_id, title, properties \\ %{
165+
Name: %{
166+
title: %{}
167+
},
168+
}) do
169+
body_params = %{
170+
parent: %{
171+
type: "page_id",
172+
page_id: parent_id
173+
},
174+
title: [
175+
%{
176+
type: "text",
177+
text: %{
178+
content: title,
179+
link: nil
180+
}
181+
}
182+
],
183+
properties: properties
184+
}
185+
186+
post("databases", body_params)
187+
end
188+
end

lib/thin_notion_api/parser.ex

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
defmodule ThinNotionApi.Parser do
2+
@moduledoc """
3+
Generic parser to parse api response
4+
"""
5+
6+
@type status_code :: integer
7+
@type headers :: map
8+
@type response ::
9+
{:ok, struct}
10+
| {:error, map, status_code}
11+
| any
12+
13+
@doc """
14+
Parses the response from API calls
15+
"""
16+
@spec parse(tuple) :: response
17+
def parse({:ok, %HTTPoison.Response{body: body, headers: _, status_code: status}})
18+
when status in [200, 201],
19+
do: {:ok, parse_response_body(body)}
20+
21+
def parse({:error, %HTTPoison.Error{id: _, reason: reason}}), do: {:error, %{reason: reason}}
22+
23+
def parse({:ok, %HTTPoison.Response{body: body, headers: _, status_code: status}}),
24+
do: {:error, parse_response_body(body), status}
25+
26+
def parse(response), do: response
27+
28+
defp parse_response_body(body), do: Jason.decode!(body)
29+
end

lib/thin_notion_api/utils.ex

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
defmodule ThinNotionApi.Utils do
2+
@moduledoc false
3+
4+
def api_key, do: Application.get_env(:thin_notion_api, :api_key)
5+
6+
def notion_version,
7+
do: Application.get_env(:thin_notion_api, :notion_version, "2021-05-13")
8+
9+
def auth_header, do: [{"Authorization", "Bearer " <> api_key()}]
10+
def version_header, do: [{"Notion-Version", notion_version()}]
11+
def content_header, do: [{"Content-Type", "application/json"}]
12+
def request_headers, do: version_header() ++ auth_header()
13+
def post_request_headers, do: version_header() ++ auth_header() ++ content_header()
14+
end

mix.exs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
defmodule ThinNotionApi.MixProject do
2+
use Mix.Project
3+
4+
def project do
5+
[
6+
app: :thin_notion_api,
7+
version: "0.1.0",
8+
elixir: "~> 1.11",
9+
start_permanent: Mix.env() == :prod,
10+
deps: deps()
11+
]
12+
end
13+
14+
# Run "mix help compile.app" to learn about applications.
15+
def application do
16+
[
17+
extra_applications: [:logger]
18+
]
19+
end
20+
21+
# Run "mix help deps" to learn about dependencies.
22+
defp deps do
23+
[
24+
# {:dep_from_hexpm, "~> 0.3.0"},
25+
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
26+
{:httpoison, "~> 1.8"},
27+
{:jason, "~> 1.2"}
28+
]
29+
end
30+
end

0 commit comments

Comments
 (0)