From 4df8f3b4c251cc4a48cc570791b49199db33fd62 Mon Sep 17 00:00:00 2001
From: Allan Bowe <>
Date: Sun, 3 Jan 2021 22:16:11 +0000
Subject: [PATCH] feat: mv_getjobcode macro, introducing LUA macros
---
README.md | 22 +++
all.sas | 265 +++++++++++++++++----------
build.py | 8 +-
lua/{json2sas.lua => json.lua} | 94 +---------
lua/{ml_json2sas.sas => ml_json.sas} | 109 ++---------
viya/mv_deletejes.sas | 4 +-
viya/mv_getjobcode.sas | 150 +++++++++++++++
7 files changed, 365 insertions(+), 287 deletions(-)
rename lua/{json2sas.lua => json.lua} (80%)
rename lua/{ml_json2sas.sas => ml_json.sas} (79%)
create mode 100644 viya/mv_getjobcode.sas
diff --git a/README.md b/README.md
index fefe2a9..684911b 100644
--- a/README.md
+++ b/README.md
@@ -40,6 +40,27 @@ Documentation: https://sasjs.github.io/core.github.io/files.html
- X command enabled
- Prefixes: _mmw_,_mmu_,_mmx_
+**lua** library
+
+Wait - this is a macro library - what is LUA doing here? Well, it is a little known fact that you CAN run LUA within a SAS Macro. It has to be written to a text file with a `.lua` extension, from where you can `%include` it. So, without using the `proc lua` wrapper.
+
+To contribute, simply write your freeform LUA in the LUA folder. Then run the `build.py`, which will convert your LUA into a data step with put statements, and create the macro wrapper with a `ml_` prefix. You can then use your module in any program by running:
+
+```
+/* compile the lua module */
+%ml_yourmodule()
+
+/* Execute. Do not use the restart keyword! */
+proc lua;
+submit;
+ print(yourStuff);
+endsubmit;
+run;
+```
+
+- X command enabled
+- Prefixes: _mmw_,_mmu_,_mmx_
+
# Installation
First, download the repo to a location your SAS system can access. Then update your sasautos path to include the components you wish to have available,eg:
@@ -72,6 +93,7 @@ filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
- _mm_ for metadata macros (interface with the metadata server).
- _mmx_ for macros that use metadata and are XCMD enabled
- _mx_ for macros that are XCMD enabled
+ - _ml_ for macros that are used to compile LUA modules
- _mv_ for macros that will only work in Viya
- follow verb-noun convention
- unix style line endings (lf)
diff --git a/all.sas b/all.sas
index ec1c987..f50f197 100644
--- a/all.sas
+++ b/all.sas
@@ -11488,8 +11488,8 @@ filename &fname1a clear;
libname &libref1a clear;
%mend;/**
- @file mv_deletejes.sas
- @brief Creates a job execution service if it does not already exist
+ @file
+ @brief Deletes a Viya Job, if it exists
@details If not executed in Studio 5+ will expect oauth token in a global
macro variable (default ACCESS_TOKEN).
@@ -12247,7 +12247,157 @@ run;
filename &fname1 clear;
libname &libref1 clear;
-%mend; /**
+%mend;/**
+ @file
+ @brief Extract the source code from a SAS Viya Job
+ @details Extracts the SAS code from a Job into a fileref or physical file.
+ Example:
+
+ %mv_getjobcode(
+ path=/Public/jobs
+ ,name=some_job
+ ,outfile=/tmp/some_job.sas
+ )
+
+ @param [in] access_token_var= The global macro variable to contain the access token
+ @param [in] grant_type= valid values:
+ * password
+ * authorization_code
+ * detect - will check if access_token exists, if not will use sas_services if
+ a SASStudioV session else authorization_code. Default option.
+ * sas_services - will use oauth_bearer=sas_services
+ @param [in] path= The SAS Drive path of the job
+ @param [in] name= The name of the job
+ @param [out] outref= A fileref to which to write the source code
+ @param [out] outfile= A file to which to write the source code
+
+ @version VIYA V.03.04
+ @author Allan Bowe, source: https://github.com/sasjs/core
+
+
SAS Macros
+ @li mp_abort.sas
+ @li mf_getplatform.sas
+ @li mf_getuniquefileref.sas
+ @li mv_getfoldermembers.sas
+ @li ml_json.sas
+
+**/
+
+%macro mv_getjobcode(outref=0,outfile=0
+ ,name=0,path=0
+ ,contextName=SAS Job Execution compute context
+ ,access_token_var=ACCESS_TOKEN
+ ,grant_type=sas_services
+ );
+%local oauth_bearer;
+%if &grant_type=detect %then %do;
+ %if %symexist(&access_token_var) %then %let grant_type=authorization_code;
+ %else %let grant_type=sas_services;
+%end;
+%if &grant_type=sas_services %then %do;
+ %let oauth_bearer=oauth_bearer=sas_services;
+ %let &access_token_var=;
+%end;
+%put &sysmacroname: grant_type=&grant_type;
+%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
+ and &grant_type ne sas_services
+ )
+ ,mac=&sysmacroname
+ ,msg=%str(Invalid value for grant_type: &grant_type)
+)
+%mp_abort(iftrue=("&path"="0")
+ ,mac=&sysmacroname
+ ,msg=%str(Job Path not provided)
+)
+%mp_abort(iftrue=("&name"="0")
+ ,mac=&sysmacroname
+ ,msg=%str(Job Name not provided)
+)
+%mp_abort(iftrue=("&outfile"="0" and "&outref"="0")
+ ,mac=&sysmacroname
+ ,msg=%str(Output destination (file or fileref) must be provided)
+)
+options noquotelenmax;
+%local base_uri; /* location of rest apis */
+%let base_uri=%mf_getplatform(VIYARESTAPI);
+data;run;
+%local foldermembers;
+%let foldermembers=&syslast;
+%mv_getfoldermembers(root=&path
+ ,access_token_var=&access_token_var
+ ,grant_type=&grant_type
+ ,outds=&foldermembers
+)
+%local joburi;
+%let joburi=0;
+data _null_;
+ set &foldermembers;
+ if name="&name" and uri=:'/jobDefinitions/definitions'
+ then call symputx('joburi',uri);
+run;
+%mp_abort(iftrue=("&joburi"="0")
+ ,mac=&sysmacroname
+ ,msg=%str(Job &path/&name not found)
+)
+
+/* prepare request*/
+%local fname1;
+%let fname1=%mf_getuniquefileref();
+proc http method='GET' out=&fname1 &oauth_bearer
+ url="&base_uri&joburi";
+ headers "Accept"="application/vnd.sas.job.definition+json"
+ %if &grant_type=authorization_code %then %do;
+ "Authorization"="Bearer &&&access_token_var"
+ %end;
+ ;
+run;
+%if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then
+%do;
+ data _null_;infile &fname1;input;putlog _infile_;run;
+ %mp_abort(mac=&sysmacroname
+ ,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
+ )
+%end;
+%local fname2 fname3 fpath1 fpath2 fpath3;
+%let fname2=%mf_getuniquefileref();
+%let fname3=%mf_getuniquefileref();
+%let fpath1=%sysfunc(pathname(&fname1));
+%let fpath2=%sysfunc(pathname(&fname2));
+%let fpath3=%sysfunc(pathname(&fname2));
+
+/* compile the lua JSON module */
+%ml_json()
+/* read using LUA - this allows the code to be of any length */
+data _null_;
+ file "&fpath3..lua";
+ put '
+ infile = io.open (sas.symget("fpath1"), "r")
+ outfile = io.open (sas.symget("fpath2"), "w")
+ io.input(infile)
+ local resp=json2sas.decode(io.read())
+ local job=resp["code"]
+ outfile:write(job)
+ io.close(infile)
+ io.close(outfile)
+ ';
+run;
+%inc "&fpath3..lua";
+/* export to desired destination */
+data _null_;
+ %if &outref=0 %then %do;
+ file "&outfile" lrecl=32767;
+ %end;
+ %else %do;
+ file &outref;
+ %end;
+ infile &fname2;
+ input;
+ put _infile_;
+run;
+filename &fname1 clear;
+filename &fname2 clear;
+%mend;
+ /**
@file mv_getrefreshtoken.sas
@brief deprecated - replaced by mv_tokenauth.sas
@@ -13385,20 +13535,21 @@ filename &fref1 clear;
%mend;
/**
- @file ml_json2sas.sas
- @brief Creates the json2sas.lua file
- @details Writes json2sas.lua to the work directory
+ @file ml_json.sas
+ @brief Compiles the json.lua lua file
+ @details Writes json.lua to the work directory
+ and then includes it.
Usage:
- %ml_json2sas()
+ %ml_json()
**/
-%macro ml_json2sas();
+%macro ml_json();
data _null_;
- file "%sysfunc(pathname(work))/json2sas.lua";
+ file "%sysfunc(pathname(work))/ml_json.lua";
put '-- ';
- put '-- json2sas.lua (modified from json.lua) ';
+ put '-- json.lua (modified from json.lua) ';
put '-- ';
put '-- Copyright (c) 2019 rxi ';
put '-- ';
@@ -13421,7 +13572,7 @@ data _null_;
put '-- SOFTWARE. ';
put '-- ';
put ' ';
- put 'local json2sas = { _version = "0.1.2" } ';
+ put 'local json = { _version = "0.1.2" } ';
put ' ';
put '------------------------------------------------------------------------------- ';
put '-- Encode ';
@@ -13521,7 +13672,7 @@ data _null_;
put ' error("unexpected type ''" .. t .. "''") ';
put 'end ';
put ' ';
- put 'function json2sas.encode(val) ';
+ put 'function json.encode(val) ';
put ' return ( encode(val) ) ';
put 'end ';
put ' ';
@@ -13755,7 +13906,7 @@ data _null_;
put ' decode_error(str, idx, "unexpected character ''" .. chr .. "''") ';
put 'end ';
put ' ';
- put 'function json2sas.decode(str) ';
+ put 'function json.decode(str) ';
put ' if type(str) ~= "string" then ';
put ' error("expected argument of type string, got " .. type(str)) ';
put ' end ';
@@ -13767,90 +13918,8 @@ data _null_;
put ' return res ';
put 'end ';
put ' ';
- put '-- convert macro variable array into one variable and decode ';
- put 'function json2sas.go(macvar) ';
- put ' local x=1 ';
- put ' local cnt=0 ';
- put ' local mac=sas.symget(macvar..''0'') ';
- put ' local newstr='''' ';
- put ' if mac and mac ~= '''' then ';
- put ' cnt=mac ';
- put ' for x=1,cnt,1 do ';
- put ' mac=sas.symget(macvar..x) ';
- put ' if mac and mac ~= '''' then ';
- put ' newstr=newstr..mac ';
- put ' else ';
- put ' return print(macvar..x..'' NOT FOUND!!'') ';
- put ' end ';
- put ' end ';
- put ' else ';
- put ' return print(macvar..''0 NOT FOUND!!'') ';
- put ' end ';
- put ' -- print(''mac:''..mac..''cnt:''..cnt..''newstr''..newstr) ';
- put ' local oneVar=json2sas.decode(newstr) ';
- put ' local jsdata=oneVar["data"] ';
- put ' local meta={} ';
- put ' local attrs={} ';
- put ' for tablename, data in pairs(jsdata) do -- each table ';
- put ' print("Processing table: "..tablename) ';
- put ' attrs[tablename]={} ';
- put ' for k, v in ipairs(data) do -- each row ';
- put ' if(k==1) then -- column names ';
- put ' for a, b in pairs(v) do ';
- put ' attrs[tablename][a]={} ';
- put ' attrs[tablename][a]["name"]=b ';
- put ' end ';
- put ' elseif(k==2) then -- get types ';
- put ' for a, b in pairs(v) do ';
- put ' if type(b)==''number'' then ';
- put ' attrs[tablename][a]["type"]="N" ';
- put ' attrs[tablename][a]["length"]=8 ';
- put ' else ';
- put ' attrs[tablename][a]["type"]="C" ';
- put ' attrs[tablename][a]["length"]=string.len(b) ';
- put ' end ';
- put ' end ';
- put ' else --update lengths ';
- put ' for a, b in pairs(v) do ';
- put ' if (type(b)==''string'' and string.len(b)>attrs[tablename][a]["length"]) ';
- put ' then ';
- put ' attrs[tablename][a]["length"]=string.len(b) ';
- put ' end ';
- put ' end ';
- put ' end ';
- put ' end ';
- put ' print(json2sas.encode(attrs[tablename])) -- show results ';
- put ' ';
- put ' -- Now create the SAS table ';
- put ' sas.new_table("work."..tablename,attrs[tablename]) ';
- put ' local dsid=sas.open("work."..tablename, "u") ';
- put ' for k, v in ipairs(data) do ';
- put ' if k>1 then ';
- put ' sas.append(dsid) ';
- put ' for a, b in pairs(v) do ';
- put ' sas.put_value(dsid, attrs[tablename][a]["name"], b) ';
- put ' end ';
- put ' sas.update(dsid) ';
- put ' end ';
- put ' end ';
- put ' sas.close(dsid) ';
- put ' end ';
- put ' return json2sas.decode(newstr) ';
- put 'end ';
- put ' ';
- put ' ';
- put 'function quote(str) ';
- put ' return sas.quote(str) ';
- put 'end ';
- put 'function sasvar(str) ';
- put ' print("processing: "..str) ';
- put ' print(sas.symexist(str)) ';
- put ' if sas.symexist(str)==1 then ';
- put ' return quote(str)..'':''..quote(sas.symget(str))..'','' ';
- put ' end ';
- put ' return '''' ';
- put 'end ';
- put ' ';
- put 'return json2sas ';
+ put 'return json ';
run;
%mend;
+
+%inc "%sysfunc(pathname(work))/ml_json.lua";
diff --git a/build.py b/build.py
index b34a57d..fc36bfc 100755
--- a/build.py
+++ b/build.py
@@ -9,19 +9,21 @@ for file in files:
ml = open('lua/' + name + '.sas', "w")
ml.write("/**\n")
ml.write(" @file " + name + '.sas\n')
- ml.write(" @brief Creates the " + basename + " file\n")
+ ml.write(" @brief Compiles the " + basename + " lua file\n")
ml.write(" @details Writes " + basename + " to the work directory\n")
+ ml.write(" and then includes it.\n")
ml.write(" Usage:\n\n")
ml.write(" %" + name + "()\n\n")
ml.write("**/\n\n")
ml.write("%macro " + name + "();\n")
ml.write("data _null_;\n")
- ml.write(" file \"%sysfunc(pathname(work))/" + basename + "\";\n")
+ ml.write(" file \"%sysfunc(pathname(work))/" + name + ".lua\";\n")
with open(file) as infile:
for line in infile:
ml.write(" put '" + line.rstrip().replace("'","''") + " ';\n")
ml.write("run;\n")
- ml.write("%mend;\n")
+ ml.write("%mend;\n\n")
+ ml.write("%inc \"%sysfunc(pathname(work))/" + name + ".lua\";\n")
ml.close()
# prepare web files
diff --git a/lua/json2sas.lua b/lua/json.lua
similarity index 80%
rename from lua/json2sas.lua
rename to lua/json.lua
index 8809c1c..fc39240 100644
--- a/lua/json2sas.lua
+++ b/lua/json.lua
@@ -1,5 +1,5 @@
--
--- json2sas.lua (modified from json.lua)
+-- json.lua (modified from json.lua)
--
-- Copyright (c) 2019 rxi
--
@@ -22,7 +22,7 @@
-- SOFTWARE.
--
-local json2sas = { _version = "0.1.2" }
+local json = { _version = "0.1.2" }
-------------------------------------------------------------------------------
-- Encode
@@ -122,7 +122,7 @@ encode = function(val, stack)
error("unexpected type '" .. t .. "'")
end
-function json2sas.encode(val)
+function json.encode(val)
return ( encode(val) )
end
@@ -356,7 +356,7 @@ parse = function(str, idx)
decode_error(str, idx, "unexpected character '" .. chr .. "'")
end
-function json2sas.decode(str)
+function json.decode(str)
if type(str) ~= "string" then
error("expected argument of type string, got " .. type(str))
end
@@ -368,88 +368,4 @@ function json2sas.decode(str)
return res
end
--- convert macro variable array into one variable and decode
-function json2sas.go(macvar)
- local x=1
- local cnt=0
- local mac=sas.symget(macvar..'0')
- local newstr=''
- if mac and mac ~= '' then
- cnt=mac
- for x=1,cnt,1 do
- mac=sas.symget(macvar..x)
- if mac and mac ~= '' then
- newstr=newstr..mac
- else
- return print(macvar..x..' NOT FOUND!!')
- end
- end
- else
- return print(macvar..'0 NOT FOUND!!')
- end
- -- print('mac:'..mac..'cnt:'..cnt..'newstr'..newstr)
- local oneVar=json2sas.decode(newstr)
- local jsdata=oneVar["data"]
- local meta={}
- local attrs={}
- for tablename, data in pairs(jsdata) do -- each table
- print("Processing table: "..tablename)
- attrs[tablename]={}
- for k, v in ipairs(data) do -- each row
- if(k==1) then -- column names
- for a, b in pairs(v) do
- attrs[tablename][a]={}
- attrs[tablename][a]["name"]=b
- end
- elseif(k==2) then -- get types
- for a, b in pairs(v) do
- if type(b)=='number' then
- attrs[tablename][a]["type"]="N"
- attrs[tablename][a]["length"]=8
- else
- attrs[tablename][a]["type"]="C"
- attrs[tablename][a]["length"]=string.len(b)
- end
- end
- else --update lengths
- for a, b in pairs(v) do
- if (type(b)=='string' and string.len(b)>attrs[tablename][a]["length"])
- then
- attrs[tablename][a]["length"]=string.len(b)
- end
- end
- end
- end
- print(json2sas.encode(attrs[tablename])) -- show results
-
- -- Now create the SAS table
- sas.new_table("work."..tablename,attrs[tablename])
- local dsid=sas.open("work."..tablename, "u")
- for k, v in ipairs(data) do
- if k>1 then
- sas.append(dsid)
- for a, b in pairs(v) do
- sas.put_value(dsid, attrs[tablename][a]["name"], b)
- end
- sas.update(dsid)
- end
- end
- sas.close(dsid)
- end
- return json2sas.decode(newstr)
-end
-
-
-function quote(str)
- return sas.quote(str)
-end
-function sasvar(str)
- print("processing: "..str)
- print(sas.symexist(str))
- if sas.symexist(str)==1 then
- return quote(str)..':'..quote(sas.symget(str))..','
- end
- return ''
-end
-
-return json2sas
\ No newline at end of file
+return json
\ No newline at end of file
diff --git a/lua/ml_json2sas.sas b/lua/ml_json.sas
similarity index 79%
rename from lua/ml_json2sas.sas
rename to lua/ml_json.sas
index 40f5666..fc4d5ad 100644
--- a/lua/ml_json2sas.sas
+++ b/lua/ml_json.sas
@@ -1,18 +1,19 @@
/**
- @file ml_json2sas.sas
- @brief Creates the json2sas.lua file
- @details Writes json2sas.lua to the work directory
+ @file ml_json.sas
+ @brief Compiles the json.lua lua file
+ @details Writes json.lua to the work directory
+ and then includes it.
Usage:
- %ml_json2sas()
+ %ml_json()
**/
-%macro ml_json2sas();
+%macro ml_json();
data _null_;
- file "%sysfunc(pathname(work))/json2sas.lua";
+ file "%sysfunc(pathname(work))/ml_json.lua";
put '-- ';
- put '-- json2sas.lua (modified from json.lua) ';
+ put '-- json.lua (modified from json.lua) ';
put '-- ';
put '-- Copyright (c) 2019 rxi ';
put '-- ';
@@ -35,7 +36,7 @@ data _null_;
put '-- SOFTWARE. ';
put '-- ';
put ' ';
- put 'local json2sas = { _version = "0.1.2" } ';
+ put 'local json = { _version = "0.1.2" } ';
put ' ';
put '------------------------------------------------------------------------------- ';
put '-- Encode ';
@@ -135,7 +136,7 @@ data _null_;
put ' error("unexpected type ''" .. t .. "''") ';
put 'end ';
put ' ';
- put 'function json2sas.encode(val) ';
+ put 'function json.encode(val) ';
put ' return ( encode(val) ) ';
put 'end ';
put ' ';
@@ -369,7 +370,7 @@ data _null_;
put ' decode_error(str, idx, "unexpected character ''" .. chr .. "''") ';
put 'end ';
put ' ';
- put 'function json2sas.decode(str) ';
+ put 'function json.decode(str) ';
put ' if type(str) ~= "string" then ';
put ' error("expected argument of type string, got " .. type(str)) ';
put ' end ';
@@ -381,90 +382,8 @@ data _null_;
put ' return res ';
put 'end ';
put ' ';
- put '-- convert macro variable array into one variable and decode ';
- put 'function json2sas.go(macvar) ';
- put ' local x=1 ';
- put ' local cnt=0 ';
- put ' local mac=sas.symget(macvar..''0'') ';
- put ' local newstr='''' ';
- put ' if mac and mac ~= '''' then ';
- put ' cnt=mac ';
- put ' for x=1,cnt,1 do ';
- put ' mac=sas.symget(macvar..x) ';
- put ' if mac and mac ~= '''' then ';
- put ' newstr=newstr..mac ';
- put ' else ';
- put ' return print(macvar..x..'' NOT FOUND!!'') ';
- put ' end ';
- put ' end ';
- put ' else ';
- put ' return print(macvar..''0 NOT FOUND!!'') ';
- put ' end ';
- put ' -- print(''mac:''..mac..''cnt:''..cnt..''newstr''..newstr) ';
- put ' local oneVar=json2sas.decode(newstr) ';
- put ' local jsdata=oneVar["data"] ';
- put ' local meta={} ';
- put ' local attrs={} ';
- put ' for tablename, data in pairs(jsdata) do -- each table ';
- put ' print("Processing table: "..tablename) ';
- put ' attrs[tablename]={} ';
- put ' for k, v in ipairs(data) do -- each row ';
- put ' if(k==1) then -- column names ';
- put ' for a, b in pairs(v) do ';
- put ' attrs[tablename][a]={} ';
- put ' attrs[tablename][a]["name"]=b ';
- put ' end ';
- put ' elseif(k==2) then -- get types ';
- put ' for a, b in pairs(v) do ';
- put ' if type(b)==''number'' then ';
- put ' attrs[tablename][a]["type"]="N" ';
- put ' attrs[tablename][a]["length"]=8 ';
- put ' else ';
- put ' attrs[tablename][a]["type"]="C" ';
- put ' attrs[tablename][a]["length"]=string.len(b) ';
- put ' end ';
- put ' end ';
- put ' else --update lengths ';
- put ' for a, b in pairs(v) do ';
- put ' if (type(b)==''string'' and string.len(b)>attrs[tablename][a]["length"]) ';
- put ' then ';
- put ' attrs[tablename][a]["length"]=string.len(b) ';
- put ' end ';
- put ' end ';
- put ' end ';
- put ' end ';
- put ' print(json2sas.encode(attrs[tablename])) -- show results ';
- put ' ';
- put ' -- Now create the SAS table ';
- put ' sas.new_table("work."..tablename,attrs[tablename]) ';
- put ' local dsid=sas.open("work."..tablename, "u") ';
- put ' for k, v in ipairs(data) do ';
- put ' if k>1 then ';
- put ' sas.append(dsid) ';
- put ' for a, b in pairs(v) do ';
- put ' sas.put_value(dsid, attrs[tablename][a]["name"], b) ';
- put ' end ';
- put ' sas.update(dsid) ';
- put ' end ';
- put ' end ';
- put ' sas.close(dsid) ';
- put ' end ';
- put ' return json2sas.decode(newstr) ';
- put 'end ';
- put ' ';
- put ' ';
- put 'function quote(str) ';
- put ' return sas.quote(str) ';
- put 'end ';
- put 'function sasvar(str) ';
- put ' print("processing: "..str) ';
- put ' print(sas.symexist(str)) ';
- put ' if sas.symexist(str)==1 then ';
- put ' return quote(str)..'':''..quote(sas.symget(str))..'','' ';
- put ' end ';
- put ' return '''' ';
- put 'end ';
- put ' ';
- put 'return json2sas ';
+ put 'return json ';
run;
%mend;
+
+%inc "%sysfunc(pathname(work))/ml_json.lua";
diff --git a/viya/mv_deletejes.sas b/viya/mv_deletejes.sas
index 9fe6edf..3759d8f 100644
--- a/viya/mv_deletejes.sas
+++ b/viya/mv_deletejes.sas
@@ -1,6 +1,6 @@
/**
- @file mv_deletejes.sas
- @brief Creates a job execution service if it does not already exist
+ @file
+ @brief Deletes a Viya Job, if it exists
@details If not executed in Studio 5+ will expect oauth token in a global
macro variable (default ACCESS_TOKEN).
diff --git a/viya/mv_getjobcode.sas b/viya/mv_getjobcode.sas
new file mode 100644
index 0000000..f45d9cb
--- /dev/null
+++ b/viya/mv_getjobcode.sas
@@ -0,0 +1,150 @@
+/**
+ @file
+ @brief Extract the source code from a SAS Viya Job
+ @details Extracts the SAS code from a Job into a fileref or physical file.
+ Example:
+
+ %mv_getjobcode(
+ path=/Public/jobs
+ ,name=some_job
+ ,outfile=/tmp/some_job.sas
+ )
+
+ @param [in] access_token_var= The global macro variable to contain the access token
+ @param [in] grant_type= valid values:
+ * password
+ * authorization_code
+ * detect - will check if access_token exists, if not will use sas_services if
+ a SASStudioV session else authorization_code. Default option.
+ * sas_services - will use oauth_bearer=sas_services
+ @param [in] path= The SAS Drive path of the job
+ @param [in] name= The name of the job
+ @param [out] outref= A fileref to which to write the source code
+ @param [out] outfile= A file to which to write the source code
+
+ @version VIYA V.03.04
+ @author Allan Bowe, source: https://github.com/sasjs/core
+
+ SAS Macros
+ @li mp_abort.sas
+ @li mf_getplatform.sas
+ @li mf_getuniquefileref.sas
+ @li mv_getfoldermembers.sas
+ @li ml_json.sas
+
+**/
+
+%macro mv_getjobcode(outref=0,outfile=0
+ ,name=0,path=0
+ ,contextName=SAS Job Execution compute context
+ ,access_token_var=ACCESS_TOKEN
+ ,grant_type=sas_services
+ );
+%local oauth_bearer;
+%if &grant_type=detect %then %do;
+ %if %symexist(&access_token_var) %then %let grant_type=authorization_code;
+ %else %let grant_type=sas_services;
+%end;
+%if &grant_type=sas_services %then %do;
+ %let oauth_bearer=oauth_bearer=sas_services;
+ %let &access_token_var=;
+%end;
+%put &sysmacroname: grant_type=&grant_type;
+%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
+ and &grant_type ne sas_services
+ )
+ ,mac=&sysmacroname
+ ,msg=%str(Invalid value for grant_type: &grant_type)
+)
+%mp_abort(iftrue=("&path"="0")
+ ,mac=&sysmacroname
+ ,msg=%str(Job Path not provided)
+)
+%mp_abort(iftrue=("&name"="0")
+ ,mac=&sysmacroname
+ ,msg=%str(Job Name not provided)
+)
+%mp_abort(iftrue=("&outfile"="0" and "&outref"="0")
+ ,mac=&sysmacroname
+ ,msg=%str(Output destination (file or fileref) must be provided)
+)
+options noquotelenmax;
+%local base_uri; /* location of rest apis */
+%let base_uri=%mf_getplatform(VIYARESTAPI);
+data;run;
+%local foldermembers;
+%let foldermembers=&syslast;
+%mv_getfoldermembers(root=&path
+ ,access_token_var=&access_token_var
+ ,grant_type=&grant_type
+ ,outds=&foldermembers
+)
+%local joburi;
+%let joburi=0;
+data _null_;
+ set &foldermembers;
+ if name="&name" and uri=:'/jobDefinitions/definitions'
+ then call symputx('joburi',uri);
+run;
+%mp_abort(iftrue=("&joburi"="0")
+ ,mac=&sysmacroname
+ ,msg=%str(Job &path/&name not found)
+)
+
+/* prepare request*/
+%local fname1;
+%let fname1=%mf_getuniquefileref();
+proc http method='GET' out=&fname1 &oauth_bearer
+ url="&base_uri&joburi";
+ headers "Accept"="application/vnd.sas.job.definition+json"
+ %if &grant_type=authorization_code %then %do;
+ "Authorization"="Bearer &&&access_token_var"
+ %end;
+ ;
+run;
+%if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201 %then
+%do;
+ data _null_;infile &fname1;input;putlog _infile_;run;
+ %mp_abort(mac=&sysmacroname
+ ,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
+ )
+%end;
+%local fname2 fname3 fpath1 fpath2 fpath3;
+%let fname2=%mf_getuniquefileref();
+%let fname3=%mf_getuniquefileref();
+%let fpath1=%sysfunc(pathname(&fname1));
+%let fpath2=%sysfunc(pathname(&fname2));
+%let fpath3=%sysfunc(pathname(&fname2));
+
+/* compile the lua JSON module */
+%ml_json()
+/* read using LUA - this allows the code to be of any length */
+data _null_;
+ file "&fpath3..lua";
+ put '
+ infile = io.open (sas.symget("fpath1"), "r")
+ outfile = io.open (sas.symget("fpath2"), "w")
+ io.input(infile)
+ local resp=json2sas.decode(io.read())
+ local job=resp["code"]
+ outfile:write(job)
+ io.close(infile)
+ io.close(outfile)
+ ';
+run;
+%inc "&fpath3..lua";
+/* export to desired destination */
+data _null_;
+ %if &outref=0 %then %do;
+ file "&outfile" lrecl=32767;
+ %end;
+ %else %do;
+ file &outref;
+ %end;
+ infile &fname2;
+ input;
+ put _infile_;
+run;
+filename &fname1 clear;
+filename &fname2 clear;
+%mend;