This repository has been archived by the owner on Nov 3, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor, apply explicit API mode, enhance the token logic
- Loading branch information
1 parent
9426fb8
commit 614326e
Showing
24 changed files
with
546 additions
and
404 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,91 +1,70 @@ | ||
package com.petersamokhin.notionapi | ||
|
||
import com.petersamokhin.notionapi.mapper.mapTable | ||
import com.petersamokhin.notionapi.model.NotionCredentials | ||
import com.petersamokhin.notionapi.model.NotionTable | ||
import com.petersamokhin.notionapi.model.error.NotionAuthException | ||
import com.petersamokhin.notionapi.model.request.LoadPageChunkRequestBody | ||
import com.petersamokhin.notionapi.model.request.Loader | ||
import com.petersamokhin.notionapi.model.request.QueryCollectionRequestBody | ||
import com.petersamokhin.notionapi.model.response.NotionResponse | ||
import com.petersamokhin.notionapi.request.LoadPageChunkRequest | ||
import com.petersamokhin.notionapi.request.QueryNotionCollectionRequest | ||
import com.petersamokhin.notionapi.request.base.NotionRequest | ||
import com.petersamokhin.notionapi.utils.dashifyId | ||
import io.ktor.client.* | ||
import io.ktor.client.features.* | ||
import io.ktor.client.request.* | ||
import io.ktor.client.statement.* | ||
import io.ktor.http.* | ||
import io.ktor.util.* | ||
import kotlinx.serialization.json.Json | ||
|
||
class Notion internal constructor(token: String, private var httpClient: HttpClient) { | ||
init { | ||
httpClient = httpClient.config { | ||
defaultRequest { | ||
header(HttpHeaders.Cookie, "$NOTION_TOKEN_COOKIE_KEY=$token") | ||
} | ||
} | ||
} | ||
public interface Notion { | ||
public val token: String | ||
|
||
suspend fun getCollection(json: Json, pageId: String, sortColumns: Boolean = false): NotionTable? { | ||
val normalPageId = pageId.dashifyId() | ||
val page = loadPage(normalPageId) | ||
public suspend fun getCollection( | ||
json: Json, | ||
pageId: String, | ||
sortColumns: Boolean = false | ||
): NotionTable? | ||
|
||
val collectionId = page.recordMap.collectionsMap?.keys?.firstOrNull() ?: return null | ||
val collectionViewId = page.recordMap.collectionViewsMap?.keys?.firstOrNull() ?: return null | ||
public suspend fun loadPage( | ||
pageId: String, | ||
limit: Int = 50 | ||
): NotionResponse | ||
|
||
val collectionResponse = queryCollection(collectionId, collectionViewId) | ||
public suspend fun queryCollection( | ||
collectionId: String, | ||
collectionViewId: String, | ||
limit: Int = 70 | ||
): NotionResponse | ||
|
||
return collectionResponse.mapTable(json, sortColumns = sortColumns) | ||
} | ||
public fun setHttpClient(newHttpClient: HttpClient) | ||
|
||
suspend fun loadPage(pageId: String, limit: Int = 50): NotionResponse { | ||
return LoadPageChunkRequest(httpClient).execute( | ||
LoadPageChunkRequestBody(pageId, limit, 0, false) | ||
) | ||
} | ||
public fun setToken(token: String) | ||
|
||
suspend fun queryCollection(collectionId: String, collectionViewId: String, limit: Int = 70): NotionResponse { | ||
return QueryNotionCollectionRequest(httpClient).execute( | ||
QueryCollectionRequestBody( | ||
collectionId, collectionViewId, Loader(limit, false, "table") | ||
) | ||
) | ||
} | ||
public fun close() | ||
|
||
fun close() = httpClient.close() | ||
|
||
fun setHttpClient(newHttpClient: HttpClient) { | ||
httpClient = newHttpClient | ||
} | ||
|
||
companion object { | ||
private const val NOTION_TOKEN_COOKIE_KEY = "token_v2" | ||
public companion object { | ||
public const val TOKEN_COOKIE_KEY: String = "token_v2" | ||
|
||
@JvmStatic | ||
fun fromToken(token: String, httpClient: HttpClient): Notion { | ||
return Notion(token, httpClient) | ||
} | ||
public fun fromToken(token: String, httpClient: HttpClient): Notion = | ||
NotionImpl(token, httpClient) | ||
|
||
@OptIn(KtorExperimentalAPI::class) | ||
@JvmStatic | ||
suspend fun fromEmailAndPassword(credentials: NotionCredentials, httpClient: HttpClient): Notion { | ||
public suspend fun fromEmailAndPassword(credentials: NotionCredentials, httpClient: HttpClient): Notion { | ||
val endpoint = "${NotionRequest.API_BASE_URL}/${NotionRequest.Endpoint.LOGIN_WITH_EMAIL}" | ||
val response = httpClient.post<HttpResponse>(endpoint) { | ||
headers.appendAll(NotionRequest.BASE_HEADERS) | ||
contentType(ContentType.Application.Json) | ||
body = credentials | ||
} | ||
|
||
println(response) | ||
|
||
val token = response.headers.getAll(HttpHeaders.SetCookie)?.firstOrNull { | ||
it.contains("$NOTION_TOKEN_COOKIE_KEY=", true) | ||
}?.split("; ")?.firstOrNull { | ||
it.contains("$NOTION_TOKEN_COOKIE_KEY=", true) | ||
}?.split("=")?.getOrNull(1) ?: throw NotionAuthException("No $NOTION_TOKEN_COOKIE_KEY in headers!") | ||
val token = response.headers.getAll(HttpHeaders.SetCookie) | ||
?.asSequence() | ||
?.map(::parseServerSetCookieHeader) | ||
?.find { it.name == TOKEN_COOKIE_KEY } | ||
?.value | ||
.orEmpty() | ||
|
||
return fromToken(token, httpClient) | ||
return fromToken( | ||
token = token, | ||
httpClient = httpClient | ||
) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package com.petersamokhin.notionapi | ||
|
||
import com.petersamokhin.notionapi.mapper.mapTable | ||
import com.petersamokhin.notionapi.model.NotionTable | ||
import com.petersamokhin.notionapi.model.request.LoadPageChunkRequestBody | ||
import com.petersamokhin.notionapi.model.request.Loader | ||
import com.petersamokhin.notionapi.model.request.QueryCollectionRequestBody | ||
import com.petersamokhin.notionapi.model.response.NotionResponse | ||
import com.petersamokhin.notionapi.request.LoadPageChunkRequest | ||
import com.petersamokhin.notionapi.request.QueryNotionCollectionRequest | ||
import com.petersamokhin.notionapi.utils.dashifyId | ||
import io.ktor.client.* | ||
import io.ktor.client.features.* | ||
import io.ktor.client.request.* | ||
import io.ktor.http.* | ||
import kotlinx.serialization.json.Json | ||
import java.util.concurrent.atomic.AtomicReference | ||
|
||
internal class NotionImpl internal constructor( | ||
token: String, | ||
httpClient: HttpClient | ||
) : Notion { | ||
private val _token = AtomicReference(token) | ||
|
||
private var _httpClient: HttpClient = | ||
httpClient.withToken() | ||
|
||
override val token: String | ||
get() = _token.get() | ||
|
||
override suspend fun getCollection(json: Json, pageId: String, sortColumns: Boolean): NotionTable? { | ||
val normalPageId = pageId.dashifyId() | ||
val page = loadPage(normalPageId) | ||
|
||
val collectionId = page.recordMap.collectionsMap?.keys?.firstOrNull() ?: return null | ||
val collectionViewId = page.recordMap.collectionViewsMap?.keys?.firstOrNull() ?: return null | ||
|
||
val collectionResponse = queryCollection(collectionId, collectionViewId) | ||
|
||
return collectionResponse.mapTable(json, sortColumns = sortColumns) | ||
} | ||
|
||
override suspend fun loadPage(pageId: String, limit: Int): NotionResponse = | ||
LoadPageChunkRequest(_httpClient).execute( | ||
LoadPageChunkRequestBody(pageId, limit, 0, false) | ||
) | ||
|
||
override suspend fun queryCollection( | ||
collectionId: String, | ||
collectionViewId: String, | ||
limit: Int | ||
): NotionResponse = | ||
QueryNotionCollectionRequest(_httpClient).execute( | ||
QueryCollectionRequestBody( | ||
collectionId, collectionViewId, Loader(limit, false, "table") | ||
) | ||
) | ||
|
||
override fun setHttpClient(newHttpClient: HttpClient) { | ||
close() | ||
_httpClient = newHttpClient.withToken() | ||
} | ||
|
||
override fun setToken(token: String) { | ||
_token.set(token) | ||
} | ||
|
||
override fun close(): Unit = | ||
_httpClient.close() | ||
|
||
private fun HttpClient.withToken(): HttpClient = | ||
config { | ||
defaultRequest { | ||
header(HttpHeaders.Cookie, "${Notion.TOKEN_COOKIE_KEY}=${_token.get()}") | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
27 changes: 27 additions & 0 deletions
27
src/main/kotlin/com/petersamokhin/notionapi/model/NotionColumn.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package com.petersamokhin.notionapi.model | ||
|
||
import com.petersamokhin.notionapi.model.response.NotionColumnType | ||
import kotlinx.serialization.SerialName | ||
import kotlinx.serialization.Serializable | ||
|
||
@Serializable | ||
public sealed class NotionColumn { | ||
public abstract val name: String | ||
public abstract val type: NotionColumnType | ||
|
||
@Serializable | ||
@SerialName("single_value") | ||
public data class SingleValue( | ||
override val name: String, | ||
override val type: NotionColumnType, | ||
val value: NotionProperty? | ||
) : NotionColumn() | ||
|
||
@Serializable | ||
@SerialName("multi_value") | ||
public data class MultiValue( | ||
override val name: String, | ||
override val type: NotionColumnType, | ||
val values: List<NotionProperty> | ||
) : NotionColumn() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.