Packages

A library for creating LTI 1.3 Tools in Gleam

Current section

Files

Jump to
lightbulb src lightbulb@services@ags.erl
Raw

src/lightbulb@services@ags.erl

-module(lightbulb@services@ags).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([create_line_item/6, post_score/4, fetch_or_create_line_item/6, get_lti_ags_claim/1, get_line_items_service_url/1, grade_passback_available/1]).
-export_type([ags_claim/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type ags_claim() :: {ags_claim,
gleam@option:option(binary()),
gleam@option:option(binary()),
list(binary()),
gleam@dict:dict(binary(), gleam@dynamic:dynamic_()),
gleam@option:option(gleam@dynamic:dynamic_())}.
-file("src/lightbulb/services/ags.gleam", 266).
-spec set_line_items_headers(gleam@http@request:request(binary())) -> gleam@http@request:request(binary()).
set_line_items_headers(Req) ->
_pipe = Req,
_pipe@1 = gleam@http@request:set_header(
_pipe,
<<"Content-Type"/utf8>>,
<<"application/vnd.ims.lis.v2.lineitem+json"/utf8>>
),
gleam@http@request:set_header(
_pipe@1,
<<"Accept"/utf8>>,
<<"application/vnd.ims.lis.v2.lineitemcontainer+json"/utf8>>
).
-file("src/lightbulb/services/ags.gleam", 127).
?DOC(" Creates a new line item.\n").
-spec create_line_item(
lightbulb@providers@http_provider:http_provider(),
binary(),
binary(),
float(),
binary(),
lightbulb@services@access_token:access_token()
) -> {ok, lightbulb@services@ags@line_item:line_item()} | {error, binary()}.
create_line_item(
Http_provider,
Line_items_service_url,
Resource_id,
Score_maximum,
Label,
Access_token
) ->
Line_item = {line_item, none, Score_maximum, Label, Resource_id},
Body = begin
_pipe = lightbulb@services@ags@line_item:to_json(Line_item),
gleam@json:to_string(_pipe)
end,
gleam@result:'try'(
begin
_pipe@1 = gleam@http@request:to(Line_items_service_url),
gleam@result:replace_error(
_pipe@1,
<<"Error creating request for URL "/utf8,
Line_items_service_url/binary>>
)
end,
fun(Req) ->
Req@1 = begin
_pipe@2 = Req,
_pipe@3 = set_line_items_headers(_pipe@2),
_pipe@4 = lightbulb@services@access_token:set_authorization_header(
_pipe@3,
Access_token
),
_pipe@5 = gleam@http@request:set_method(_pipe@4, post),
gleam@http@request:set_body(_pipe@5, Body)
end,
case (erlang:element(2, Http_provider))(Req@1) of
{ok, Res} ->
case erlang:element(2, Res) of
200 ->
_pipe@6 = gleam@json:parse(
erlang:element(4, Res),
lightbulb@services@ags@line_item:decoder()
),
gleam@result:map_error(
_pipe@6,
fun(E) ->
<<"Error decoding line item: "/utf8,
(gleam@string:inspect(E))/binary>>
end
);
201 ->
_pipe@6 = gleam@json:parse(
erlang:element(4, Res),
lightbulb@services@ags@line_item:decoder()
),
gleam@result:map_error(
_pipe@6,
fun(E) ->
<<"Error decoding line item: "/utf8,
(gleam@string:inspect(E))/binary>>
end
);
E@1 ->
lightbulb@utils@logger:error_meta(
<<"Error creating line item"/utf8>>,
Res
),
{error,
<<"Unexpected status: "/utf8,
(gleam@string:inspect(E@1))/binary>>}
end;
E@2 ->
lightbulb@utils@logger:error_meta(
<<"Error creating line item"/utf8>>,
E@2
),
{error, <<"Error creating new line item"/utf8>>}
end
end
).
-file("src/lightbulb/services/ags.gleam", 278).
-spec set_score_headers(gleam@http@request:request(binary())) -> gleam@http@request:request(binary()).
set_score_headers(Req) ->
_pipe = Req,
_pipe@1 = gleam@http@request:set_header(
_pipe,
<<"Content-Type"/utf8>>,
<<"application/vnd.ims.lis.v1.score+json"/utf8>>
),
gleam@http@request:set_header(
_pipe@1,
<<"Accept"/utf8>>,
<<"application/vnd.ims.lis.v2.lineitemcontainer+json"/utf8>>
).
-file("src/lightbulb/services/ags.gleam", 287).
-spec build_url_with_path(binary(), binary()) -> binary().
build_url_with_path(Base, Path) ->
case gleam@string:split(Base, <<"?"/utf8>>) of
[Base@1, Query] ->
<<<<<<<<Base@1/binary, "/"/utf8>>/binary, Path/binary>>/binary,
"?"/utf8>>/binary,
Query/binary>>;
_ ->
<<<<Base/binary, "/"/utf8>>/binary, Path/binary>>
end.
-file("src/lightbulb/services/ags.gleam", 29).
?DOC(" Posts a score to the AGS service for a given line item.\n").
-spec post_score(
lightbulb@providers@http_provider:http_provider(),
lightbulb@services@ags@score:score(),
lightbulb@services@ags@line_item:line_item(),
lightbulb@services@access_token:access_token()
) -> {ok, binary()} | {error, binary()}.
post_score(Http_provider, Score, Line_item, Access_token) ->
gleam@result:'try'(
begin
_pipe = erlang:element(2, Line_item),
gleam@option:to_result(_pipe, <<"Missing line item ID"/utf8>>)
end,
fun(Line_item_id) ->
Url = build_url_with_path(Line_item_id, <<"scores"/utf8>>),
Body = begin
_pipe@1 = lightbulb@services@ags@score:to_json(Score),
gleam@json:to_string(_pipe@1)
end,
gleam@result:'try'(
begin
_pipe@2 = gleam@http@request:to(Url),
gleam@result:replace_error(
_pipe@2,
<<"Error creating request for URL "/utf8, Url/binary>>
)
end,
fun(Req) ->
Req@1 = begin
_pipe@3 = Req,
_pipe@4 = set_score_headers(_pipe@3),
_pipe@5 = lightbulb@services@access_token:set_authorization_header(
_pipe@4,
Access_token
),
_pipe@6 = gleam@http@request:set_method(_pipe@5, post),
gleam@http@request:set_body(_pipe@6, Body)
end,
case (erlang:element(2, Http_provider))(Req@1) of
{ok, Res} ->
case erlang:element(2, Res) of
200 ->
{ok, erlang:element(4, Res)};
201 ->
{ok, erlang:element(4, Res)};
204 ->
{ok, erlang:element(4, Res)};
_ ->
{error,
<<"Unexpected status: "/utf8,
(gleam@string:inspect(Res))/binary>>}
end;
E ->
{error,
<<"Error posting score: "/utf8,
(gleam@string:inspect(E))/binary>>}
end
end
)
end
).
-file("src/lightbulb/services/ags.gleam", 294).
-spec build_url_with_params(binary(), list({binary(), binary()})) -> binary().
build_url_with_params(Base, Params) ->
case gleam_stdlib:parse_query(Base) of
{ok, Base_params} ->
<<<<Base/binary, "?"/utf8>>/binary,
(gleam@uri:query_to_string(lists:append(Base_params, Params)))/binary>>;
_ ->
<<<<Base/binary, "?"/utf8>>/binary,
(gleam@uri:query_to_string(Params))/binary>>
end.
-file("src/lightbulb/services/ags.gleam", 67).
?DOC(" Fetches an existing line item or creates a new one if it doesn't exist.\n").
-spec fetch_or_create_line_item(
lightbulb@providers@http_provider:http_provider(),
binary(),
binary(),
fun(() -> float()),
binary(),
lightbulb@services@access_token:access_token()
) -> {ok, lightbulb@services@ags@line_item:line_item()} | {error, binary()}.
fetch_or_create_line_item(
Http_provider,
Line_items_service_url,
Resource_id,
Maximum_score_provider,
Label,
Access_token
) ->
Url = build_url_with_params(
Line_items_service_url,
[{<<"resource_id"/utf8>>, Resource_id},
{<<"limit"/utf8>>, <<"1"/utf8>>}]
),
gleam@result:'try'(
begin
_pipe = gleam@http@request:to(Url),
gleam@result:replace_error(
_pipe,
<<"Error creating request for URL "/utf8, Url/binary>>
)
end,
fun(Req) ->
Req@1 = begin
_pipe@1 = Req,
_pipe@2 = set_line_items_headers(_pipe@1),
_pipe@3 = lightbulb@services@access_token:set_authorization_header(
_pipe@2,
Access_token
),
gleam@http@request:set_method(_pipe@3, get)
end,
case (erlang:element(2, Http_provider))(Req@1) of
{ok, Res} ->
case erlang:element(2, Res) of
200 ->
gleam@result:'try'(
begin
_pipe@4 = gleam@json:parse(
erlang:element(4, Res),
gleam@dynamic@decode:list(
lightbulb@services@ags@line_item:decoder(
)
)
),
gleam@result:map_error(
_pipe@4,
fun(E) ->
lightbulb@utils@logger:error_meta(
<<"Error decoding line items"/utf8>>,
E
),
<<"Error decoding line items"/utf8>>
end
)
end,
fun(Line_items) -> case Line_items of
[] ->
create_line_item(
Http_provider,
Line_items_service_url,
Resource_id,
Maximum_score_provider(),
Label,
Access_token
);
[Raw_line_item | _] ->
{ok, Raw_line_item}
end end
);
201 ->
gleam@result:'try'(
begin
_pipe@4 = gleam@json:parse(
erlang:element(4, Res),
gleam@dynamic@decode:list(
lightbulb@services@ags@line_item:decoder(
)
)
),
gleam@result:map_error(
_pipe@4,
fun(E) ->
lightbulb@utils@logger:error_meta(
<<"Error decoding line items"/utf8>>,
E
),
<<"Error decoding line items"/utf8>>
end
)
end,
fun(Line_items) -> case Line_items of
[] ->
create_line_item(
Http_provider,
Line_items_service_url,
Resource_id,
Maximum_score_provider(),
Label,
Access_token
);
[Raw_line_item | _] ->
{ok, Raw_line_item}
end end
);
_ ->
{error,
<<"Error retrieving existing line items"/utf8>>}
end;
_ ->
{error, <<"Error retrieving existing line items"/utf8>>}
end
end
).
-file("src/lightbulb/services/ags.gleam", 219).
-spec get_lti_ags_claim(gleam@dict:dict(binary(), gleam@dynamic:dynamic_())) -> {ok,
ags_claim()} |
{error, binary()}.
get_lti_ags_claim(Claims) ->
Lti_ags_claim_decoder = begin
gleam@dynamic@decode:optional_field(
<<"lineitem"/utf8>>,
none,
gleam@dynamic@decode:optional(
{decoder, fun gleam@dynamic@decode:decode_string/1}
),
fun(Lineitem) ->
gleam@dynamic@decode:optional_field(
<<"lineitems"/utf8>>,
none,
gleam@dynamic@decode:optional(
{decoder, fun gleam@dynamic@decode:decode_string/1}
),
fun(Lineitems) ->
gleam@dynamic@decode:field(
<<"scope"/utf8>>,
gleam@dynamic@decode:list(
{decoder,
fun gleam@dynamic@decode:decode_string/1}
),
fun(Scope) ->
gleam@dynamic@decode:optional_field(
<<"errors"/utf8>>,
maps:new(),
gleam@dynamic@decode:dict(
{decoder,
fun gleam@dynamic@decode:decode_string/1},
{decoder,
fun gleam@dynamic@decode:decode_dynamic/1}
),
fun(Errors) ->
gleam@dynamic@decode:optional_field(
<<"validation_context"/utf8>>,
none,
gleam@dynamic@decode:optional(
{decoder,
fun gleam@dynamic@decode:decode_dynamic/1}
),
fun(Validation_context) ->
gleam@dynamic@decode:success(
{ags_claim,
Lineitem,
Lineitems,
Scope,
Errors,
Validation_context}
)
end
)
end
)
end
)
end
)
end
)
end,
_pipe = gleam_stdlib:map_get(
Claims,
<<"https://purl.imsglobal.org/spec/lti-ags/claim/endpoint"/utf8>>
),
_pipe@1 = gleam@result:replace_error(
_pipe,
<<"Missing LTI AGS claim"/utf8>>
),
gleam@result:then(
_pipe@1,
fun(C) -> _pipe@2 = gleam@dynamic@decode:run(C, Lti_ags_claim_decoder),
gleam@result:replace_error(
_pipe@2,
<<"Invalid LTI AGS claim"/utf8>>
) end
).
-file("src/lightbulb/services/ags.gleam", 198).
-spec get_line_items_service_url(
gleam@dict:dict(binary(), gleam@dynamic:dynamic_())
) -> {ok, binary()} | {error, binary()}.
get_line_items_service_url(Lti_launch_claims) ->
begin
gleam@result:'try'(
get_lti_ags_claim(Lti_launch_claims),
fun(Lti_ags_claim) -> _pipe = erlang:element(3, Lti_ags_claim),
gleam@option:to_result(
_pipe,
<<"Missing line items URL in LTI AGS claim"/utf8>>
) end
)
end.
-file("src/lightbulb/services/ags.gleam", 185).
?DOC(
" Given a set of LTI claims, returns True if the grade passback\n"
" feature is available for the given LTI launch.\n"
).
-spec grade_passback_available(
gleam@dict:dict(binary(), gleam@dynamic:dynamic_())
) -> boolean().
grade_passback_available(Lti_launch_claims) ->
_pipe@1 = begin
gleam@result:'try'(
begin
_pipe = get_lti_ags_claim(Lti_launch_claims),
gleam@result:replace_error(_pipe, false)
end,
fun(Lti_ags_claim) ->
{ok,
gleam@list:contains(
erlang:element(4, Lti_ags_claim),
<<"https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly"/utf8>>
)}
end
)
end,
gleam@result:unwrap_both(_pipe@1).