diff --git a/api/src/main/nim/personal_measure_apipkg/db.nim b/api/src/main/nim/personal_measure_apipkg/db.nim index cd90d9e..afe0324 100644 --- a/api/src/main/nim/personal_measure_apipkg/db.nim +++ b/api/src/main/nim/personal_measure_apipkg/db.nim @@ -28,20 +28,34 @@ proc dbFormat[T](list: seq[T]): string = proc dbFormat[T](item: T): string = return $item -proc createParseStmts(t: NimNode, value: string): NimNode = +proc createParseStmt(t, value: NimNode): NimNode = result = newStmtList() if t.typeKind == ntyObject: if t.getType == UUID.getType: result.add quote do: - discard parseUUID(`value`) + parseUUID(`value`) elif t.getType == DateTime.getType: result.add quote do: - discard `value`.parseIso8601 - + `value`.parseIso8601 + elif t.getTypeInst == Option.getType: + let innerType = t.getTypeImpl[2][0][0][1] + let parseStmt = createParseStmt(innerType, value) + result.add quote do: + if `value`.len == 0: + none[`innerType`]() + else: + some(`parseStmt`) + else: + error "Unknown value object type: " & $t.getTypeInst elif t.typeKind == ntyString: result.add quote do: - discard `value` + `value` + elif t.typeKind == ntyInt: + result.add quote do: + parseInt(`value`) + else: + error "Unknown value type: " & $t.getTypeInst template walkFieldDefs(t: NimNode, body: untyped) = let tTypeImpl = t.getTypeImpl @@ -104,23 +118,28 @@ macro populateMutateClauses(t: typed, newRecord: bool, mc: var MutateClauses): u #echo result.repr -# TODO -macro rowToModel(modelType: typed): untyped = - result = newStmtList() +macro rowToModel(modelType: typed, row: seq[string]): untyped = - #echo modelType.getType[1].getType.treeRepr + # Create the object constructor AST node + result = newNimNode(nnkObjConstr).add(modelType) + + # Create new colon expressions for each of the property initializations + var idx = 0 + echo modelType.getTypeImpl.treeRepr + # TODO: how to guarantee order? modelType.walkFieldDefs: - result.add createParseStmts(fieldType, "") - #[ - result.add quote do: - User( - id: genUUID - #modelType.walkFieldDefs: - ]# + let itemLookup = quote do: `row`[`idx`] + result.add(newColonExpr( + fieldIdent, + createParseStmt(fieldType, itemLookup))) + idx += 1 -macro modelName(model: typed): string = +macro modelName(model: object): string = return $model.getTypeInst +macro modelName(modelType: type): string = + return $modelType.getType[1] + proc identNameToDb(name: string): string = return name.replace(UPPERCASE_PATTERN, "$1_$2").toLower() @@ -128,6 +147,9 @@ proc dbNameToIdent(name: string): string = let parts = name.split("_") return @[parts[0]].concat(parts[1..^1].mapIt(capitalize(it))).join("") +proc tableName(modelType: type): string = + return modelName(modelType).identNameToDb & "s" + proc tableName[T](rec: T): string = return modelName(rec).identNameToDb & "s" @@ -158,8 +180,9 @@ proc updateRecord[T](db: DbConn, rec: T): bool = return numRowsUpdated > 0; -# TODO -#proc getRecord[T](db: DbConn, UUID id): T = +template getRecord(db: DbConn, modelType: type, id: UUID): untyped = + let row = db.getRow(sql("SELECT * FROM " & tableName(modelType) & " WHERE id = ?"), @[$id]) + rowToModel(modelType, row) macro listFields(t: typed): untyped = var fields: seq[tuple[n: string, t: string]] = @[] @@ -169,30 +192,34 @@ macro listFields(t: typed): untyped = result = newLit(fields) - # proc create: Typed create methods for specific records -proc createUser*(db: DbConn, user: User): User = db.createRecord(user) -proc createApiToken*(db: DbConn, token: ApiToken): ApiToken = db.createRecord(token) -proc createMeasure*(db: DbConn, measure: Measure): Measure = db.createRecord(measure) -proc createValue*(db: DbConn, value: Value): Value = db.createRecord(value) +proc createUser*(db: DbConn, user: User): User = return db.createRecord(user) +proc createApiToken*(db: DbConn, token: ApiToken): ApiToken = return db.createRecord(token) +proc createMeasure*(db: DbConn, measure: Measure): Measure = return db.createRecord(measure) +proc createValue*(db: DbConn, value: Value): Value = return db.createRecord(value) + +proc getUser*(db: DbConn, id: UUID): User = return db.getRecord(User, id) +proc getApiToken*(db: DbConn, id: UUID): ApiToken = return db.getRecord(ApiToken, id) when isMainModule: - rowToModel(User) + let db = open("", "", "", "host=192.168.99.100 port=5500 dbname=personal_measure user=postgres password=password") + + for row in db.fastRows(sql"SELECT id FROM users"): + echo $db.getUser(parseUUID(row[0])) + + for row in db.fastRows(sql"SELECT id FROM api_tokens"): + echo $db.getApiToken(parseUUID(row[0])) + + #echo tableName(ApiToken) + echo $rowToModel(ApiToken, @["47400441-5c3a-4119-8acf-f616ae25c16c", "9e5460dd-b580-4071-af97-c1cbdedaae12", "Test Token", "5678", ""]) +#[ + for row in db.fastRows(sql"SELECT * FROM api_tokens"): + echo $rowToModel(ApiToken, row); let u = User( displayName: "Bob", email: "bob@bobsco.com", hashedPwd: "test") -#[ - let db = open("", "", "", "host=localhost port=5500 dbname=personal_measure user=postgres password=password") - for row in db.fastRows(sql"SELECT * FROM users"): - echo $row - echo "----" - rowToModel(User) - - echo "New user:\n\t" & $db.createUser(u) - for row in db.fastRows(sql"SELECT * FROM users"): - echo $row ]# diff --git a/api/src/main/nim/personal_measure_apipkg/models.nim b/api/src/main/nim/personal_measure_apipkg/models.nim index 452aa95..c57877f 100644 --- a/api/src/main/nim/personal_measure_apipkg/models.nim +++ b/api/src/main/nim/personal_measure_apipkg/models.nim @@ -1,23 +1,34 @@ -import options, times, uuids +import options, times, timeutils, uuids type User* = object id*: UUID - displayName*, email*, hashedPwd*: string + displayName*: string + email*: string + hashedPwd*: string ApiToken* = object - id*, userId*: UUID - name*, hashedToken*: string + id*: UUID + userId*: UUID + name*:string expires*: Option[DateTime] + hashedToken*: string Measure* = object - id*, userId*: UUID - slug*, name*, description*, domainUnits*, rangeUnits*: string - domainSource*, rangeSource*: Option[string] + id*: UUID + userId*: UUID + slug*: string + name*: string + description*: string + domainSource*: Option[string] + domainUnits*: string + rangeSource*: Option[string] + rangeUnits*: string analysis*: seq[string] Value* = object - id*, measureId*: UUID + id*: UUID + measureId*: UUID value*: int timestamp*: DateTime extData*: string @@ -26,7 +37,9 @@ proc `$`*(u: User): string = return "User " & ($u.id)[0..6] & " - " & u.displayName & " <" & u.email & ">" proc `$`*(tok: ApiToken): string = - return "ApiToken " & ($tok.id)[0..6] & " - " & tok.name + result = "ApiToken " & ($tok.id)[0..6] & " - " & tok.name + if tok.expires.isSome: result &= " (expires " & tok.expires.get.formatIso8601 & ")" + else: result &= " (no expiry)" proc `$`*(m: Measure): string = return "Measure " & ($m.id)[0..6] & " - " & m.slug diff --git a/api/src/main/sql/migrations/20190214122514-initial-schema-up.sql b/api/src/main/sql/migrations/20190214122514-initial-schema-up.sql index cfb51ce..0208ea8 100644 --- a/api/src/main/sql/migrations/20190214122514-initial-schema-up.sql +++ b/api/src/main/sql/migrations/20190214122514-initial-schema-up.sql @@ -13,7 +13,7 @@ create table "api_tokens" ( user_id uuid not null references users (id) on delete cascade on update cascade, name varchar not null, expires timestamp with time zone default null, - hashedToken varchar not null + hashed_token varchar not null ); create table "measures" (