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 fetchDatabaseObject*(notion: NotionClient, dbId: string): untyped = let resp = notion.http.get(notion.apiBaseUrl & "/databases/" & dbId) if not resp.status.startsWith("2"): debug resp.body raise newException(HttpRequestError, "API Request failed: " & resp.body) parseJson(resp.body) 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()))