Implemented GET on /projects/<proj-id> and started unit tests.
This commit is contained in:
parent
e547ecd607
commit
1e2af48892
13
api.rst
13
api.rst
@ -1,9 +1,16 @@
|
||||
✓ GET /api/ping
|
||||
- GET /api/auth-token
|
||||
✓ GET /api/auth-token
|
||||
✓ GET /api/verify-auth -- returns 200 or 401 depend on validity of the provided auth
|
||||
✓ GET /api/projects -- return project summaries
|
||||
- POST /api/projects -- create a new project
|
||||
- GET /api/project/<proj-id> -- return detailed project record (include steps)
|
||||
* GET /api/project/<proj-id> -- return detailed project record (include steps)
|
||||
- GET /api/project/<proj-id>/active -- return detailed information about all currently running runs
|
||||
- GET /api/project/<proj-id>/<step-id> -- return detailed step information (include runs)
|
||||
- POST /api/project/<proj-id>/<step-id>/run/<ref> -- kick off a run
|
||||
* POST /api/project/<proj-id>/<step-id>/run/<ref> -- kick off a run
|
||||
- GET /api/project/<proj-id>/<step-id>/run/<ref> -- return detailed run information
|
||||
|
||||
|
||||
Legend:
|
||||
✓ implemented with passing tests
|
||||
* implemented, needs testing
|
||||
- not implemented
|
||||
|
@ -79,7 +79,7 @@ proc getOrFail(n: JsonNode, key: string, objName: string = ""): JsonNode =
|
||||
return n[key]
|
||||
|
||||
# Configuration parsing code
|
||||
|
||||
|
||||
proc parseProjectDef*(pJson: JsonNode): ProjectDef =
|
||||
var envVars = newStringTable(modeCaseSensitive)
|
||||
for k, v in pJson.getIfExists("envVars").getFields: envVars[k] = v.getStr("")
|
||||
@ -186,13 +186,28 @@ proc `%`*(req: RunRequest): JsonNode =
|
||||
"workspaceDir": req.workspaceDir,
|
||||
"forceRebuild": req.forceRebuild }
|
||||
|
||||
proc `%`*(user: User): JsonNode =
|
||||
result = %* {
|
||||
"name": user.name,
|
||||
"hashedPwd": user.hashedPwd }
|
||||
|
||||
proc `%`*(cfg: StrawBossConfig): JsonNode =
|
||||
result = %* {
|
||||
"artifactsRepo": cfg.artifactsRepo,
|
||||
"authSecret": cfg.authSecret,
|
||||
"debug": cfg.debug,
|
||||
"projects": %cfg.projects,
|
||||
"pwdCost": cfg.pwdCost,
|
||||
"users": %cfg.users }
|
||||
|
||||
proc `$`*(s: BuildStatus): string = result = pretty(%s)
|
||||
proc `$`*(req: RunRequest): string = result = pretty(%req)
|
||||
proc `$`*(pd: ProjectDef): string = result = pretty(%pd)
|
||||
proc `$`*(cfg: StrawBossConfig): string = result = pretty(%cfg)
|
||||
|
||||
# TODO: maybe a macro for more general-purpose, shallow object comparison?
|
||||
#proc `==`*(a, b: ProjectDef): bool =
|
||||
|
||||
|
||||
template shallowEquals(a, b: RootObj): bool =
|
||||
if type(a) != type(b): return false
|
||||
var anyB = toAny(b)
|
||||
|
@ -140,7 +140,10 @@ proc runStep*(wksp: Workspace, step: Step) =
|
||||
let artifactPath = a.resolveEnvVars(wksp.env)
|
||||
let artifactName = artifactPath[(artifactPath.rfind("/")+1)..^1]
|
||||
try:
|
||||
wksp.outputHandler.sendMsg "copy " & wksp.dir & "/repo/" & step.workingDir & "/" & artifactPath & " -> " & wksp.artifactsDir & "/" & artifactName
|
||||
wksp.outputHandler.sendMsg "copy " & wksp.dir & "/repo/" &
|
||||
step.workingDir & "/" & artifactPath & " -> " &
|
||||
wksp.artifactsDir & "/" & artifactName
|
||||
|
||||
copyFile(wksp.dir & "/repo/" & step.workingDir & "/" & artifactPath,
|
||||
wksp.artifactsDir & "/" & artifactName)
|
||||
except:
|
||||
@ -205,6 +208,13 @@ proc runStep*(cfg: StrawBossConfig, req: RunRequest,
|
||||
"cloning project repo and preparing to run '" & req.stepName & "'")
|
||||
wksp.setupProject()
|
||||
|
||||
# Update our cache of project configurations by copying the configuration
|
||||
# file to our artifacts directory.
|
||||
copyFile(
|
||||
wksp.dir & "/repo/" & wksp.projectDef.cfgFilePath,
|
||||
cfg.artifactsRepo & "/" & wksp.project.name & "/configuration." &
|
||||
wksp.version & ".json")
|
||||
|
||||
# Find the requested step
|
||||
if not wksp.project.steps.hasKey(req.stepName):
|
||||
raiseEx "no step name '" & req.stepName & "' for " & req.projectName
|
||||
|
@ -1,5 +1,5 @@
|
||||
import asyncdispatch, bcrypt, jester, json, jwt, os, osproc, sequtils,
|
||||
strutils, tempfile, times, unittest
|
||||
import algorithm, asyncdispatch, bcrypt, jester, json, jwt, os, osproc,
|
||||
sequtils, strutils, tempfile, times, unittest
|
||||
|
||||
import logging
|
||||
import ./configuration, ./core, private/util
|
||||
@ -143,7 +143,60 @@ proc start*(cfg: StrawBossConfig): void =
|
||||
resp(Http200, $(%*{ "username": session.user.name }), JSON)
|
||||
|
||||
get "/projects": withSession:
|
||||
resp($(%(givenCfg.projects)), "application/json")
|
||||
# List project summaries (ProjectDefs only)
|
||||
resp($(%(cfg.projects)), JSON)
|
||||
|
||||
post "/projects": withSession:
|
||||
# Create a new project definition
|
||||
resp(Http501, makeJsonResp(Http501), JSON)
|
||||
|
||||
get "/project/@projectName/@version?": withSession:
|
||||
## Get a detailed project record including step definitions (ProjectConfig).
|
||||
|
||||
# Make sure we know about that project
|
||||
var project: ProjectDef
|
||||
try: project = cfg.findProject(@"projectName")
|
||||
except: resp(Http404, makeJsonResp(Http404, getCurrentExceptionMsg()), JSON)
|
||||
|
||||
# Given version
|
||||
|
||||
var cachedFilePath: string
|
||||
if @"version" != "":
|
||||
cachedFilePath = cfg.artifactsRepo & "/" & project.name &
|
||||
"/configuration." & @"version" & ".json"
|
||||
|
||||
if not existsFile(cachedFilePath):
|
||||
resp(Http404,
|
||||
makeJsonResp(Http404, "I have never built version " & @"version"),
|
||||
JSON)
|
||||
|
||||
# No version requested, use "latest"
|
||||
else:
|
||||
let confFilePaths = toSeq(walkFiles("configuration.*.json"))
|
||||
if confFilePaths.len == 0:
|
||||
resp(Http404, makeJsonResp(Http404, "I have not built any versions of " & project.name), JSON)
|
||||
let modTimes = confFilePaths.mapIt(it.getLastModificationTime)
|
||||
cachedFilePath = sorted(zip(confFilePaths, modTimes),
|
||||
proc (a, b: tuple): int = cmp(a.b, b.b))[0].a
|
||||
|
||||
try: resp(readFile(cachedFilePath), JSON)
|
||||
except:
|
||||
debug "Could not serve cached project configuration at: " &
|
||||
cachedFilePath & "\n\t Reason: " & getCurrentExceptionMsg()
|
||||
resp(Http500, makeJsonResp(Http500, "could not read cached project configuration"), JSON)
|
||||
|
||||
get "/api/project/@projectName/active": withSession:
|
||||
# List all currently active runs
|
||||
resp(Http501, makeJsonResp(Http501), JSON)
|
||||
|
||||
get "/api/project/@projectName/@stepName": withSession:
|
||||
|
||||
# Get step details including runs.
|
||||
resp(Http501, makeJsonResp(Http501), JSON)
|
||||
|
||||
get "/api/project/@projectName/@stepName/run/@buildRef": withSession:
|
||||
# Get detailed information about a run
|
||||
resp(Http501, makeJsonResp(Http501), JSON)
|
||||
|
||||
post "/project/@projectName/@stepName/run/@buildRef?":
|
||||
# Kick off a run
|
||||
@ -159,4 +212,12 @@ proc start*(cfg: StrawBossConfig): void =
|
||||
callSoon(proc(): void = complete(stopFuture))
|
||||
resp($(%*"shutting down"), JSON)
|
||||
|
||||
#[
|
||||
get re".*":
|
||||
resp(Http404, makeJsonResp(Http404), JSON)
|
||||
|
||||
post re".*":
|
||||
resp(Http404, makeJsonResp(Http404), JSON)
|
||||
]#
|
||||
|
||||
waitFor(stopFuture)
|
||||
|
@ -107,6 +107,10 @@ suite "load and save configuration objects":
|
||||
sameContents(pc.steps["test"].expectedEnv, @[])
|
||||
sameContents(pc.steps["test"].cmdInput, @[])
|
||||
|
||||
test "StrawBossConfig to string":
|
||||
# TODO
|
||||
check false
|
||||
|
||||
test "loadBuildStatus":
|
||||
let st = loadBuildStatus("src/test/json/test-status.json")
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
import asyncdispatch, httpclient, json, os, osproc, sequtils, strutils, times, unittest
|
||||
import asyncdispatch, httpclient, json, os, osproc, sequtils, strutils,
|
||||
tempfile, times, unittest
|
||||
|
||||
import logging
|
||||
import ./testutil
|
||||
import ../../main/nim/strawbosspkg/configuration
|
||||
import ../../main/nim/strawbosspkg/server
|
||||
import ../../main/nim/strawbosspkg/private/util
|
||||
|
||||
import strtabs
|
||||
|
||||
# test helpers
|
||||
proc newAuthenticatedHttpClient(apiBase, uname, pwd: string): HttpClient =
|
||||
result = newHttpClient()
|
||||
@ -13,20 +14,20 @@ proc newAuthenticatedHttpClient(apiBase, uname, pwd: string): HttpClient =
|
||||
assert authResp.status.startsWith("200")
|
||||
result.headers = newHttpHeaders({"Authorization": "Bearer " & parseJson(authResp.body).getStr})
|
||||
|
||||
suite "strawboss server can...":
|
||||
let apiBase = "http://localhost:8180/api"
|
||||
let cfgFilePath = "src/test/json/strawboss.config.json"
|
||||
let cfg = loadStrawBossConfig(cfgFilePath)
|
||||
|
||||
let testuser = UserRef( # note: needs to correspond to an actual user
|
||||
name: "bob@builder.com",
|
||||
hashedPwd: "$2a$11$lVZ9U4optQMhzPh0E9A7Yu6XndXblUF3gCa.zmEvJy4F.4C4718b.")
|
||||
|
||||
suite "strawboss server":
|
||||
|
||||
# suite setup code
|
||||
let cfgFilePath = "src/test/json/strawboss.config.json"
|
||||
let cfg = loadStrawBossConfig(cfgFilePath)
|
||||
|
||||
discard startProcess("./strawboss", ".", @["serve", "-c", cfgFilePath], loadEnv(), {poUsePath})
|
||||
|
||||
let http = newHttpClient()
|
||||
let apiBase = "http://localhost:8180/api"
|
||||
|
||||
let testuser = UserRef( # note: needs to correspond to an actual user
|
||||
name: "bob@builder.com",
|
||||
hashedPwd: "$2a$11$lVZ9U4optQMhzPh0E9A7Yu6XndXblUF3gCa.zmEvJy4F.4C4718b.")
|
||||
|
||||
# give the server time to spin up
|
||||
sleep(100)
|
||||
@ -88,5 +89,25 @@ suite "strawboss server can...":
|
||||
check sameContents(projects, cfg.projects)
|
||||
|
||||
# suite tear-down
|
||||
try: discard http.post(apiBase & "/service/debug/stop")
|
||||
except: discard ""
|
||||
discard newAsyncHttpClient().post(apiBase & "/service/debug/stop")
|
||||
|
||||
suite "strawboss server continued":
|
||||
|
||||
setup:
|
||||
let tmpArtifactsDir = mkdtemp()
|
||||
let (_, tmpCfgPath) = mkstemp()
|
||||
var newCfg = cfg
|
||||
newCfg.artifactsRepo = tmpArtifactsDir
|
||||
writeFile(tmpCfgPath, $newCfg)
|
||||
discard startProcess("./strawboss", ".", @["serve", "-c", tmpCfgPath], loadEnv(), {poUsePath})
|
||||
|
||||
# give the server time to spin up
|
||||
sleep(100)
|
||||
|
||||
teardown:
|
||||
discard newAsyncHttpClient().post(apiBase & "/service/debug/stop")
|
||||
|
||||
test "handle missing project configuration":
|
||||
let http = newAuthenticatedHttpClient(apibase, "bob@builder.com", "password")
|
||||
let resp = http.get(apiBase & "/projects/test-project-1")
|
||||
check resp.status.startsWith("404")
|
||||
|
Loading…
x
Reference in New Issue
Block a user