Giter Site home page Giter Site logo

notion-sdk-jvm's Introduction

Notion SDK for Any JVM Language

A simple and easy to use client for the Notion API


Maven Central CI Build

Here is a Notion API SDK for any JVM language users 👋

This project aims to provide a Notion API client for any JVM language developers without hurdles. To realize the goal, its code is written in Kotlin with a nice consideration for Java compatibility.

This SDK works on Android runtime and any distributions based on OpenJDK. With regard to programming languages, this project provides out-of-the-box supports for Java (of course!) and Kotlin. We don't have nice wrappers for some other JVM languages such as Scala, Groovy, and Clojure, but your code using this library should work in the languages too.

Getting Started

You can start using this library just by adding notion-sdk-jvm-core dependency to your project.

For Gradle users:

ext.notionSdkVersion = "1.11.1"
dependencies {
  // This dependency is at least required
  implementation("com.github.seratch:notion-sdk-jvm-core:${notionSdkVersion}")
}

For Maven users:

<properties>
  <notion-sdk.version>1.11.1</notion-sdk.version>
</properties>

<dependencies>
  <dependency>
    <groupId>com.github.seratch</groupId>
    <artifactId>notion-sdk-jvm-core</artifactId>
    <version>${notion-sdk.version}</version>
  </dependency>
</dependencies>

As this library is in Kotlin, using in the same language is the smoothest :) Let's start with the following code, which manipulates Notion pages 👋

import notion.api.v1.NotionClient
import notion.api.v1.model.common.ObjectType
import notion.api.v1.model.pages.PageParent
import notion.api.v1.request.search.SearchRequest
import notion.api.v1.model.pages.PageProperty

fun main() {
  NotionClient(token = System.getenv("NOTION_TOKEN")).use { client ->
    // Find the "Test Database" from the list
    val database = client
      .search(
        query = "Test Database",
        filter = SearchRequest.SearchFilter("database", property = "object")
      )
      .results
      .find { it.asDatabase().properties.containsKey("Severity") }
      ?.asDatabase()
      ?: error("Create a database named 'Test Database' and invite this app's user!")
    // Alternatively if you know the UUID of the Database, use `val database = client.retrieveDatabase("...")`.

    // All the options for "Severity" property (select type)
    val severityOptions = database.properties["Severity"]!!.select!!.options!!
    // All the options for "Tags" property (multi_select type)
    val tagOptions = database.properties["Tags"]!!.multiSelect!!.options!!
    // A user object for "Assignee" property (people type)
    val assignee = client.listUsers().results.first() // Just picking a random user.

    // Create a new page in the database
    val newPage = client.createPage(
      // Use the "Test Database" as this page's parent
      parent = PageParent.database(database.id),
      // Set values to the page's properties
      // (Values of referenced options, people, and relations must be pre-defined before this API call!)
      properties = mapOf(
        "Title" to PageProperty(title = "Fix a bug".asRichText()),
        "Severity" to PageProperty(select = severityOptions.single { it.name == "High" }),
        "Tags" to PageProperty(multiSelect = listOf("Tag1", "Tag2").map { tag -> tagOptions.single { it.name == tag } }),
        "Due" to PageProperty(date = PageProperty.Date(start = "2021-05-13", end = "2021-12-31")),
        "Velocity Points" to PageProperty(number = 3),
        "Assignee" to PageProperty(people = listOf(assignee)),
        "Done" to PageProperty(checkbox = true),
        "Link" to PageProperty(url = "https://www.example.com"),
        "Contact" to PageProperty(email = "[email protected]"),
      )
    )
    
    // Properties can be addressed by their ID too.
    val severityId = newPage.properties["Severity"]!!.id

    // Update properties in the page
    val updatedPage = client.updatePage(
        pageId = newPage.id,
        // Update only "Severity" property
        properties = mapOf(
          severityId to PageProperty(select = severityOptions.single { it.name == "Medium" }),
        )
      )

    // Fetch the latest data of the page
    val retrievedPage = client.retrievePage(newPage.id)
  }
}

private fun String.asRichText(): List<PageProperty.RichText> =
  listOf(PageProperty.RichText(text = PageProperty.RichText.Text(content = this)))

Using in Java

Even when you use this SDK in Java and other languages, all the classes/methods should be accessible. If you find issues, please let us know the issue in this project's issue tracker.

package integration_tests;

import notion.api.v1.NotionClient;
import notion.api.v1.model.search.SearchResults;
import org.junit.Assert;

public class Readme {
  public static void main(String[] args) {
    NotionClient client = new NotionClient(System.getenv("NOTION_TOKEN"));
    try {
      SearchResults results = client.search("Test Database");
      Assert.assertNotNull(results);
    } finally {
      client.close();
    }
  }
}

