jomei / notionapi Goto Github PK
View Code? Open in Web Editor NEWA Notion API SDK, written in Golang
Home Page: https://pkg.go.dev/github.com/jomei/notionapi
License: BSD 2-Clause "Simplified" License
A Notion API SDK, written in Golang
Home Page: https://pkg.go.dev/github.com/jomei/notionapi
License: BSD 2-Clause "Simplified" License
First of all,
thank you for the repo! I am very much enjoying it.
Currently, I am testing some workloads with Search.Do function that requires a SearchFilter.
Notion API does not require for us to specify any filter (basically omit,) but I think there must be other reasons you have enforced it not to be nil. However, the objects we retrieve from the Search.Do function do provide the ObjectType, which could hint us the type of the object, meaning that we could possibly skip the SearchFilter if that is nil.
So... if there's any chance, could I post PR with the following and the some tests:
But as always, I would like to learn the reason.
Thank you!
According to this Notion Wiki page, Page also uses retrieve block children endpoint.
Currently, BlockService.GetChildren()
only uses BlockID
type for block_id
parameter, and PageService
doesn't have GetChildren()
method. Then, How can I retrieve Page Content? Is there any way to do this?
I think adding GetChildren()
method to PageService
may make this work.
BlockUpdateRequest
struct has a field Code
but the ``Codestruct does not have
Caption` field. rather it is the `CodeBlock` that has the `Caption` field.
My question is: is there a way to update CodeBlock instead of Code thus making the caption update possible?
Thanks for developing this SDK within a few weeks after the public beta launch!
Notion doc:
Sorts are similar to the sorts provided in the Notion UI. Sorts operate on database properties or page timestamps and can be combined.
It seems that Property Field of SortObject should be omitted in case of an empty value, to allow sort by page timestamps
Otherwise notion API returns:
{
"object": "error",
"status": 400,
"code": "validation_error",
"message": "Could not find sort property with name or id: "
}
I want to implement it, but filter method is private..
It would be great if we could set fields on blocks via the block interface to limit casting + switch-case statements when working with update APIs.
The following blocks are not supported as they were recently added to the API, listed in the changelog: https://developers.notion.com/changelog
After going back and forth on #9, it feels like making sure the API reference is properly implemented is a quite meticulous task that is quite error prone and may introduce vicious bugs that will be hard to track down.
The official JS SDK has all the API types properly defined in Typescript, which could be used to generate those structs: https://github.com/makenotion/notion-sdk-js/blob/main/src/api-types.ts
An example on how to achieve that can be found in gopls
, which uses that approach to handle the structs that deals with the Language Server Protocol: https://github.com/golang/tools/tree/master/internal/lsp/protocol/typescript
I may give it a try in the upcoming days, I'll post here if I do!
var ptr *notion.DatabaseQueryRequest
response, err := client.Database.Query(context.Background(), notion.DatabaseID(id), ptr)
As seen in the snippet above, if a pointer to empty struct or nil
is passed, the module encounters error.
I have confirmed that the POST /v1/databases/<id>/query
works without supplying any of the filter, sort, cursor or page size.
Hi, firstly thanks for the code!
When I checked the client code, I found that all the response body from http client is not closed. Typically, there should be a line of
defer resp.Body.Close()
Otherwise it causes resource leak.
Currently these share the same underlying structure, though I guess it's not guaranteed to stay that way. To reduce switch-case statements in client code, it would be helpful to define an interface to get these common fields, maybe
type DownloadableFileBlock interface {
Block
GetURL() string
GetExpiryTime() *time.Time
}
A block returned by the Notion API contains an object field parent
, which points to either a parent page reference or parent block reference in the case of nested children.
For example:
{
"object": "block",
"id": "fbc4a8e8-302e-4258-a8eb-6a5f5066ddfe",
"parent": {
"type": "page_id",
"page_id": "bdd526b3-fb3e-44c7-ac77-797023bcb73f"
},
...
}
or
{
"object": "block",
"id": "7d5aea6a-2c98-4496-a81d-7d1bfbe7fb6b",
"parent": {
"type": "block_id",
"block_id": "fbc4a8e8-302e-4258-a8eb-6a5f5066ddfe"
},
...
}
It would be great if the Block interface had a function to fetch the parent, perhaps something like
type Parent interface {
GetType() string
GetID() string
}
type Block interface {
...
GetParent() Parent
}
Is it possible to add support for editing BasicBlock
which is part of every block type?
Something similar to this
type Block interface {
...
...
...
GetBasicBlock() *BasicBlock. <- New addition
}
...
...
...
func (b *BasicBlock) GetBasicBlock() *BasicBlock {
return b
}
If any client wants to edit something from BasicBlock
which is common to all blocks, the client would always need to type cast the block interface to a particular block type and edit the BasicBlock
info even when the client is not making any change specific to given block type.
According to the BlockUpdateRequest
struct it is not possible to update Code block.
(See https://github.com/jomei/notionapi/blob/v1.8.2/block.go#L689)
it seems that the notion api enables it.
is their a way to update code block using the package ?
See https://developers.notion.com/reference/block#table-blocks
When retrieving a TableBlock, The Children array of the Table element is always empty even if the tableBlock has children (HasChildren
=true)
I see in the code that https://github.com/jomei/notionapi/blob/main/block.go#L31:
// NOTE: For blocks with their own children, the Children slice will not be
// populated, instead the HasChildren flag will be true.
Thus my question is: how can I retrieve children for TableBlock ? (table_row children)
The CreateToken
endpoint uses the same underlying impl request helper function as all other API endpoints in this package.
This implementation is hard-coded to use the Bearer prefix when making requests.
Notion's public docs for this endpoint define that it uses the Basic
prefix, where the token is the base64-encoded concatenation of an OAuth app's client ID and secret.
Two possible fixes are:
Basic
prefix for this endpoint only, and expect library users to pass in the base64-encoded token.Token
type that can support both Bearer
and Basic
cases, the latter of which could include struct fields for ID + Secret, and implement the base64 encoding in this library.The Notion API is rate limited, and it recommends to use the Retry-After
response header value if the API returns a 429 error. I don't think it is currently possible to access response headers using this client.
A possible solution is to struct embed http.Response
in all of the returned struct types where this is necessary, then update the code accordingly. This makes sure the API does not break.
Google's go-github client does this, for example, so it's not an uncommon pattern. I'll be making a PR for this change.
Edit: after reading more of the code and associated tests, I think it may be too big of a change to embed the full *http.Response
. Instead, it may be better for now to just add two new fields, StatusCode
and Header
, from http.Response
.
Edit: PR has been closed, decided to implement a different solution. The new solution is to handle the 429 errors within the notionapi.Client
rather than exposing this error to users.
It would be very helpful to abstract these objects away in a comment interface, as currently in our code we use a lot of switch/case such as
switch obj.Type{
case "block":
return obj.CreatedAt
case "page":
return obj.CreatedAt
}
It would be much nicer to be able to do
obj.GetCreatedAt()
simplifying the client code greatly. It seems Blocks have a common Block
interface, I am wondering if we can create a common interface shared between Blocks, Pages, and Databases to access these common fields?
The optional param "after" is missing in AppendBlockChildrenRequest.
It has been added in this changelog: June 13 - July 10, 2023
Docs: https://developers.notion.com/reference/patch-block-children.
New Notion API version 2022-02-22 has been released which has some backward-incompatible changes.
Are there any plans to upgrade the module to use the newer version of Notion API?
While using the lib, I noticed that a bunch of properties have some fields typed with interface{}
:
DateProperty.Date
PeopleProperty.People
CheckboxProperty.Checkbox
While technically, this doesn't prevent to use the API, it still shifts the work on the library user to handle those fields. Is there a particular reason motivating this choice?
From my understanding so far, the API data structures for these fields are rather straightforward, thus it should be simple to handle those cases.
I don't want to meddle with unwanted PRs, so I thought it'd be best to first discuss things here!
example code
key := "token token token"
client := notionapi.NewClient(notionapi.Token(key))
childrenvv := notionapi.ParagraphBlock{
Object: notionapi.ObjectTypeBlock,
Type: notionapi.BlockTypeParagraph,
Paragraph: notionapi.Paragraph{
Text: []notionapi.RichText{
{
Type: notionapi.ObjectTypeText,
Text: notionapi.Text{
Content: "AAAAAA",
},
Annotations: ¬ionapi.Annotations{
Bold: true,
Italic: false,
Strikethrough: false,
Underline: false,
Code: false,
Color: "",
},
},
{
Type: notionapi.ObjectTypeText,
Text: notionapi.Text{
Content: " BBBBB",
},
Annotations: ¬ionapi.Annotations{
Bold: false,
Italic: false,
Strikethrough: false,
Underline: false,
Code: false,
Color: "",
},
},
},
},
}
req := notionapi.AppendBlockChildrenRequest{Children: []notionapi.Block{childrenvv}}
appendChildren, err := client.Block.AppendChildren(context.TODO(), "blockId(is a page)", &req)
if err != nil {
panic(err)
}
fmt.Println(appendChildren)
get error : interface conversion: interface {} is nil, not string
in github.com/jomei/[email protected]/block.go:537
The Blocks returned by the API all have some common fields like the type, ID, etc. A basic block type can be defined which has these fields and then embedded in the other blocks. This would reduce the duplication of these fields in the different block types.
For example:
type basicBlock struct {
Object ObjectType `json:"object"`
ID BlockID `json:"id,omitempty"`
Type BlockType `json:"type"`
CreatedTime *time.Time `json:"created_time,omitempty"`
LastEditedTime *time.Time `json:"last_edited_time,omitempty"`
HasChildren bool `json:"has_children,omitempty"`
Archived bool `json:"archived,omitempty"`
}
...
type ParagraphBlock struct {
basicBlock
Paragraph Paragraph `json:"paragraph"`
}
The idea being similar to https://play.golang.org/p/BTwPIwzuFJ8
Not sure how compatible this is with #10
Having an array type rollup causes unmarshalling to give an error:
json: cannot unmarshal object into Go struct field Page.properties of type notionapi.Property
This is because it does not know how to unmarshal into an interface. There is a TODO listed there in the property.go
file.
This error happens when the encounters something like this: https://go.dev/play/p/UgSBjohh-Cy
New Notion API version 2022-02-22 has been released which has some backward-incompatible changes.
Are there any plans to upgrade the module to use the newer version of Notion API?
HiHi master, thanks for your SDK!
The official API supports updating the icon and cover of the page. Could you update the SDK to support this function ?
I often just want to get the plain text from a property like the TitleProperty to print it in log or somewhere else.
What about adding some util functions which concats the []RichText
and returns it as string.
Something like:
func (p TitleProperty) GetPlainText() string {
builder := strings.Builder{}
for _, title := range p.Title {
builder.WriteString(title.PlainText)
builder.WriteString(" ")
}
return strings.TrimSpace(builder.String())
}
We could also add something like GetMarkdown
or getHTML
, but with some limitations like the color annotation in markdown wouldn't work.
Would affect the following properties:
WDYT?
Hi, I'm trying to query a database based on the property Name:
The equivalent curl request gives a successful response:
➜ curl -X POST 'https://api.notion.com/v1/databases/'$DB_ID'/query' \
-H 'Authorization: Bearer '"$NOTION_API_KEY"'' \
-H 'Notion-Version: 2022-02-22' \
-H "Content-Type: application/json" \
--data '{
"filter": {
"property": "Name",
"title": {
"equals": "May 2022"
}
}
}'
However when using the package:
dbQueryReq := ¬ionapi.DatabaseQueryRequest{
PropertyFilter: ¬ionapi.PropertyFilter{
Property: "Name",
Text: ¬ionapi.TextFilterCondition{
Equals: today.Format("Jan 2006"),
},
},
}
d, err := t.client.Database.Query(ctx, notionapi.DatabaseID(dbId), dbQueryReq)
Returns the error
panic: body failed validation. Fix one:
body.filter.or should be defined, instead was `undefined`.
body.filter.title should be defined, instead was `undefined`.
...
Looks like the Text
field in PropertyFilter
needs to have it's json_tag updated to use rich_text
?
parent is databaseParent or PageParent
use a map format like
{
"parent": {
"database_id": "abcd"
}
}
Current in sdk is
{
"parent": {
"type": "database_id",
"database_id": "abcd"
}
}
Bug:
It seems that updating a ToDoBlock
with Todo
properties check
equals to false
does not uncheck the box if it has been previously checked.
Test case:
BlockUpdateRequest
with a ToDo
object with check
properties with the false
value-> The box remains checked even after the update.
Conversely, if the box has not been checked and you make an update with check at true
, the box changes and become checked.
So many blocks have the RichText
field, so when retrieving blocks and getting their rich text, clients have to do
switch t := blockWithRichText.(type) {
case *notionapi.Heading1:
return t.Heading1.RichText
case *notionapi.Heading2:
return t.Heading2.RichText
It would be so much better to do
richText := t.GetRichText()
Maybe an interface such as
type BlockWithRichText interface {
Block
GetRichText() []RichText
}
would work?
I just opened this PR for adding support for reading status property configs, which was core to my use case.
Even if it's accepted, I'm still unclear on the best way to add support for parsing the status property type into a StatusPropertyConfig while also preventing the StatusPropertyConfig from being used as an input for the DatabaseClient.Create() and DatabaseClient.Update() methods.
Perhaps we could add a methods to DataBaseUpdateRequest
and DatabaseCreateRequest
that would remove any incompatible PropertyConfigs assigned to them?
I've not investigated this thoroughly, but it doesn't appear to me that the .Update()
method has any logic in place to prevent other invalid PropertyConfigs, e.g. formula
from being passed in as values.
Would love to hear your thoughts!
I think we could improve the library by adding some additional comments in parts of the code that connects back to Notion's API documentation.
What might be the highest priority items to tackle first? And are there any commenting style guides I could reference or follow?
I'd love to contribute if possible.
I can use the following request to remove the value of select.
PATCH https://api.notion.com/v1/pages/:id
{
"properties": {
"ProjectType": {
"select": null
}
}
}
But when used the notionapi pkg to send the request, I cann't set the select to nil. Because the type of select is notionapi.Option
instead of *notionapi.Option
.
type SelectProperty struct {
ID ObjectID `json:"id,omitempty"`
Type PropertyType `json:"type,omitempty"`
Select Option `json:"select"`
}
when I used notionapi.Option{}
as the value of Select
, I got the following errer response:
body.properties.ProjectType.select.id should be defined, instead was `undefined`.
body.properties.ProjectType.select.name should be populated, instead was `""`.
How can I remove the value of select propery?
When fetching a Database with an empty ID I get a misleading error "This API is deprecated.".
client := notionapi.NewClient(notionapi.Token("mytoken"))
db, err := client.Database.Get(context.TODO(), notionapi.DatabaseID(""))
if err != nil {
panic(err.Error())
}
panic: This API is deprecated.
The JSON decoder doesn't know how to parse the type []Block of children attributes.
type ListItem struct {
Text []RichText `json:"text"`
Children []Block `json:"children,omitempty"`
}
...
type NumberedListItemBlock struct {
Object ObjectType `json:"object"`
ID BlockID `json:"id,omitempty"`
Type BlockType `json:"type"`
CreatedTime *time.Time `json:"created_time,omitempty"`
LastEditedTime *time.Time `json:"last_edited_time,omitempty"`
HasChildren bool `json:"has_children,omitempty"`
NumberedListItem ListItem `json:"numbered_list_item"`
}
This is due to Block beign an interface and JSON can't resolve dynamically the Block type.
One way to solve this is creating a Block struct with the common attributes, decoding it first and then resolving the concrete type dynamically.
The ToggleBlock
specifies RichText
and Text
fields which are not documented in the Notion API. I suppose these were left from earlier versions.
Practically, these fields should not be causing any errors as they're serialised only when they're present (i.e. omitempty
). However, the RichText
field misses the omitempty
, causing error responses form Notion.
Would be great if the unnecessary fields can be cleaned up - if that's too much work just adding an omitempty
tag to ToggleBlock.RichText
would do the job too.
Ref. https://github.com/jomei/notionapi/blob/main/block.go#L352.
PS, not sure how you manage contributions here, but given the right permissions I can open up a PR as well.
Many thanks.
Hello!
What do you think about a default mock for the Notion API client? We have that in our fork here (still work in progress). With that, it's quicker to get started with unit tests.
Got exception messag when I query a database
unsupported property type: unique_id
Currently, it isn't possible to unset a date property.
Making it a pointer works.
Line 110 in 24b668e
I've created a small workaround that works:
type EmptyDateProperty struct {
ID notionapi.ObjectID `json:"id,omitempty"`
Type notionapi.PropertyType `json:"type,omitempty"`
Date *notionapi.DateObject `json:"date"`
}
func (p EmptyDateProperty) GetType() notionapi.PropertyType {
return p.Type
}
// [...]
updates["Completed At"] = &EmptyDateProperty{
ID: completedAtProperty.ID,
Type: completedAtProperty.Type,
Date: nil,
}
// [...]
Is it possible to add support for returning pointer to BasicBlock
which is part of every block type? This would help editing the basic block field without needing to type cast Block
interface to concrete block type object.
Something similar to this
type Block interface {
...
...
...
GetBasicBlock() *BasicBlock. <- New addition
}
...
...
...
func (b *BasicBlock) GetBasicBlock() *BasicBlock {
return b
}
If any client wants to edit something from BasicBlock
which is common to all blocks, the client would always need to type cast the block interface to a particular block type and edit the BasicBlock
info even when the client is not making any change specific to given block type.
Thx for the cool package!
Looks like the is_inline
field was added in https://developers.notion.com/changelog/changes-for-june-6-june-20-2022 but I don't see suppport for in DatabaseCreateRequest. Of course I may just be missing it somewhere.
I'd be willing to open a PR supporting is_inline on DB creation if there's interest.
Per notion reference, the compound filters can be nested. Currently, CompoundFilter
is defined as map[FilterOperator][]PropertyFilter
, thus only property filters are allowed inside of a compound filter.
It's better to have a Filter
interface, and let both CompoundFilter
and PropertyFilter
to implement the interface.
I can try to do it in the coming days.
unsupported property type: status
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.