-module(dreki_tasks). -include("dreki.hrl"). -behaviour(dreki_store_namespace). -export([start/0, version/0, valid_store/4, format_item/1, actions/0, actions/1, schemas/0, new/0, as_tuple/1, as_map/1]). %% old stuff -export([resolve/1, exists/1, read_uri/2]). start() -> ok. valid_store(_Namespace, _Location, _Name, _BackendMod) -> ok. format_item(Item) -> ok. handlers() -> [dreki_tasks_script, dreki_tasks_cloyster, drekid_function]. -record(t, { id, version, schema, module, params }). version() -> 1. new() -> #{ <<"@schema">> => <<"task:1.0">>, <<"id">> => ksuid:gen_id(), <<"module">> => <<"dreki_tasks_cloyster">>, <<"params">> => #{ <<"@schema">> => <<"cloyster-task:1.0">>, <<"script">> => <<>> } }. actions() -> [ {new, <<"New task">>, {dreki_tasks, new, []}, {dreki_store, create, []}} ]. actions(_) -> [ {request, <<"Request run">>, {dreki_requests, new, []}, {dreki_store, create, []}} ]. schemas() -> Subs = lists:foldr(fun (Handler, Acc) -> maps:merge(Acc, Handler:schemas()) end, #{}, handlers()), maps:merge(Subs, #{ default => <<"task">>, <<"task">> => schemas(task) }). schemas(task) -> #{ default_version => <<"1.0">>, <<"1.0">> => schemas(task, <<"1.0">>) }. schemas(task, <<"1.0">>) -> Handlers = handlers(), Manifests0 = lists:map(fun (Handler) -> {Handler, Handler:schema_field(handler_manifest)} end, Handlers), Manifests = lists:foldr(fun ({Handler, undefined}, Acc) -> Acc; ({Handler, Schema}, Acc) -> Schemas = maps:fold(fun (Atom, _, Acc) when is_atom(Atom) -> Acc; (Vsn, _, Acc) -> [#{'$$ref' => <>} | Acc] end, [], maps:get(Schema, Handler:schemas())), Acc ++ Schemas end, [], Manifests0), #{ version => 'draft-06', title => <<"Task">>, type => object, properties => #{ id => #{type => string, <<"dreki:form">> => #{default => {ksuid, gen_id, []}}}, schema => #{type => string}, name => #{type => string, title => <<"Name">>}, handler => #{type => string, title => <<"Handler">>, enum => handlers()}, handler_manifest => #{'$ref' => <<"handler_manifest">>} }, '$defs' => #{ <<"handler_manifest">> => #{anyOf => Manifests} }, required => [handler, handler_manifest] }. as_tuple(#{id := Id, module := Module, params := Params}) -> #t{id = Id, version = undefined, schema = undefined, module = Module, params = Params}. as_map(Task = #t{}) -> #{ id => Task#t.id, version => Task#t.version, schema => Task#t.schema, module => Task#t.module, params => Task#t.params }. %% old stuff read_uri(undefined, Uri) -> {ok, #{stores => #{}}}; read_uri(Path, Uri) -> case binary:split(Path, <<":">>, [global]) of [<<>>, <<>>] -> {error, invalid_resource}; [<<>>, Id] -> {ok, #{resolve => #{kind => tasks, id => Id}}}; [S] -> {ok, #{store => #{kind => tasks, id => S}}}; [S, <<>>] -> {ok, #{store => #{kind => tasks, id => S}}}; [S, Id] -> {ok, #{resource => #{kind => tasks, store => S, id => Id}}}; R -> {error, {invalid_address, {Path, R}}} end. all(Uri) when is_binary(Uri) -> all(dreki_world:read_uri(Uri)); all({ok, Uri = #{kind := tasks, uri := Uri, resource := Res = #{store := #{id := _S}}}}) -> {ok, Mod, Store} = dreki_node:get_store(Uri), Mod:all(Store); all({ok, Uri}) -> {error, unresolvable_uri, Uri}; all(Error = {error, _}) -> Error. resolve(Id) -> Path = dreki_world:path(), case binary:match(Id, <>) of {0,End} -> StoreAndId = binary:part(Id, {End, byte_size(Id) - End}), [StoreN, LId] = binary:split(StoreAndId, <<":">>), {ok, {node(), StoreN, LId}} end. exists({Node, StoreN, LId}) when Node =:= node() -> #{mod := Mod, args := Args} = maps:get(StoreN, load_local_stores()), {ok, Db} = Mod:open(Args), Mod:exists(Db, LId); exists(N = {Node, _, _}) -> erpc:call(Node, dreki_tasks, exists, [N]); exists(Id) when is_binary(Id) -> case resolve(Id) of {ok, N} -> exists(N); Error = {error, _} -> Error end. load_local_stores() -> {ok, Val} = application:get_env(dreki, local_tasks_stores), _World = #{path := Path, me := Me} = dreki_world:to_map(), MapFn = fun ({Name, Mod, Args, Env}, Acc) -> Store = #{local => true, node => Me, path => <>, mod => Mod, args => Args, env => Env, name => Name }, maps:put(Name, Store, Acc) end, lists:foldr(MapFn, #{}, Val).