OAuth Support

The Notion app installation via the OAuth flow is also supported. To learn how to implement it, please check an example app built with Ktor web framework in the core library project.

Plugins

By default, the NotionClient utilizes only JDK's java.net.HttpURLConnection and Gson library for JSON serialization.

For HTTP communications and logging, you can easily switch to other implementations.

Pluggable HTTP Client

As some may know, java.net.HttpURLConnection does not support PATCH request method 😢. Thus, the default httpClient has to make an "illegal reflective access" to overcome the limitation for perfoming PATCH method requests (see this class for details).

The PATCH method workaround does not work with OpenJDK 19 or newer. Thus, if you use PATCH method API calls such as PATCH https://api.notion.com/v1/pages/{page_id}, we recommend other httpClient implementations listed below. If you don't use PATCH method APIs at all and don't want to add any extra dependencies, the default httpClient works fine for you.

// Add this if you use java.net.http.HttpClient in JDK 11+
// Please note that this module does not work on Android runtime
implementation("com.github.seratch:notion-sdk-jvm-httpclient:${notionSdkVersion}")

// Add this if you use OkHttp 5.x (still alpha)
// If you have other dependencies relying on okhttp 5.x (e.g., Retrofit)
implementation("com.github.seratch:notion-sdk-jvm-okhttp5:${notionSdkVersion}")

// Add this if you use OkHttp 4.x
// Although the package name is `okhttp3`, the latest version is 4.x
implementation("com.github.seratch:notion-sdk-jvm-okhttp4:${notionSdkVersion}")

// Add this if you use OkHttp 3.x
// If you have other dependencies relying on okhttp 3.x (e.g., Retrofit)
implementation("com.github.seratch:notion-sdk-jvm-okhttp3:${notionSdkVersion}")

You can switch the httpClient in either of the following ways:

import notion.api.v1.NotionClient
import notion.api.v1.http.JavaNetHttpClient

val client = NotionClient(
    token = System.getenv("NOTION_TOKEN"),
    httpClient = JavaNetHttpClient(),
)

or

import notion.api.v1.NotionClient
import notion.api.v1.http.OkHttp3Client

val client = NotionClient(token = System.getenv("NOTION_TOKEN"))
client.httpClient = OkHttp3Client()

Pluggable Logging

You can change the logger property of a NotionClient instances Currently, this library supports its own stdout logger (default), java.util.logging, and slf4j-api based ones. Here are the steps to switch to an slf4j-api logger. Add the following optional module along with your favorite implementation (e.g., logback-classic, slf4j-simple).

implementation("com.github.seratch:notion-sdk-jvm-slf4j:${notionSdkVersion}") // slf4j-api 1.7
implementation("org.slf4j:slf4j-simple:1.7.36")

Now you can switch to your slf4j logger. As with the httpClient example, you can use the setter method too.

import notion.api.v1.NotionClient
import notion.api.v1.http.JavaNetHttpClient
import notion.api.v1.logging.Slf4jLogger

// for slf4j-simple
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "debug")

val client = NotionClient(
    token = System.getenv("NOTION_TOKEN"),
    httpClient = JavaNetHttpClient(),
    logger = Slf4jLogger(),
)

If you desire to use slf4j-api v2 instead, you can use the module for the major version as below:

implementation("com.github.seratch:notion-sdk-jvm-slf4j2:${notionSdkVersion}") // slf4j-api 2.0
implementation("org.slf4j:slf4j-simple:2.0.0")

Why isn't JSON serialization pluggable?

We don't support other JSON libraries yet. There are two reasons:

Necessity of polymorphic serializers for list objects

In the early development stage of this SDK, we started with kotlinx.serialization. It worked well except for the Search API responses. The results returned by the API requires polymorphic serializers for properties: List<DatabaseProperty | PageProperty> (this is a pseudo-code illustrating the property is a list of union type). We could not find a way to handle the pattern with the library at that time.

Easily enabling camelCased property names

We know a few novel Kotlin libraries do not support the conversions between snake_cased keys and camelCased keys. Although we do respect the opinion and see the benefit, we still prefer consistent field naming in the Java world. This is another major reason why we did not go with either of kotlinx.serialization and Moshi.

Supported Java Runtimes

  • OpenJDK 8 or higher
  • All Android runtime versions supported by Kotlin 1.x

As notion-sdk-jvm-httpclient utilizes java.net.http.HttpClient, the module works with JDK 11 and higher versions.

License

The MIT License

notion-sdk-jvm's People

Contributors

