Refactor to make this more obviously HFF's Notion API client.
This commit is contained in:
177
src/hff_notion_api_client.nim
Normal file
177
src/hff_notion_api_client.nim
Normal file
@@ -0,0 +1,177 @@
|
||||
import std/httpclient, std/json, std/logging, std/sequtils, std/tables, std/strutils
|
||||
|
||||
import buffoonery/jsonutils
|
||||
|
||||
import hff_notion_api_client/config
|
||||
import hff_notion_api_client/models
|
||||
import hff_notion_api_client/sequtils_ext
|
||||
import hff_notion_api_client/utils
|
||||
|
||||
type
|
||||
NotionClientConfig* = object
|
||||
apiBaseUrl*: string
|
||||
apiVersion*: string
|
||||
configDbId*: string
|
||||
integrationToken*: string
|
||||
|
||||
NotionClient* = ref object
|
||||
http: HttpClient
|
||||
apiBaseUrl: string
|
||||
config*: HffNotionConfig
|
||||
|
||||
proc loadNotionConfig(http: HttpClient, cfg: NotionClientConfig): HffNotionConfig =
|
||||
let url = cfg.apiBaseUrl & "/databases/" & cfg.configDbId & "/query"
|
||||
let body = $(%*{ "page_size": NOTION_MAX_PAGE_SIZE })
|
||||
|
||||
debug "loadNotionConfig\n\tPOST " & url & "\n\t" & $body
|
||||
let resp = http.postContent(url, body)
|
||||
let resultsJson = parseJson(resp)["results"]
|
||||
|
||||
result = parseHffNotionConfig(resultsJson)
|
||||
|
||||
proc initNotionClient*(cfg: NotionClientConfig): NotionClient =
|
||||
result = NotionClient(
|
||||
apiBaseUrl: cfg.apiBaseUrl,
|
||||
http: newHttpClient(headers = newHttpHeaders([
|
||||
("Content-Type", "application/json"),
|
||||
("Authorization", "Bearer " & cfg.integrationToken),
|
||||
("Notion-Version", cfg.apiVersion)
|
||||
], true)))
|
||||
|
||||
result.config = result.http.loadNotionConfig(cfg)
|
||||
|
||||
proc `%`*(c: NotionClientConfig): JsonNode =
|
||||
%*{
|
||||
"apiBaseUrl": c.apiBaseUrl,
|
||||
"apiVersion": c.apiVersion,
|
||||
"configDbId": c.configDbId,
|
||||
"integrationToken": c.integrationToken
|
||||
}
|
||||
|
||||
proc parseNotionClientConfig*(n: JsonNode): NotionClientConfig =
|
||||
NotionClientConfig(
|
||||
apiBaseUrl: n.getOrFail("apiBaseUrl").getStr,
|
||||
apiVersion: n.getOrFail("apiVersion").getStr,
|
||||
configDbId: n.getOrFail("configDbId").getStr,
|
||||
integrationToken: n.getOrFail("integrationToken").getStr)
|
||||
|
||||
proc fetchAllPages*(
|
||||
http: HttpClient,
|
||||
url: string,
|
||||
bodyTemplate = %*{ "page_size": NOTION_MAX_PAGE_SIZE}): seq[JsonNode] =
|
||||
|
||||
result = @[]
|
||||
var nextCursor: string = ""
|
||||
|
||||
while true:
|
||||
let body = parseJson($bodyTemplate)
|
||||
if not nextCursor.isEmptyOrWhitespace: body["start_cursor"] = %nextCursor
|
||||
|
||||
debug "Fetching pages from database:\n\tPOST " & url & "\n\t" & $body
|
||||
let jsonResp = parseJson(http.postContent(url, $body))
|
||||
|
||||
result = result & jsonResp["results"].getElems
|
||||
if jsonResp.hasKey("next_cursor") and jsonResp["next_cursor"].kind != JNull:
|
||||
nextCursor = jsonResp["next_cursor"].getStr
|
||||
|
||||
if nextCursor.isEmptyOrWhitespace: break
|
||||
|
||||
template fetchPage*(notion: NotionClient, pageId: string): untyped =
|
||||
let resp = notion.http.get(notion.apiBaseUrl & "/pages/" & pageId)
|
||||
|
||||
if not resp.status.startsWith("2"):
|
||||
debug resp.body
|
||||
raise newException(HttpRequestError, "API Request failed: " & resp.body)
|
||||
parseJson(resp.body)
|
||||
|
||||
template createDbPage*(notion: NotionClient, parentDbId: string, r: typed): untyped =
|
||||
let page = r.toPage
|
||||
page["parent"] = %*{ "database_id": parentDbId }
|
||||
|
||||
let resp = notion.http.post(notion.apiBaseUrl & "/pages", $page)
|
||||
|
||||
if not resp.status.startsWith("2"):
|
||||
debug resp.body
|
||||
raise newException(HttpRequestError, "API Request failed: " & resp.body)
|
||||
parseJson(resp.body)
|
||||
|
||||
template updatePage*(notion: NotionClient, r: typed): JsonNode =
|
||||
let page = r.toPage
|
||||
|
||||
let resp = notion.http.patch(notion.apiBaseUrl & "/pages/" & r.id, $page)
|
||||
|
||||
if not resp.status.startsWith("2"): debug resp.body
|
||||
parseJson(resp.body)
|
||||
|
||||
template delete*(notion: NotionClient, r: typed): string =
|
||||
let resp = notion.http.delete(notion.apiBaseUrl & "/blocks/" & r.id)
|
||||
|
||||
if not resp.status.startsWith("2"): debug resp.body
|
||||
parseJson(resp.body)["id"].getStr
|
||||
|
||||
proc fetchSyncRecords*(notion: NotionClient): seq[SyncRecord] =
|
||||
let respRecords = notion.http.fetchAllPages(
|
||||
notion.apiBaseUrl & "/databases/" & notion.config.syncRecordsDbId & "/query")
|
||||
|
||||
return respRecords.mapIt(syncRecordFromPage(it))
|
||||
|
||||
proc create*(notion: NotionClient, r: SyncRecord): SyncRecord =
|
||||
return syncRecordFromPage(createDbPage(notion, notion.config.syncRecordsDbId, r))
|
||||
|
||||
proc update*(notion: NotionClient, r: SyncRecord): SyncRecord =
|
||||
return syncRecordFromPage(updatePage(notion, r))
|
||||
|
||||
proc fetchAddress*(notion: NotionClient, addressId: string): Address =
|
||||
return addressFromPage(notion.fetchPage(addressId))
|
||||
|
||||
proc fetchAddresses*(notion: NotionClient): seq[Address] =
|
||||
let respRecords = notion.http.fetchAllPages(
|
||||
notion.apiBaseUrl & "/databases/" & notion.config.addressesDbId & "/query")
|
||||
|
||||
return resprecords.mapIt(addressFromPage(it))
|
||||
|
||||
proc create*(notion: NotionClient, a: Address): Address =
|
||||
return addressFromPage(notion.createDbPage(notion.config.addressesDbId , a))
|
||||
|
||||
proc update*(notion: NotionClient, r: Address): Address =
|
||||
return addressFromPage(updatePage(notion, r))
|
||||
|
||||
proc fetchFamily*(notion: NotionClient, familyId: string): Family =
|
||||
return familyFromPage(notion.fetchPage(familyId))
|
||||
|
||||
proc fetchFamilies*(notion: NotionClient): seq[Family] =
|
||||
let respRecords = notion.http.fetchAllPages(
|
||||
notion.apiBaseUrl & "/databases/" & notion.config.familiesDbId & "/query")
|
||||
|
||||
return respRecords.mapIt(familyFromPage(it))
|
||||
|
||||
proc create*(notion: NotionClient, f: Family): Family =
|
||||
return familyFromPage(notion.createDbPage(notion.config.familiesDbId , f))
|
||||
|
||||
proc update*(notion: NotionClient, r: Family): Family =
|
||||
return familyFromPage(updatePage(notion, r))
|
||||
|
||||
proc fetchPerson*(notion: NotionClient, personId: string): Person =
|
||||
return personFromPage(notion.fetchPage(personId))
|
||||
|
||||
proc fetchPeople*(notion: NotionClient): seq[Person] =
|
||||
let respRecords = notion.http.fetchAllPages(
|
||||
notion.apiBaseUrl & "/databases/" & notion.config.peopleDbId & "/query")
|
||||
|
||||
return respRecords.mapIt(personFromPage(it))
|
||||
|
||||
proc create*(notion: NotionClient, p: Person): Person =
|
||||
return personFromPage(notion.createDbPage(notion.config.peopleDbId, p))
|
||||
|
||||
proc update*(notion: NotionClient, r: Person): Person =
|
||||
return personFromPage(updatePage(notion, r))
|
||||
|
||||
proc persist*[T](notion: NotionClient, r: T): T =
|
||||
if r.id.isEmptyOrWhitespace: notion.create(r)
|
||||
else: notion.update(r)
|
||||
|
||||
proc fetchMembershipDataSet*(notion: NotionClient): MembershipDataSet =
|
||||
result = MembershipDataSet(
|
||||
addresses: mapById(notion.fetchAddresses()),
|
||||
families: mapById(notion.fetchFamilies()),
|
||||
people: mapById(notion.fetchPeople()))
|
||||
Reference in New Issue
Block a user