import JSON
import URIs
import HTTP
const token_endpoint = "https://auth.brightspace.com/core/connect/token"
const auth_endpoint = "https://auth.brightspace.com/oauth2/auth"
function genparams(tokenpath, configpath)
tokens = JSON.parsefile(tokenpath)
conf = JSON.parsefile(configpath)
refreshparams = Dict([("scope" => j["scope"]),
("refresh_token" => j["refresh_token"]),
("grant_type" => "refresh_token"),
("client_id" => conf["client_id"]),
("client_secret" => conf["client_secret"])])
return (tokens=>tokens, conf=>conf, refreshparams=>refreshparams)
end
tokenpath
- path to json file in following format:
{"access_token":<access token>,
"token_type":"Bearer",
"scope":<scope>,
"expires_in":<expired in>,
"refresh_token":<refresh token>}
configpath
- path to json file in following format:
{"client_id": <client id>,
"client_secret": <client secret>,
"redirect_uri": <redirect uri>}
Tokenpath and configpath are parsed into a parameter dictionary ready for use in a refresh request, and returned as an element of a named tuple along with the tokens dictionary and configuration dictionary.
function genbod(params)
bod = string(URIs.URI(query = params))
return bod[2:length(bod)]
end
params
- the refreshparams dictionary/element returned from genparams, or a dictionary of form:
Dict([("scope" => <scope>),
("refresh_token" => <refresh token>),
("grant_type" => "refresh_token"),
("client_id" => <client id>),
("client_secret" => <client secret>)])
This function uses URIs to generate a query, then removes the leading '?' and returns a string. This is necessary because the brightspace API expects a string of this form in the body argument of HTTP.request.
function refresh(body, newtok)
req = HTTP.request("POST",token_endpoint, ["Content-Type"=>"application/x-www-form-urlencoded"], body)
tok = String(req.body)
open(newtok, "w") do f
write(f, tok)
end
end
body
- query string for submission in body parameter of HTTP.request.
newtok
- file name to save new access/refresh tokens, scope, expiration etc.
Submits a POST request to token endpoint, then writes new tokens to file.
staleparams = genparams("token.json", "config.json")
refresh(genbod(staleparams.refreshparams), "new_token.json")
We now have a live access token, we can read the new data by calling genparams again.
freshparams = genparams("new_token.json", "config.json")
To start, we need to get an access and refresh token. This is done by sending a GET request to the authentication endpoint.
tokpath = joinpath(path, "new_token.json")
confpath = joinpath(path,"d2lconfig.json")
staleparams = genparams(tokpath, confpath)
params = Dict([
("response_type"=>"code"),
("client_id"=>staleparams.conf["client_id"]),
("redirect_uri"=>staleparams.conf["redirect_uri"]),
("scope"=>staleparams.tokens["scope"]),
("state"=>"jfjkds31l")
])
authcoderesp = HTTP.request("GET", auth_endpoint, []; query=params)
authcodeurl = joinpath(authcoderesp.request.headers[end][2], authcoderesp.request.target[2:end])
authcodeurl
is a string of the form dev.brightspace.com/d2l/auth/api/token?<token>target=<target>
. This string needs to be copied into a browser URL bar and followed. It will redirect to the redirect_url and the URL w/i the URL bar will contain a parameter code=<code>
. This is the authorization code and should be
The next step is to request the tokens. We need a slightly different param dictionary than the one generated by genparams
, so we can manually create a params dictionary for genbod
.
params = Dict([
("grant_type"=>"authorization_code"),
("client_id"=>staleparams.conf["client_id"]),
("client_secret"=>staleparams.conf["client_secret"]),
("redirect_uri"=>staleparams.conf["redirect_uri"]),
("scope"=>staleparams.conf["scope"]),
("code"=><code>)
])
refresh(genbod(params), "new_token.json")
freshparams = genparams("new_token.json", "config.json")
We can now send an API request using the access token.
r = HTTP.request("GET", joinpath(urlbase, "d2l/api/lp/<vers>/dataExport/bds/list"), ["Authorization"=> "Bearer $(freshparams.tokens["access_token"])"],[] )
Where urlbase
is the address of your brightspace dev instance and <vers>
is your brightspace version. This request will return a list of data sets. Given a list of data sets datasetstoget
, we can process the response.
First, we parse the json response and push the data sets we are interested in to an array.
res = JSON.parse(r.body)
dictlist = []
for dct in res
if (dct["Name"] in datasetstoget)
push!(dictlist, dct)
end
end
Next, we loop over the array and download the datasets. Brighspace returns zip files, which can be written directly to disk.
for ds in dictlist
r = HTTP.request("GET", joinpath(urlbase, "d2l/api/lp/<vers>/dataExport/bds/download/$(ds["PluginId"])"), ["Authorization"=> "Bearer $(freshparams.tokens["access_token"])"],[])
open("$(replace(ds["Name"], " "=>"")).zip" ,"w") do f
write(f, r.body)
end
end
Finally, we can read the contents of the current directory and read the data into memory.
cdir = readdir()
ncdir = [x for x in cdir if x[length(x)-3:length(x)] == ".zip"]
cdat = []
for f in ncdir
zr = ZipFile.Reader(f)
push!(cdat, Symbol(split(zr.files[1].name, ".")[1])=>CSV.read(zr.files[1], DataFrame))
close(zr)
end