dependabot[bot] avatar eblanchette avatar j-mr-t avatar landicefu avatar looorent avatar mrvik avatar scordio avatar seratch avatar sillycoon avatar software-atelier avatar twisterrob avatar whatasame avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

notion-sdk-jvm's Issues

NotionClient constructor difficult to call from Java

It would be nice to call the NotionClient constructor from Java similar to the Kotlin example

val client = NotionClient(
    token = System.getenv("NOTION_TOKEN"),
    httpClient = JavaNetHttpClient(),
    logger = Slf4jLogger(),
)

It is not that straightforward however, and requires a magic boolean parameter to GsonSerializer constructor

NotionClient client = new NotionClient(System.getenv("NOTION_TOKEN"),
                                       null,
                                       null,
                                       null,
                                       new JavaNetHttpClient(),
                                       new Slf4jLogger(),
                                       new GsonSerializer(false),
                                       "https://api.notion.com/v1");

Might it be possible to add a few Java-friendly constructors? Thank you in advance.

Support children in Heading*Block.Element classes to be able to create the toggle headers

Now the HeadingOneBlock.Element, HeadingTwoBlock.Element, and HeadingThreeBlock.Element classes do not support the children blocks.
It means that we cannot create the toggle headings via the library :(

However, creating a heading with children elements via the pure Notion API is working, the Toggle Heading is created.

Working example:

curl -X PATCH 'https://api.notion.com/v1/blocks/YOUR_PAGE_ID/children' \
  -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \
  -H "Content-Type: application/json" \
  -H "Notion-Version: 2022-02-22" \
  --data '{
  "children": [
    {
      "object": "block",
      "type": "heading_1",
      "heading_1": {
        "rich_text": [{ "type": "text", "text": { "content": "Heading 1 from script (try 2)" } }],
        "children": [
          {
            "object": "block",
            "type": "paragraph",
            "paragraph": {
              "rich_text": [
                {
                  "type": "text",
                  "text": {
                    "content": "Lacinato kale is a variety of kale with a long tradition in Italian cuisine, especially that of Tuscany. It is also known as Tuscan kale, Italian kale, dinosaur kale, kale, flat back kale, palm tree kale, or black Tuscan palm.",
                    "link": { "url": "https://en.wikipedia.org/wiki/Lacinato_kale" }
                  }
                }
              ]
            }
          }
        ]
      }
    }
  ]
}'

Create database with multi_select doesn't work

This is my code, and if I remove the Tags column then it works.

    client.createDatabase(
            parent = DatabaseParent.page(page.id),
            title = listOf(DatabaseProperty.RichText(text = DatabaseProperty.RichText.Text(content = folderName), plainText = folderName)),
            isInline = true,
            properties = mapOf(
                "Name" to TitlePropertySchema(),
                "isFolder" to CheckboxPropertySchema(),
                "Tags" to MultiSelectPropertySchema(), // this cause the problem
                "Mime" to RichTextPropertySchema(),
                "File" to FilePropertySchema(),
                "Size In Bytes" to NumberPropertySchema(),
                "Upload Complete" to CheckboxPropertySchema(),
                "Thumbnail" to FilePropertySchema(),
            )
        )

The error response is:

body.properties.Tags.people should be defined, instead was `undefined`.
    body.properties.Tags.files should be defined, instead was `undefined`.
    body.properties.Tags.email should be defined, instead was `undefined`.
    body.properties.Tags.phone_number should be defined, instead was `undefined`.
    body.properties.Tags.date should be defined, instead was `undefined`.
    body.properties.Tags.checkbox should be defined, instead was `undefined`.
    body.properties.Tags.created_by should be defined, instead was `undefined`.
    body.properties.Tags.created_time should be defined, instead was `undefined`.
    body.properties.Tags.last_edited_by should be defined, instead was `undefined`.
    body.properties.Tags.last_edited_time should be defined, instead was `undefined`.

Java code examples in readme

Thanks for this great sdk!

I'm wondering if you can add few Java examples.
I'm stuck at working with the SDK API and like for example how to use the filters (PropertyFilter and CompoundFilter) and sorts in QueryDatabase.
Unfortunately for people like me who isn't familiar with Kotlin, it isn't easy to fully understand the source code or the generated code in the decompiled files

Missing "status" database filter condition

Hi,

looks like there are some database filters in Notion which are missing in the library. For example, there is no status filter condition inside notion.api.v1.model.databases.query.filter.condition

NullPointerException in model.databases.Database.hashCode

Are Kotlin model data classes appropriate for use as keys in e.g. Maps? I am running into NullPointerException with

