Macros for SAS Application Developers
https://github.com/sasjs/core
ms_webout.sas
Go to the documentation of this file.
1 /**
2  @file
3  @brief Send data to/from sasjs/server
4  @details This macro should be added to the start of each web service,
5  **immediately** followed by a call to:
6 
7  %ms_webout(FETCH)
8 
9  This will read all the input data and create same-named SAS datasets in the
10  WORK library. You can then insert your code, and send data back using the
11  following syntax:
12 
13  data some datasets; * make some data ;
14  retain some columns;
15  run;
16 
17  %ms_webout(OPEN)
18  %ms_webout(ARR,some) * Array format, fast, suitable for large tables ;
19  %ms_webout(OBJ,datasets) * Object format, easier to work with ;
20  %ms_webout(CLOSE)
21 
22 
23  @param [in] action Either FETCH, OPEN, ARR, OBJ or CLOSE
24  @param [in] ds The dataset to send back to the frontend
25  @param [out] dslabel= value to use instead of table name for sending to JSON
26  @param [in] fmt= (N) Setting Y converts all vars to their formatted values
27  @param [out] fref= (_webout) The fileref to which to write the JSON
28  @param [in] missing= (NULL) Special numeric missing values can be sent as NULL
29  (eg `null`) or as STRING values (eg `".a"` or `".b"`)
30  @param [in] showmeta= (N) Set to Y to output metadata alongside each table,
31  such as the column formats and types. The metadata is contained inside an
32  object with the same name as the table but prefixed with a dollar sign - ie,
33  `,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
34  @param [in] workobs= (0) When set to a positive integer, will create a new
35  output object (WORK) which contains this number of observations from all
36  tables in the WORK library.
37  @param [in] maxobs= (MAX) Provide an integer to limit the number of input rows
38  that should be converted to output JSON
39 
40  <h4> SAS Macros </h4>
41  @li mf_getuser.sas
42  @li mp_jsonout.sas
43  @li mfs_httpheader.sas
44 
45  <h4> Related Macros </h4>
46  @li mv_webout.sas
47  @li mm_webout.sas
48 
49  @version 9.3
50  @author Allan Bowe
51 
52 **/
53 
54 %macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=N,missing=NULL
55  ,showmeta=N,maxobs=MAX,workobs=0
56 );
57 %global _webin_file_count _webin_fileref1 _webin_name1 _program _debug
58  sasjs_tables;
59 
60 %local i tempds;
61 %let action=%upcase(&action);
62 
63 %if &action=FETCH %then %do;
64  %if %str(&_debug) ge 131 %then %do;
65  options mprint notes mprintnest;
66  %end;
67  %let _webin_file_count=%eval(&_webin_file_count+0);
68  /* now read in the data */
69  %do i=1 %to &_webin_file_count;
70  %if &_webin_file_count=1 %then %do;
71  %let _webin_fileref1=&_webin_fileref;
72  %let _webin_name1=&_webin_name;
73  %end;
74  data _null_;
75  infile &&_webin_fileref&i termstr=crlf lrecl=32767;
76  input;
77  call symputx('input_statement',_infile_);
78  putlog "&&_webin_name&i input statement: " _infile_;
79  stop;
80  data &&_webin_name&i;
81  infile &&_webin_fileref&i firstobs=2 dsd termstr=crlf encoding='utf-8'
82  lrecl=32767;
83  input &input_statement;
84  %if %str(&_debug) ge 131 %then %do;
85  if _n_<20 then putlog _infile_;
86  %end;
87  run;
88  %let sasjs_tables=&sasjs_tables &&_webin_name&i;
89  %end;
90 %end;
91 
92 %else %if &action=OPEN %then %do;
93  /* fix encoding and ensure enough lrecl */
94  OPTIONS NOBOMFILE lrecl=32767;
95 
96  /* set the header */
97  %mfs_httpheader(Content-type,application/json)
98 
99  /* setup json. */
100  data _null_;file &fref encoding='utf-8' termstr=lf ;
101  put '{"SYSDATE" : "' "&SYSDATE" '"';
102  put ',"SYSTIME" : "' "&SYSTIME" '"';
103  run;
104 
105 %end;
106 
107 %else %if &action=ARR or &action=OBJ %then %do;
108  %if "%substr(&sysver,1,1)"="4" or "%substr(&sysver,1,1)"="5" %then %do;
109  /* functions in formats unsupported */
110  %put &sysmacroname: forcing missing back to NULL as feature not supported;
111  %let missing=NULL;
112  %end;
113  %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref
114  ,engine=DATASTEP,missing=&missing,showmeta=&showmeta,maxobs=&maxobs
115  )
116 %end;
117 %else %if &action=CLOSE %then %do;
118  %if %str(&workobs) > 0 %then %do;
119  /* if debug mode, send back first XX records of each work table also */
120  data;run;%let tempds=%scan(&syslast,2,.);
121  ods output Members=&tempds;
122  proc datasets library=WORK memtype=data;
123  %local wtcnt;%let wtcnt=0;
124  data _null_;
125  set &tempds;
126  if not (upcase(name) =:"DATA"); /* ignore temp datasets */
127  if not (upcase(name)=:"_DATA_");
128  i+1;
129  call symputx(cats('wt',i),name,'l');
130  call symputx('wtcnt',i,'l');
131  data _null_; file &fref mod encoding='utf-8' termstr=lf;
132  put ",""WORK"":{";
133  %do i=1 %to &wtcnt;
134  %let wt=&&wt&i;
135  data _null_; file &fref mod encoding='utf-8' termstr=lf;
136  dsid=open("WORK.&wt",'is');
137  nlobs=attrn(dsid,'NLOBS');
138  nvars=attrn(dsid,'NVARS');
139  rc=close(dsid);
140  if &i>1 then put ','@;
141  put " ""&wt"" : {";
142  put '"nlobs":' nlobs;
143  put ',"nvars":' nvars;
144  %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y
145  ,maxobs=&workobs
146  )
147  data _null_; file &fref mod encoding='utf-8' termstr=lf;
148  put "}";
149  %end;
150  data _null_; file &fref mod encoding='utf-8' termstr=lf;
151  put "}";
152  run;
153  %end;
154  /* close off json */
155  data _null_;file &fref mod encoding='utf-8' termstr=lf lrecl=32767;
156  length SYSPROCESSNAME syserrortext syswarningtext autoexec $512;
157  put ",""_DEBUG"" : ""&_debug"" ";
158  _PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
159  put ',"_PROGRAM" : ' _PROGRAM ;
160  autoexec=quote(urlencode(trim(getoption('autoexec'))));
161  put ',"AUTOEXEC" : ' autoexec;
162  put ",""MF_GETUSER"" : ""%mf_getuser()"" ";
163  put ",""SYSCC"" : ""&syscc"" ";
164  put ",""SYSENCODING"" : ""&sysencoding"" ";
165  syserrortext=cats(symget('syserrortext'));
166  if findc(syserrortext,'"\'!!'0A0D09000E0F010210111A'x) then do;
167  syserrortext='"'!!trim(
168  prxchange('s/"/\\"/',-1, /* double quote */
169  prxchange('s/\x0A/\n/',-1, /* new line */
170  prxchange('s/\x0D/\r/',-1, /* carriage return */
171  prxchange('s/\x09/\\t/',-1, /* tab */
172  prxchange('s/\x00/\\u0000/',-1, /* NUL */
173  prxchange('s/\x0E/\\u000E/',-1, /* SS */
174  prxchange('s/\x0F/\\u000F/',-1, /* SF */
175  prxchange('s/\x01/\\u0001/',-1, /* SOH */
176  prxchange('s/\x02/\\u0002/',-1, /* STX */
177  prxchange('s/\x10/\\u0010/',-1, /* DLE */
178  prxchange('s/\x11/\\u0011/',-1, /* DC1 */
179  prxchange('s/\x1A/\\u001A/',-1, /* SUB */
180  prxchange('s/\\/\\\\/',-1,syserrortext)
181  )))))))))))))!!'"';
182  end;
183  else syserrortext=cats('"',syserrortext,'"');
184  put ',"SYSERRORTEXT" : ' syserrortext;
185  SYSHOSTINFOLONG=quote(trim(symget('SYSHOSTINFOLONG')));
186  put ',"SYSHOSTINFOLONG" : ' SYSHOSTINFOLONG;
187  put ",""SYSHOSTNAME"" : ""&syshostname"" ";
188  put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" ";
189  put ",""SYSPROCESSMODE"" : ""&SYSPROCESSMODE"" ";
190  SYSPROCESSNAME=quote(urlencode(cats(SYSPROCESSNAME)));
191  put ",""SYSPROCESSNAME"" : " SYSPROCESSNAME;
192  put ",""SYSJOBID"" : ""&sysjobid"" ";
193  put ",""SYSSCPL"" : ""&sysscpl"" ";
194  put ",""SYSSITE"" : ""&syssite"" ";
195  put ",""SYSTCPIPHOSTNAME"" : ""&SYSTCPIPHOSTNAME"" ";
196  put ",""SYSUSERID"" : ""&sysuserid"" ";
197  sysvlong=quote(trim(symget('sysvlong')));
198  put ',"SYSVLONG" : ' sysvlong;
199  syswarningtext=cats(symget('syswarningtext'));
200  if findc(syswarningtext,'"\'!!'0A0D09000E0F010210111A'x) then do;
201  syswarningtext='"'!!trim(
202  prxchange('s/"/\\"/',-1, /* double quote */
203  prxchange('s/\x0A/\n/',-1, /* new line */
204  prxchange('s/\x0D/\r/',-1, /* carriage return */
205  prxchange('s/\x09/\\t/',-1, /* tab */
206  prxchange('s/\x00/\\u0000/',-1, /* NUL */
207  prxchange('s/\x0E/\\u000E/',-1, /* SS */
208  prxchange('s/\x0F/\\u000F/',-1, /* SF */
209  prxchange('s/\x01/\\u0001/',-1, /* SOH */
210  prxchange('s/\x02/\\u0002/',-1, /* STX */
211  prxchange('s/\x10/\\u0010/',-1, /* DLE */
212  prxchange('s/\x11/\\u0011/',-1, /* DC1 */
213  prxchange('s/\x1A/\\u001A/',-1, /* SUB */
214  prxchange('s/\\/\\\\/',-1,syswarningtext)
215  )))))))))))))!!'"';
216  end;
217  else syswarningtext=cats('"',syswarningtext,'"');
218  put ',"SYSWARNINGTEXT" : ' syswarningtext;
219  put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
220  length memsize $32;
221  memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)";
222  memsize=quote(cats(memsize));
223  put ',"MEMSIZE" : ' memsize;
224  put "}" @;
225  run;
226 %end;
227 
228 %mend ms_webout;