Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
03e6c0bf5a | |||
ad8f36dc48 | |||
3f9eb80713 | |||
8f0ce0900e | |||
d9cddc4371 | |||
7c879a0fca |
14
README.md
Normal file
14
README.md
Normal file
@ -0,0 +1,14 @@
|
||||
# Hope Family Fellowship Notion API Client
|
||||
|
||||
## Building
|
||||
|
||||
### Software Packages
|
||||
|
||||
`hff_notion_api_client` depends on a number of packages which are not yet
|
||||
available in he official Nim repository. The easiest way to get access to these
|
||||
packages is to add a new `PackageList` to your [nimble configuration] for the
|
||||
[JDB Software Nim packages repository]. The url is
|
||||
`https://git.jdb-software.com/jdb/nim-packages/raw/main/packages.json`
|
||||
|
||||
[nimble configuration]: https://github.com/nim-lang/nimble#configuration
|
||||
[JDB Software Nim packages]: https://git.jdb-software.com/jdb/nim-packages
|
@ -1,14 +1,14 @@
|
||||
# Package
|
||||
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
author = "Jonathan Bernard"
|
||||
description = "Utilities and bindings for HFF's Notion API."
|
||||
license = "GPL-3.0-or-later"
|
||||
srcDir = "src"
|
||||
|
||||
|
||||
# Dependencies
|
||||
|
||||
requires "nim >= 1.4.8"
|
||||
requires "https://git.jdb-software.com/jdb/buffoonery"
|
||||
requires "https://git.jdb-software.com/jdb/nim-time-utils.git >= 0.5.0"
|
||||
|
||||
# packages from git.jdb-software.com/jdb/nim-packages
|
||||
requires @["buffoonery", "timeutils >= 0.5.0", "namespaced_logging >= 0.3.1"]
|
||||
|
@ -1,11 +1,8 @@
|
||||
import std/httpclient, std/json, std/logging, std/sequtils, std/tables, std/strutils
|
||||
import std/[httpclient, json, logging, options, sequtils, tables, 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
|
||||
import hff_notion_api_client/[config, models, sequtils_ext, utils]
|
||||
|
||||
type
|
||||
NotionClientConfig* = object
|
||||
@ -61,20 +58,19 @@ proc fetchAllPages*(
|
||||
bodyTemplate = %*{ "page_size": NOTION_MAX_PAGE_SIZE}): seq[JsonNode] =
|
||||
|
||||
result = @[]
|
||||
var nextCursor: string = ""
|
||||
var nextCursor: Option[string] = none[string]()
|
||||
|
||||
while true:
|
||||
let body = parseJson($bodyTemplate)
|
||||
if not nextCursor.isEmptyOrWhitespace: body["start_cursor"] = %nextCursor
|
||||
if nextCursor.isSome: body["start_cursor"] = %nextCursor.get
|
||||
|
||||
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
|
||||
nextCursor = some(jsonResp["next_cursor"].getStr)
|
||||
else: break
|
||||
|
||||
template fetchDatabaseObject*(notion: NotionClient, dbId: string): untyped =
|
||||
let resp = notion.http.get(notion.apiBaseUrl & "/databases/" & dbId)
|
||||
|
@ -11,6 +11,7 @@ type
|
||||
city*: string
|
||||
state*: string
|
||||
street*: string
|
||||
suiteOrBuilding*: string
|
||||
zipCode*: string
|
||||
createdAt*: Option[DateTime]
|
||||
lastUpdatedAt*: Option[DateTime]
|
||||
@ -21,7 +22,8 @@ type
|
||||
name*: string
|
||||
headsOfHousehold*: seq[string]
|
||||
headOfHouseholdIds*: seq[string]
|
||||
primaryAddressId*: seq[string]
|
||||
primaryAddress*: string
|
||||
primaryAddressId*: string
|
||||
members*: seq[string]
|
||||
memberIds*: seq[string]
|
||||
createdAt*: Option[DateTime]
|
||||
@ -84,8 +86,8 @@ func sameContents[T](a: seq[T], b: seq[T]): bool =
|
||||
func `==`*(a, b: Family): bool =
|
||||
return a.name == b.name and
|
||||
sameContents(a.headOfHouseholdIds, b.headOfHouseholdIds) and
|
||||
sameContents(a.primaryAddressId, b.primaryAddressId) and
|
||||
sameContents(a.memberIds, b.memberIds)
|
||||
sameContents(a.memberIds, b.memberIds) and
|
||||
a.primaryAddressId == b.primaryAddressId
|
||||
|
||||
func `==`*(a, b: Person): bool =
|
||||
return a.preferredName == b.preferredName and
|
||||
@ -126,6 +128,7 @@ func toPage*(a: Address): JsonNode =
|
||||
"City": makeTextProp("rich_text", a.city),
|
||||
"State": makeSelectProp(a.state),
|
||||
"Street Address": makeTextProp("title", a.street),
|
||||
"Suite or Building": makeTextProp("rich_text", a.suiteOrBuilding),
|
||||
"Zip Code": makeTextProp("rich_text", a.zipCode)
|
||||
}
|
||||
}
|
||||
@ -135,7 +138,7 @@ func toPage*(f: Family): JsonNode =
|
||||
"properties": {
|
||||
"Name": makeTextProp("title", f.name),
|
||||
"Head(s) of Household": makeRelationProp(f.headOfHouseholdIds),
|
||||
"Primary Address": makeRelationProp(f.primaryAddressId),
|
||||
"Primary Address": makeRelationProp(@[f.primaryAddressId]),
|
||||
"Members": makeRelationProp(f.memberIds)
|
||||
}
|
||||
}
|
||||
@ -181,23 +184,30 @@ proc addressFromPage*(page: JsonNode): Address =
|
||||
city: page.getText("City"),
|
||||
state: page.getSelect("State"),
|
||||
street: page.getTitle("Street Address"),
|
||||
suiteOrBuilding: page.getText("Suite or Building"),
|
||||
zipCode: page.getText("Zip Code"),
|
||||
createdAt: some(parseIso8601(page["created_time"].getStr)),
|
||||
lastUpdatedAt: some(parseIso8601(page["last_edited_time"].getStr)))
|
||||
|
||||
proc familyFromPage*(page: JsonNode): Family =
|
||||
let primaryAddressIds = page.getRelationIds("Primary Address")
|
||||
result = Family(
|
||||
id: page["id"].getStr,
|
||||
name: page.getTitle("Name"),
|
||||
headsOfHousehold: page.getRolledupDisplayValues("Head(s) of Household (display)"),
|
||||
headsOfHousehold: page.getRolledupRecordTitles("Head(s) of Household (display)"),
|
||||
headOfHouseholdIds: page.getRelationIds("Head(s) of Household"),
|
||||
primaryAddressId: page.getRelationIds("Primary Address"),
|
||||
members: page.getRolledupDisplayValues("Members (display)"),
|
||||
primaryAddress: if primaryAddressIds.len == 0: ""
|
||||
else: page.getRolledupRecordTitles("Primary Address (display)")[0],
|
||||
primaryAddressId: if primaryAddressIds.len == 0: ""
|
||||
else: primaryAddressIds[0],
|
||||
members: page.getRolledupRecordTitles("Members (display)"),
|
||||
memberIds: page.getRelationIds("Members"),
|
||||
createdAt: some(parseIso8601(page["created_time"].getStr)),
|
||||
lastUpdatedAt: some(parseIso8601(page["last_edited_time"].getStr)))
|
||||
|
||||
proc personFromPage*(page: JsonNode): Person =
|
||||
let marriedToIds = page.getRelationIds("Married To")
|
||||
|
||||
result = Person(
|
||||
id: page["id"].getStr,
|
||||
preferredName: page.getTitle("Preferred Name"),
|
||||
@ -209,14 +219,16 @@ proc personFromPage*(page: JsonNode): Person =
|
||||
primaryPhoneNumber: page.getPhone("Primary Phone Number"),
|
||||
primaryEmailAddress: page.getEmail("Email Address"),
|
||||
relationshipToHff: page.getMultiSelect("Relationship to HFF"),
|
||||
addresses: page.getRolledupDisplayValues("Full Address"),
|
||||
addresses: page.getRollupArrayValues("Full Address").mapIt(it{"formula", "string"}.getStr),
|
||||
addressIds: page.getRelationIds("Address"),
|
||||
marriedTo: page.getRolledupDisplayValues("Married To (display)")[0],
|
||||
marriedToId: page.getRelationIds("Married To")[0],
|
||||
marriedTo: if marriedToIds.len == 0: ""
|
||||
else: page.getRolledupRecordTitles("Married To (display)")[0],
|
||||
marriedToId: if marriedToIds.len == 0: ""
|
||||
else: marriedToIds[0],
|
||||
anniversary: page.getDateTime("Anniversary"),
|
||||
parents: page.getRolledupDisplayValues("Parents (display)"),
|
||||
parents: page.getRolledupRecordTitles("Parents (display)"),
|
||||
parentIds: page.getRelationIds("Parents"),
|
||||
children: page.getRolledupDisplayValues("Children (display)"),
|
||||
children: page.getRolledupRecordTitles("Children (display)"),
|
||||
childIds: page.getRelationIds("Children"),
|
||||
createdAt: some(parseIso8601(page["created_time"].getStr)),
|
||||
lastUpdatedAt: some(parseIso8601(page["last_edited_time"].getStr)),
|
||||
|
@ -1,9 +1,16 @@
|
||||
import std/json, std/options, std/sequtils, std/times
|
||||
import std/json, std/logging, std/options, std/sequtils, std/times
|
||||
import namespaced_logging
|
||||
import timeutils
|
||||
|
||||
const NOTION_MAX_PAGE_SIZE* = 100
|
||||
const NOTION_DATE_FORMAT = "YYYY-MM-dd"
|
||||
|
||||
var logNs {.threadvar.}: LoggingNamespace
|
||||
|
||||
template log(): untyped =
|
||||
if logNs.isNil: logNs = getLoggerForNamespace("hff_notion_api_client/utils", lvlInfo)
|
||||
logNs
|
||||
|
||||
proc parseDate(str: string): DateTime =
|
||||
try: result = parseIso8601(str)
|
||||
except: result = times.parse(str, NOTION_DATE_FORMAT)
|
||||
@ -74,9 +81,24 @@ proc getRelationIds*(page: JsonNode, propName: string): seq[string] =
|
||||
let propNode = page.getPropNode("relation", propName)
|
||||
return propNode.getElems.mapIt(it["id"].getStr)
|
||||
|
||||
proc getRolledupDisplayValues*(page: JsonNode, propName: string): seq[string] =
|
||||
let propNode = page.getPropNode("rollup", propName)
|
||||
return propNode.getElems.mapIt(it{"formula", "string"}.getStr)
|
||||
proc getRollupArrayValues*(page: JsonNode, propName: string): seq[JsonNode] =
|
||||
return page.getPropNode("rollup", propName){"array"}.getElems(@[])
|
||||
|
||||
proc getRolledupRecordTitles*(page: JsonNode, propName: string): seq[string] =
|
||||
let rollups = page.getPropNode("rollup", propName)["array"]
|
||||
if rollups.getElems.len == 0: return @[]
|
||||
|
||||
result = @[]
|
||||
for rollupItem in rollups.getElems:
|
||||
if not rollupItem.hasKey("title") or rollupItem["title"].getElems.len != 1:
|
||||
log().debug "Expected exactly one item of type 'title'. Received: \n" &
|
||||
rollupItem.pretty
|
||||
|
||||
raise newException(
|
||||
ValueError,
|
||||
"unexpected format of rollup value for '" & propName & "' property")
|
||||
|
||||
result.add(rollupItem["title"].getElems[0]["plain_text"].getStr)
|
||||
|
||||
proc getText*(page: JsonNode, propName: string): string =
|
||||
let propNode = page.getPropNode("rich_text", propName)
|
||||
|
Loading…
x
Reference in New Issue
Block a user