Database database = client.retrieveDatabase(databaseId);
MutableNetwork<Database, DatabaseProperty> network = NetworkBuilder.directed().build();
network.addNode(database);
java.lang.NullPointerException
	at notion.api.v1.model.databases.Database.hashCode(Database.kt)
	at java.base/java.util.HashMap.hash(HashMap.java:340)
	at java.base/java.util.HashMap.containsKey(HashMap.java:592)
	at com.google.common.graph.MapIteratorCache.containsKey(MapIteratorCache.java:104)
	at com.google.common.graph.StandardNetwork.containsNode(StandardNetwork.java:198)
	at com.google.common.graph.StandardMutableNetwork.addNode(StandardMutableNetwork.java:57)

Option classes in database properties can be shared

This inconsistency is really strange:

open class MultiSelect @JvmOverloads constructor(var options: List<Option>? = null) {
open class Option(
var id: String? = null,
var name: String? = null,
var color: OptionColor? = null,
)
}
data class Select @JvmOverloads constructor(val options: List<Option>? = null) {
data class Option(
val id: String? = null,
val name: String? = null,
val color: OptionColor? = null,
)
}

and bit me while trying to do some logic on List<DatabaseProperty.MultiSelect.Option> objects.

I got this output:

PageProperty(id=bfb4764a-e6f7-4ea8-b95c-5299223de91d, type=null, title=null, richText=null, select=null, status=null, multiSelect=[notion.api.v1.model.databases.DatabaseProperty$MultiSelect$Option@5d0e31f8], number=null, date=null, people=null, checkbox=null, url=null, phoneNumber=null, email=null, files=null, relation=null, formula=null, rollup=null, createdBy=null, lastEditedBy=null, createdTime=null, lastEditedTime=null, hasMore=null)

half data class, half not. There is no external way of adding a toString onto multiSelect field to see the values (i.e no workaround), so have to do it in this codebase.

Captions on Images and Videos are not deserialized as Rich-Text

Problem encountered

When fetching Blocks including images and videos with captions, the caption seems to be parsed as a String and not a Rich-Text.
For a video, GSON produces:

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected STRING but was BEGIN_OBJECT at path $.video.caption[0]

For an image, GSON produces:

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected STRING but was BEGIN_OBJECT at path $.image.caption[0]

How to reproduce

  • Create a page including a video or an image
  • Add a caption on this block
  • Fetch this Block by retrieving all the blocks of this page

Pull Request

I have created a PR to fix this.

Notion's API returns URL encoded PageProperty's ids

When fetching a page from Notion's API (https://api.notion.com/v1/pages/:id), some property ids can contain special characters (https://developers.notion.com/reference/page).

For example: t%7B%7Di, zl%7Da. Notion's documentation does not state if these values are URL encoded or not.
However, this line re-encodes the property id:

"$baseUrl/pages/${urlEncode(request.pageId)}/properties/${urlEncode(request.propertyId)}"

This is particularly a problem since Notion's version 2022-06-28, which forces the use of https://api.notion.com/v1/pages/:id/properties/:propertyId.

I am wondering whether the property id must be encoded when using retrievePagePropertyItem or not. Or, maybe PageProperty.id should be URL-decoded when deserializing.

val id: String = UUID.randomUUID().toString(),

My current workaround is to URL-decode the property id before using retrievePagePropertyItem.

Upgrading from 1.4.x to 1.5.x Properties Issue

Hello,

I am experiencing this weird issue when I upgrade to the latest version. For some reason, properties aren't being loaded. I am still able to create objects but I cannot retrieve them correctly. Any suggesstions? Thanks

Sample

