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 = initLoggingNamespace("hff_notion_api_client/utils", lvlInfo) logNs proc parseDate(str: string): DateTime = try: result = parseIso8601(str) except: result = times.parse(str, NOTION_DATE_FORMAT) ## Utility functions for creating Page property values ## --------------------------------------------------- proc makeDateProp*(d: Option[DateTime]): JsonNode = if d.isSome: return %*{ "date": { "start": format(d.get, NOTION_DATE_FORMAT) } } else: return %*{ "date": nil } proc makeDateTimeProp*(d: Option[DateTime]): JsonNode = if d.isSome: return %*{ "date": { "start": formatIso8601(d.get) } } else: return %*{ "date": nil } proc makeIntervalProp*(s: Option[DateTime], e: Option[DateTime]): JsonNode = if not s.isSome: result = %*{ "date": nil } result = %*{ "date": { "start": formatIso8601(s.get) } } if e.isSome: result["date"]["end"] = %formatIso8601(e.get) proc makeMultiSelectProp*(values: seq[string]): JsonNode = if values.len == 0: return %*{ "multi_select": [] } return %*{ "multi_select": values.mapIt(%*{ "name": it }) } proc makeRelationProp*(ids: seq[string]): JsonNode = if ids.len == 0: return %*{ "relation": [] } return %*{ "relation": ids.mapIt(%*{ "id": it }) } proc makeSelectProp*(value: string): JsonNode = return %*{ "select": { "name": value } } proc makeTextProp*(propType: string, value: string): JsonNode = return %*{ propType: [ { "type": "text", "text": { "content": value } } ] } ## Utility functions for reading Page property values ## -------------------------------------------------- proc getPropNode*(page: JsonNode, propType, propName: string): JsonNode = result = page{"properties", propName, propType} if isNil(result): raise newException(ValueError, "could not find a " & propType & " property named '" & propName & "' in the Notion page (id: " & page["id"].getStr & ")") proc getEmail*(page: JsonNode, propName: string): string = let propNode = page.getPropNode("email", propName) return propNode.getStr proc getMultiSelect*(page: JsonNode, propName: string): seq[string] = let propNode = page.getPropNode("multi_select", propName) return propNode.getElems.mapIt(it["name"].getStr) proc getPhone*(page: JsonNode, propName: string): string = let propNode = page.getPropNode("phone_number", propName) return propNode.getStr proc getRelationIds*(page: JsonNode, propName: string): seq[string] = let propNode = page.getPropNode("relation", propName) return propNode.getElems.mapIt(it["id"].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 @[] elif rollups.getElems.len != 1 or not rollups.getElems[0].hasKey("title"): log().debug "Expected exactly one item of type 'title'. Received: \n" & rollups.pretty raise newException( ValueError, "unexpected format of rollup value for '" & propName & "' property") let titleElems = rollups.getElems[0]["title"] return titleElems.getElems.mapIt(it["plain_text"].getStr) proc getText*(page: JsonNode, propName: string): string = let propNode = page.getPropNode("rich_text", propName) if propNode.len == 0: return "" return propNode[0]["plain_text"].getStr proc getTitle*(page: JsonNode, propName: string): string = let propNode = page.getPropNode("title", propName) if propNode.len == 0: return "" return propNode[0]["plain_text"].getStr proc getSelect*(page: JsonNode, propName: string): string = let propNode = page.getPropNode("select", propName) if propNode.kind == JNull: return "" return propNode["name"].getStr proc getDateTime*(page: JsonNode, propName: string): Option[DateTime] = let propNode = page.getPropNode("date", propName) if propNode.kind == JNull: result = none[DateTime]() else: result = some(parseDate(propNode["start"].getStr))