V 1.4

	"properties": {
		"Owner": {
			"id": "%3A%7BT%3E",
			"type": "people",
			"people": []
		},
		"🔒 Order": {
			"id": "BPEC",
			"type": "relation",
			"relation": [
				{
					"id": "a9fd4022-d841-49b9-bef8-cff2cfb7831f"
				}
			]
		},

V 1.5

	"properties": {
		"Owner": {
			"id": "%3A%7BT%3E"
		},
		"🔒 Order": {
			"id": "BPEC"
		},

Question regarding what I can do with the client.

Where can I see what operations I can call with the NotionClient? I'm trying to develop a personal app in Java. I can't seem to find any other operations besides the "search" you have on the README, which works fine, but doesn't return the children of the database given,

Thanks in advance.

How to unset a date in a database table

Hi,

I am using this sdk to manipulate some cells and I have a hard time to unset a date property.
Basically, a date is set on the property and I want to delete it.

I have tried setting a null object as date property and setting a null object for start, end, timezone on a date property.

Both give me errors.

With a null as data property, I got:

Got an error from Notion (status: 400, code: validation_error, message: body failed validation. Fix one:
body.properties.Date de Gain.title should be defined, instead was `undefined`.
body.properties.Date de Gain.rich_text should be defined, instead was `undefined`.
body.properties.Date de Gain.number should be defined, instead was `undefined`.
body.properties.Date de Gain.url should be defined, instead was `undefined`.
body.properties.Date de Gain.select should be defined, instead was `undefined`.
body.properties.Date de Gain.multi_select should be defined, instead was `undefined`.
body.properties.Date de Gain.people should be defined, instead was `undefined`.
body.properties.Date de Gain.email should be defined, instead was `undefined`.
body.properties.Date de Gain.phone_number should be defined, instead was `undefined`.
body.properties.Date de Gain.date should be defined, instead was `undefined`.
body.properties.Date de Gain.checkbox should be defined, instead was `undefined`.
body.properties.Date de Gain.relation should be defined, instead was `undefined`.
body.properties.Date de Gain.files should be defined, instead was `undefined`.

With a null object for start, end, timezone on a date property, I got:

Got an error from Notion (status: 400, code: validation_error, message: body failed validation: body.properties.Date de Gain.date.start should be defined, instead was `undefined`.)

Using the empty constructor for date property, I got the same error.

Is there another way to unset a date property ?

No values in page properties

All the properties of pages gotten via both NotionClient#queryDatabase and NotionClient#retrievePage have no values. I cannot understand why.

Notion sdk v1.7.0 with httpclient.

Update block

Hello,

thanks for your SDK.

I can easily create a new page, but I can't modify one.

For create a new page, this code work :

return clientNotion.updatePage(
            pageId = PAGE_ID,
            properties = mapOf(
                "Name" to prop(title = listOf(RichText(text = Text(content = "Titre modifié")))),
                "Date" to prop(date = Date(start = "2022-07-05", end = "2022-07-29"))
            )
        )

But I can't find the right syntax regarding the modification of my page with the associated block ID :

return clientNotion.updateBlock(
            blockId = "6679e3d0ec3341f487eb633c7a59bfa1",
            elements = mapOf(
                "Paragraph" to prop(type = ParagraphBlock)
            )
        )

Any idea ?

Thanks :)

Example code in README does not work for me

I was trying to implement code that creates new page like it is in the README guide. I get stuck on this. Notion API says something like:

body failed validation. Fix one:\nbody.parent.database_id should be defined, instead was undefined.\nbody.properties.Title should be not present, instead was {\"id\":\"199a7d2e-28f4-4d4d-9269-2bb9d739740e\",\"title\":[{...."}

I thought something wrong with page parent, but it's just "Title" instead of "title" in properties.

I think would be good to update the README file.

"Title" to prop(title = listOf(prop.RichText(text = prop.RichText.Text(content = "Fix a bug"))))

This and others lines with property types

Android - Unresolved reference: NotionClient

I am trying to import the library to an Android project, but the compiler complains "Unresolved reference: NotionClient".

Kotlin version 1.5.30.
Dependencies: com.github.seratch:notion-sdk-jvm-core:0.1.16

Source code:

import notion.api.v1.NotionClient
import notion.api.v1.model.pages.PageParent
import notion.api.v1.model.pages.PageProperty as prop

class NotionApi {
}

Is Android supported?

"xxx" is not a property that exists

I am getting this error:

Exception in thread "pool-1-thread-1" notion.api.v1.exception.NotionAPIError: Got an error from Notion (status: 400, code: validation_error, message: h%3A%7BD is not a property that exists.)
	at notion.api.v1.endpoint.PagesSupport$DefaultImpls.retrievePagePropertyItem(PagesSupport.kt:183)
	at notion.api.v1.NotionClient.retrievePagePropertyItem(NotionClient.kt:12)
	at notion.api.v1.endpoint.PagesSupport$DefaultImpls.retrievePagePropertyItem(PagesSupport.kt:162)
	at notion.api.v1.NotionClient.retrievePagePropertyItem(NotionClient.kt:12)

Whereas the property ID has been retrieved via NotionClient#queryDatabase.

Debug log:
database query, you can clearly see the property is here (ApiTaskID)

11:15:09.094 [main] [DEBUG] NOTION CLIENT - Sending a request:
POST https://api.notion.com/v1/databases/6f825874-8fb8-4d32-8e42-3dbfff883005/query
body   {"filter":{"property":"ApiUpToDate","formula":{"checkbox":{"equals":false}}}}
11:15:10.635 [main] [DEBUG] NOTION CLIENT - Received a response (1540 millis):
status  200
body    {"object":"list","results":[{"object":"page","id":"9d973a16-2b37-49a1-b14f-a840c00a99e7","created_time":"2022-08-30T13:34:00.000Z","last_edited_time":"2022-08-31T09:14:00.000Z","created_by":{"object":"user","id":"b46685fb-557e-4889-a339-438228e012c4"},"last_edited_by":{"object":"user","id":"b46685fb-557e-4889-a339-438228e012c4"},"cover":null,"icon":null,"parent":{"type":"database_id","database_id":"6f825874-8fb8-4d32-8e42-3dbfff883005"},"archived":false,"properties":{"ApiModifier":{"id":"MFmp"},"Dernière modification":{"id":"UokX"},"ApiUpToDate":{"id":"Xbus"},"Fait":{"id":"YK%5BT"},"Date":{"id":"gHHc"},"ApiTaskID":{"id":"h%3A%7BD"},"ApiValidationDate":{"id":"kby%3D"},"Échéance":{"id":"pP_F"},"Délai":{"id":"tDIg"},"Nom":{"id":"title"},"Matière":{"id":"e8daffc2-e8ba-4ffc-9b3c-03be91856f94"}},"url":"https://www.notion.so/Truca-9d973a162b3749a1b14fa840c00a99e7"},{"object":"page","id":"cbe5e2ca-6bc5-4270-84a3-c2415649cf2a","created_time":"2022-06-15T15:34:00.000Z","last_edited_time":"2022-08-31T09:14:00.000Z","created_by":{"object":"user","id":"b46685fb-557e-4889-a339-438228e012c4"},"last_edited_by":{"object":"user","id":"b46685fb-557e-4889-a339-438228e012c4"},"cover":null,"icon":null,"parent":{"type":"database_id","database_id":"6f825874-8fb8-4d32-8e42-3dbfff883005"},"archived":false,"properties":{"ApiModifier":{"id":"MFmp"},"Dernière modification":{"id":"UokX"},"ApiUpToDate":{"id":"Xbus"},"Fait":{"id":"YK%5BT"},"Date":{"id":"gHHc"},"ApiTaskID":{"id":"h%3A%7BD"},"ApiValidationDate":{"id":"kby%3D"},"Échéance":{"id":"pP_F"},"Délai":{"id":"tDIg"},"Nom":{"id":"title"},"Matière":{"id":"e8daffc2-e8ba-4ffc-9b3c-03be91856f94"}},"url":"https://www.notion.so/Prout-cbe5e2ca6bc5427084a3c2415649cf2a"}],"next_cursor":null,"has_more":false,"type":"page","page":{}}

property query with the property ID returned before

11:15:10.893 [pool-1-thread-1] [DEBUG] NOTION CLIENT - Sending a request:
GET https://api.notion.com/v1/pages/9d973a16-2b37-49a1-b14f-a840c00a99e7/properties/h%253A%257BD
11:15:11.113 [pool-1-thread-1] [DEBUG] NOTION CLIENT - Received a response (220 millis):
status  400
body    {"object":"error","status":400,"code":"validation_error","message":"h%3A%7BD is not a property that exists."}
Exception in thread "pool-1-thread-1" notion.api.v1.exception.NotionAPIError: Got an error from Notion (status: 400, code: validation_error, message: h%3A%7BD is not a property that exists.)
	at notion.api.v1.endpoint.PagesSupport$DefaultImpls.retrievePagePropertyItem(PagesSupport.kt:183)
	at notion.api.v1.NotionClient.retrievePagePropertyItem(NotionClient.kt:12)
	at notion.api.v1.endpoint.PagesSupport$DefaultImpls.retrievePagePropertyItem(PagesSupport.kt:162)
	at notion.api.v1.NotionClient.retrievePagePropertyItem(NotionClient.kt:12)
	at notiontasks.NotionTasksApp.updateTask(NotionTasksApp.java:118)
	at notiontasks.NotionTasksApp.lambda$0(NotionTasksApp.java:107)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:833)

rich_text prop in PagePropertyItem is no longer a list

Hi,

During Retrieve a page property item , the rich_text return from api endpoint is no-longer a list.

"results": [ { "object": "property_item", "id" "kjPO", "type": "rich_text", "rich_text": { "type": "text", "text": { "content": "Avocado ", "link": null }, "annotations": { "bold": false, "italic": false, "strikethrough": false, "underline": false, "code": false, "color": "default" }, "plain_text": "Avocado ", "href": null } }, ...

However, the richText defined in PagePropertyItem is still a List<RichText>

var richText: List<PageProperty.RichText>? = null,

This causes json parsing exception during retrievePropertyItem call.

javax.ejb.EJBException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 101 path $.results[0].rich_text

Is it possible to create nested compound filter?

Hi!

Is it possible to create nested compound filters for QueryDatabase?

Example:

{
    "filter": {
        "or": [
            {
                "property": "Description",
                "rich_text": {
                    "contains": "fish"
                }
            },
            {
                "and": [
                    {
                        "property": "Food group",
                        "select": {
                            "equals": "🥦Vegetable"
                        }
                    },
                    {
                        "property": "Is protein rich?",
                        "checkbox": {
                            "equals": true
                        }
                    }
                ]
            }
        ]
    }
}

I checked this for CompoundFilter:
https://github.com/seratch/notion-sdk-jvm/blob/v0.2.0/core/src/test/kotlin/integration_tests/DatabasesTest.kt#L131-L155

I managed to use the Compound Filter but could not find a way to have the nested compound.

Thank you for your help!

Users can now delete Block objects

https://developers.notion.com/changelog/users-can-now-delete-block-objects

The Notion API now supports the Delete a block endpoint for all supported block types (include pages). The endpoint mirrors the behavior in the Notion application UI where items are added to the "Trash" bucket. In addition, the Block object now returns a boolean archived field to denote if the block has been deleted.

After deleting (archiving) the block, it can be unarchived using the Update a block or Update page endpoint with the body { archived: false }.

Is it possible to filter a query?

Hi! I Is there a way to apply filter to a query?

I am requesting a quite big database and I am not getting back all the records present in the db, response code 200. I am not sure there is a limit to the size of the json. I have tried with another client and I am getting the same issue.

I just need few records from that database, maybe with a filter I can solve the problem.

On QueryDatabaseRequest I see there is a constructor with filter and sorting in the signature, not sure how to use them or if it has been implemented at all.

Thank you for your help.

Error in SelectPropertySchema for database creation

The current implementation of SelectPropertySchema of DatabasePropertySchema is

open class SelectOptionSchema
@JvmOverloads
constructor(val name: String, val color: OptionColor? = null)

open class SelectPropertySchema
@JvmOverloads
constructor(val select: List<SelectOptionSchema>? = null) : DatabasePropertySchema

, which converts to

{
    "select": [
        {
                "color": String?,
                "name": String
        },
        ...
    ]
}

However, when I used it in the following code, I got an error for wrong schema:

client.use { c ->
    c.updateDatabase(
           id = someDatabaseId,
           properties = mapOf(
                   "Some Property" to SelectPropertySchema((1..3).map { SelectOptionSchema(name = it.toString()) })
           )
    )
}

I believe the schema should be

{
    "select": {
          "options": [
                 {
                         "color": String?,
                         "name": String
                  },
                  ...
          ]
    }
}

, which should be in the following form:

open class SelectOptionSchema
@JvmOverloads
constructor(val options: List<SelectOption>) {
    open class SelectOption(val name: String, val color: OptionColor? = null)
}

open class SelectPropertySchema
@JvmOverloads
constructor(val select: SelectOptionSchema? = null) : DatabasePropertySchema

Nested Compound filter are not valid as Notion Request ?

According to Notion documentation it is possible to combine up to two nesting levels deep.

In addition the documentation said that a compound filter looks like this :

{
    "filter": {
        "and": [
            {
                "property": "Seen",
                "checkbox": {
                    "equals": false
                }
            },
            {
                "property": "Yearly visitor count",
                "number": {
                    "greater_than": 1000000
                }
            }
        ]
    }
}

So according to my understanding of all of this, if I use multiple nested CompoundFilter, I guess the request send to Notion should look like this :

{
  "filter": {
    "and": [
      {
        "filter": {
          "or": [
            {
              "property": "Seen",
              "checkbox": {
                "equals": false
              }
            },
            {
              "filter": {
                "and": [
                  {
                    "property": "Seen",
                    "checkbox": {
                      "equals": true
                    }
                  },
                  {
                    "property": "Yearly visitor count",
                    "number": {
                      "greater_than": 1000000
                    }
                  }
                ]
              }
            }
          ]
        }
      },
      {
        "property": "Yearly visitor count",
        "number": {
          "greater_than": 1000000
        }
      }
    ]
  }
}

If everything I told until yet is correct then I have an issue with the CompoundFilter built from my request.
Basically I got this request body that are sent to Notion :

{
  "filter": {
    "and": [
      {
        "property": "Machine",
        "relation": {
          "contains": "<my id>"
        }
      },
      {
        "or": [
          {
            "and": [
              {
                "property": "endDate",
                "formula": {
                  "property": "endDate",
                  "date": {
                    "before": "2022-02-01"
                  }
                }
              },
              {
                "property": "startDate",
                "date": {
                  "on_or_after": "2022-01-01"
                }
              }
            ]
          }
        ]
      }
    ]
  },
  "sorts": []
}

which does not seems to be similar to the one I built as example.

Basically I have 3 properties (Machine as an Id, startDate as a date and endDate as a date build with a formula.

The Java code that build this request is the following :

    
    PropertyFilter machineFilter = new PropertyFilter("Machine");
    machineFilter.setRelation(new RelationFilter(<my id>));
    
    PropertyFilter endDateFilter = new PropertyFilter("endDate);
    FormulaFilter endDateFormulaFilter = new FormulaFilter();
    endDateFormulaFilter.setDate(
            new DateFilter(
                    null,
                    endDateIsoFormat));
    endDateFilter.setFormula(endDateFormulaFilter);

    PropertyFilter periodFilter = new PropertyFilter("startDate");
    periodFilter.setDate(
            new DateFilter(
                    null,
                    null,
                    null,
                    null,
                    startDateIsoFormat));

    CompoundFilter compoundFilter = new CompoundFilter();
    compoundFilter.setAnd(List.of(endDateFilter, periodFilter));

    CompoundFilter filterDates = new CompoundFilter();
    filterDates.setOr(List.of(dateFilterBetweenInterval));
    
    CompoundFilter filters = new CompoundFilter();
    filters.setAnd(List.of(machineFilter, filterDates));

Is there something wrong in my code ?
Is there another way to do that ?

How to retrieve value when property is rollup?

First of all thanks for creating this client, helping me a lot.

I would need to retrieve the value of some rollup properties, but not sure how to do it.

This is how I am accessing value for other properties type:
List<Page> database = client.queryDatabase(new QueryDatabaseRequest(DatabaseList.REPERTOIRE_DATABASE)).getResults(); String title = database.get(0).getProperties().get("Order").getTitle().get(0).getPlainText()

I don't seem to find a way to access to the rollup values using the same path:
Rollup musicRollup = database.get(0).getProperties().get("Music Rollup").getRollup()

The Rollup class contains only the 3 key related to the rollup itself but not the content of the rollup.

Is there another way I should access the rollup values?

Thank you for your help!

NotionClient#appendBlockChildren fails (seems to be Jersey + Http PATCH method bug)

client.appendBlockChildren(pageId, blocksToAppend); fails with the following exception:

Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make field private final sun.net.www.protocol.https.DelegateHttpsURLConnection sun.net.www.protocol.https.HttpsURLConnectionImpl.delegate accessible: module java.base does not "opens sun.net.www.protocol.https" to unnamed module @5cdd8682
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
	at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178)
	at java.base/java.lang.reflect.Field.setAccessible(Field.java:172)
	at notion.api.v1.http.HttpUrlConnPatchMethodWorkaround.setPatchRequestMethod$lambda-0(HttpUrlConnPatchMethodWorkaround.kt:45)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:569)
	at notion.api.v1.http.HttpUrlConnPatchMethodWorkaround.setPatchRequestMethod(HttpUrlConnPatchMethodWorkaround.kt:36)
	at notion.api.v1.http.HttpUrlConnNotionHttpClient.patchTextBody(HttpUrlConnNotionHttpClient.kt:86)
	at notion.api.v1.http.NotionHttpClient$DefaultImpls.patchTextBody$default(NotionHttpClient.kt:24)
	at notion.api.v1.endpoint.BlocksSupport$DefaultImpls.appendBlockChildren(BlocksSupport.kt:129)
	at notion.api.v1.NotionClient.appendBlockChildren(NotionClient.kt:12)
	at notion.api.v1.endpoint.BlocksSupport$DefaultImpls.appendBlockChildren(BlocksSupport.kt:124)
	at notion.api.v1.NotionClient.appendBlockChildren(NotionClient.kt:12)
	at com.dv.telegram.notion.NotionWrapper.main(NotionWrapper.java:51)

The root bug seems to be here — eclipse-ee4j/jersey#4825

Missing Checkbox PropertyFilter + can't set property name

Hi,
I found that the checkbox entry was missing in notion.api.v1.model.common. Adding

@SerializedName("checkbox") Checkbox("checkbox");

should solve this part of the issue.

Also I can't found how to set the name of the property :
My checkbox is called Done in notion
if i managed to make the following code where i can't set the property name

println(notionClient.queryDatabase(databaseId = database_id, filter = PropertyFilter(PropertyType.Checkbox (Missing as of now), checkbox = CheckboxFilter("true"))))

I end wit this error ; notion.api.v1.exception.NotionAPIError: Got an error from Notion (status: 400, code: validation_error, message: Could not find property with name or id: checkbox)
I suppose it take the property type as name

Additionnal thing but not important :
CheckboxFilter equals take a String => Shouldn't it be a bool ?

Thanks
Denis

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.