Macros for SAS Application Developers
https://github.com/sasjs/core
mm_createstp.sas
Go to the documentation of this file.
1 /**
2  @file
3  @brief Create a type 1 Stored Process (9.2 compatible)
4  @details This macro creates a Type 1 stored process, and also the necessary
5  PromptGroup / File / TextStore objects. It requires the location (or uri)
6  for the App Server / Directory / Folder (Tree) objects.
7  To upgrade this macro to work with type 2 (which can embed SAS code
8  and is compabitible with SAS from 9.3 onwards) then the UsageVersion should
9  change to 2000000 and the TextStore object updated. The ComputeServer
10  reference will also be to ServerContext rather than LogicalServer.
11 
12  This macro is idempotent - if you run it twice, it will only create an STP
13  once.
14 
15  Usage (type 1 STP):
16 
17  %mm_createstp(stpname=MyNewSTP
18  ,filename=mySpecialProgram.sas
19  ,directory=SASEnvironment/SASCode/STPs
20  ,tree=/User Folders/sasdemo
21  ,outds=work.uris)
22 
23  If you wish to remove the new STP you can do so by running:
24 
25  data _null_;
26  set work.uris;
27  rc1 = METADATA_DELOBJ(texturi);
28  rc2 = METADATA_DELOBJ(prompturi);
29  rc3 = METADATA_DELOBJ(fileuri);
30  rc4 = METADATA_DELOBJ(stpuri);
31  putlog (_all_)(=);
32  run;
33 
34  Usage (type 2 STP):
35 
36  %mm_createstp(stpname=MyNewType2STP
37  ,filename=mySpecialProgram.sas
38  ,directory=SASEnvironment/SASCode/STPs
39  ,tree=/User Folders/sasdemo
40  ,Server=SASApp
41  ,stptype=2)
42 
43  @param [in] stpname= (SASjs Default STP) Stored Process name.
44  Avoid spaces - testing has shown that
45  the check to avoid creating multiple STPs in the same folder with the same
46  name does not work when the name contains spaces.
47  @param [in] stpdesc= Stored Process description (optional)
48  @param [in] filename= the name of the .sas program to run
49  @param [in] directory= (SASEnvironment/sascode)
50  The directory uri or the actual path to the sas program (no trailing slash).
51  If more than uri is found with that path, then the first one will be used.
52  @param [in] tree= The metadata folder uri, or the metadata path, in which to
53  create the STP.
54  @param [in] server= (SASApp) The server which will run the STP.
55  Server name or uri is fine.
56  @param [out] outds= (work.mm_createstp)
57  The two level name of the output dataset. Will contain all the meta uris.
58  @param [in] mDebug= set to 1 to show debug messages in the log
59  @param [in] stptype= Default is 1 (STP code saved on filesystem). Set to 2 if
60  source code is to be saved in metadata (9.3 and above feature).
61  @param [in] minify= set to YES to strip comments / blank lines etc
62  @param [in] frefin= (mm_in) fileref to use (enables change if there is
63  a conflict).
64  The filerefs are left open, to enable inspection after running the
65  macro (or importing into an xmlmap if needed).
66  @param [out] frefout= (mm_out) fileref to use (enables change if there is
67  a conflict)
68  @param [in] repo= ServerContext is tied to a repo, if you are not using the
69  foundation repo then select a different one here
70  @param [in] LogicalServerType= (Sps) Server Type to use. Valid options:
71  @li Any - Uses the default server.
72  @li Sps - Stored Process Server, best choice for web app development. Runs
73  under a system account identity (eg sassrv).
74  @li Wks - Workspace Server. Runs under the end user identity.
75 
76  @returns outds dataset containing the following columns:
77  - stpuri
78  - prompturi
79  - fileuri
80  - texturi
81 
82  <h4> SAS Macros </h4>
83  @li mf_nobs.sas
84  @li mf_verifymacvars.sas
85  @li mm_getdirectories.sas
86  @li mm_updatestpsourcecode.sas
87  @li mm_getservercontexts.sas
88  @li mp_abort.sas
89  @li mp_dropmembers.sas
90 
91  <h4> Related Macros </h4>
92  @li mm_createwebservice.sas
93 
94  @version 9.2
95  @author Allan Bowe
96 
97 **/
98 
99 %macro mm_createstp(
100  stpname=SASjs Default STP
101  ,stpdesc=This stp was created automatically by the mm_createstp macro
102  ,filename=mm_createstp.sas
103  ,directory=SASEnvironment/SASCode
104  ,tree=/User Folders/sasdemo
105  ,package=false
106  ,streaming=true
107  ,outds=work.mm_createstp
108  ,mDebug=0
109  ,server=SASApp
110  ,stptype=1
111  ,minify=NO
112  ,frefin=mm_in
113  ,frefout=mm_out
114  ,LogicalServerType=Sps
115 )/*/STORE SOURCE*/;
116 
117 %local mD;
118 %if &mDebug=1 %then %let mD=;
119 %else %let mD=%str(*);
120 %&mD.put Executing mm_CreateSTP.sas;
121 %&mD.put _local_;
122 
123 %mp_abort(
124  iftrue=(%mf_verifymacvars(stpname filename directory tree)=0)
125  ,mac=&sysmacroname
126  ,msg=%str(Empty inputs: stpname filename directory tree)
127 )
128 
129 %mp_dropmembers(%scan(&outds,2,.))
130 
131 /* check LogicalServerType validity */
132 %mp_abort(
133  iftrue=(
134  &LogicalServerType ne Sps
135  and &LogicalServerType ne Wks
136  and &LogicalServerType ne Any
137  )
138  ,mac=&sysmacroname
139  ,msg=%str(Invalid value for LogicalServerType (&LogicalServerType))
140 )
141 
142 /**
143  * check tree exists
144  */
145 data _null_;
146  length type uri $256;
147  rc=metadata_pathobj("","&tree","Folder",type,uri);
148  call symputx('foldertype',type,'l');
149  call symputx('treeuri',uri,'l');
150 run;
151 %if &foldertype ne Tree %then %do;
152  %put %str(WARN)ING: Tree &tree does not exist!;
153  %return;
154 %end;
155 
156 /**
157  * Check STP does not exist already
158  */
159 %local cmtype;
160 data _null_;
161  length type uri $256;
162  rc=metadata_pathobj("","&tree/&stpname",'StoredProcess',type,uri);
163  call symputx('cmtype',type,'l');
164  call symputx('stpuri',uri,'l');
165 run;
166 %if &cmtype = ClassifierMap %then %do;
167  %put %str(WARN)ING: Stored Process &stpname already exists in &tree!;
168  %return;
169 %end;
170 
171 /**
172  * Check that the physical file exists
173  */
174 %if %sysfunc(fileexist(&directory/&filename)) ne 1 %then %do;
175  %put %str(WARN)ING: FILE *&directory/&filename* NOT FOUND!;
176  %return;
177 %end;
178 
179 %if &stptype=1 %then %do;
180  /* type 1 STP - where code is stored on filesystem */
181  %if %sysevalf(&sysver lt 9.2) %then %do;
182  %put %str(WARN)ING: Version 9.2 or later required;
183  %return;
184  %end;
185 
186  /* check directory object (where 9.2 source code reference is stored) */
187  data _null_;
188  length id $20 dirtype $256;
189  rc=metadata_resolve("&directory",dirtype,id);
190  call symputx('checkdirtype',dirtype,'l');
191  run;
192 
193  %if &checkdirtype ne Directory %then %do;
194  %mm_getdirectories(path=&directory,outds=&outds ,mDebug=&mDebug)
195  %if %mf_nobs(&outds)=0 or %sysfunc(exist(&outds))=0 %then %do;
196  %put %str(WARN)ING: The directory object does not exist for &directory;
197  %return;
198  %end;
199  %end;
200  %else %do;
201  data &outds;
202  directoryuri="&directory";
203  run;
204  %end;
205 
206  data &outds (keep=stpuri prompturi fileuri texturi);
207  length stpuri prompturi fileuri texturi serveruri $256 ;
208  if _n_=1 then call missing (of _all_);
209  set &outds;
210 
211  /* final checks on uris */
212  length id $20 type $256;
213  __rc=metadata_resolve("&treeuri",type,id);
214  if type ne 'Tree' then do;
215  putlog "%str(WARN)ING: Invalid tree URI: &treeuri";
216  stopme=1;
217  end;
218  __rc=metadata_resolve(directoryuri,type,id);
219  if type ne 'Directory' then do;
220  putlog "%str(WARN)ING: Invalid directory URI: " directoryuri;
221  stopme=1;
222  end;
223 
224  /* get server info */
225  __rc=metadata_resolve("&server",type,serveruri);
226  if type ne 'LogicalServer' then do;
227  __rc=metadata_getnobj("omsobj:LogicalServer?@Name='&server'",1,serveruri);
228  if serveruri='' then do;
229  putlog "%str(WARN)ING: Invalid server: &server";
230  stopme=1;
231  end;
232  end;
233 
234  if stopme=1 then do;
235  putlog (_all_)(=);
236  stop;
237  end;
238 
239  /* create empty prompt */
240  rc1=METADATA_NEWOBJ('PromptGroup',prompturi,'Parameters');
241  rc2=METADATA_SETATTR(prompturi, 'UsageVersion', '1000000');
242  rc3=METADATA_SETATTR(prompturi, 'GroupType','2');
243  rc4=METADATA_SETATTR(prompturi, 'Name','Parameters');
244  rc5=METADATA_SETATTR(prompturi, 'PublicType','Embedded:PromptGroup');
245  GroupInfo=
246  "<PromptGroup promptId='PromptGroup_%sysfunc(datetime())_&sysprocessid'"
247  !!" version='1.0'><Label><Text xml:lang='en-GB'>Parameters</Text>"
248  !!"</Label></PromptGroup>";
249  rc6 = METADATA_SETATTR(prompturi, 'GroupInfo',groupinfo);
250 
251  if sum(of rc1-rc6) ne 0 then do;
252  putlog "%str(WARN)ING: Issue creating prompt.";
253  if prompturi ne . then do;
254  putlog ' Removing orphan: ' prompturi;
255  rc = METADATA_DELOBJ(prompturi);
256  put rc=;
257  end;
258  stop;
259  end;
260 
261  /* create a file uri */
262  rc7=METADATA_NEWOBJ('File',fileuri,'SP Source File');
263  rc8=METADATA_SETATTR(fileuri, 'FileName',"&filename");
264  rc9=METADATA_SETATTR(fileuri, 'IsARelativeName','1');
265  rc10=METADATA_SETASSN(fileuri, 'Directories','MODIFY',directoryuri);
266  if sum(of rc7-rc10) ne 0 then do;
267  putlog "%str(WARN)ING: Issue creating file.";
268  if fileuri ne . then do;
269  putlog ' Removing orphans:' prompturi fileuri;
270  rc = METADATA_DELOBJ(prompturi);
271  rc = METADATA_DELOBJ(fileuri);
272  put (_all_)(=);
273  end;
274  stop;
275  end;
276 
277  /* create a TextStore object */
278  rc11= METADATA_NEWOBJ('TextStore',texturi,'Stored Process');
279  rc12= METADATA_SETATTR(texturi, 'TextRole','StoredProcessConfiguration');
280  rc13= METADATA_SETATTR(texturi, 'TextType','XML');
281  storedtext='<?xml version="1.0" encoding="UTF-8"?><StoredProcess>'
282  !!"<ResultCapabilities Package='&package' Streaming='&streaming'/>"
283  !!"<OutputParameters/></StoredProcess>";
284  rc14= METADATA_SETATTR(texturi, 'StoredText',storedtext);
285  if sum(of rc11-rc14) ne 0 then do;
286  putlog "%str(WARN)ING: Issue creating TextStore.";
287  if texturi ne . then do;
288  putlog ' Removing orphans: ' prompturi fileuri texturi;
289  rc = METADATA_DELOBJ(prompturi);
290  rc = METADATA_DELOBJ(fileuri);
291  rc = METADATA_DELOBJ(texturi);
292  put (_all_)(=);
293  end;
294  stop;
295  end;
296 
297  /* create meta obj */
298  rc15= METADATA_NEWOBJ('ClassifierMap',stpuri,"&stpname");
299  rc16= METADATA_SETASSN(stpuri, 'Trees','MODIFY',treeuri);
300  rc17= METADATA_SETASSN(stpuri, 'ComputeLocations','MODIFY',serveruri);
301  rc18= METADATA_SETASSN(stpuri, 'SourceCode','MODIFY',fileuri);
302  rc19= METADATA_SETASSN(stpuri, 'Prompts','MODIFY',prompturi);
303  rc20= METADATA_SETASSN(stpuri, 'Notes','MODIFY',texturi);
304  rc21= METADATA_SETATTR(stpuri, 'PublicType', 'StoredProcess');
305  rc22= METADATA_SETATTR(stpuri, 'TransformRole', 'StoredProcess');
306  rc23= METADATA_SETATTR(stpuri, 'UsageVersion', '1000000');
307  rc24= METADATA_SETATTR(stpuri, 'Desc', "&stpdesc");
308 
309  /* tidy up if err */
310  if sum(of rc15-rc24) ne 0 then do;
311  putlog "%str(WARN)ING: Issue creating STP.";
312  if stpuri ne . then do;
313  putlog ' Removing orphans: ' prompturi fileuri texturi stpuri;
314  rc = METADATA_DELOBJ(prompturi);
315  rc = METADATA_DELOBJ(fileuri);
316  rc = METADATA_DELOBJ(texturi);
317  rc = METADATA_DELOBJ(stpuri);
318  put (_all_)(=);
319  end;
320  end;
321  else do;
322  fullpath=cats('_program=',treepath,"/&stpname");
323  putlog "NOTE: Stored Process Created!";
324  putlog "NOTE- "; putlog "NOTE-"; putlog "NOTE-" fullpath;
325  putlog "NOTE- "; putlog "NOTE-";
326  end;
327  output;
328  stop;
329  run;
330 %end;
331 %else %if &stptype=2 %then %do;
332  /* type 2 stp - code is stored in metadata */
333  %if %sysevalf(&sysver lt 9.3) %then %do;
334  %put %str(WARN)ING: SAS version 9.3 or later required to create type2 STPs;
335  %return;
336  %end;
337  /* check we have the correct ServerContext */
338  %mm_getservercontexts(outds=contexts)
339  %local serveruri; %let serveruri=NOTFOUND;
340  data _null_;
341  set contexts;
342  where upcase(servername)="%upcase(&server)";
343  call symputx('serveruri',serveruri);
344  run;
345  %if &serveruri=NOTFOUND %then %do;
346  %put %str(WARN)ING: ServerContext *&server* not found!;
347  %return;
348  %end;
349 
350  /**
351  * First, create a Hello World type 2 stored process
352  */
353  filename &frefin temp;
354  data _null_;
355  file &frefin;
356  treeuri=quote(symget('treeuri'));
357  serveruri=quote(symget('serveruri'));
358  stpdesc=quote(symget('stpdesc'));
359  stpname=quote(symget('stpname'));
360 
361  put "<AddMetadata><Reposid>$METAREPOSITORY</Reposid><Metadata> "/
362  '<ClassifierMap UsageVersion="2000000" IsHidden="0" IsUserDefined="0" '/
363  ' IsActive="1" PublicType="StoredProcess" TransformRole="StoredProcess" '/
364  ' Name=' stpname ' Desc=' stpdesc '>'/
365  " <ComputeLocations>"/
366  " <ServerContext ObjRef=" serveruri "/>"/
367  " </ComputeLocations>"/
368  "<Notes> "/
369  ' <TextStore IsHidden="0" Name="SourceCode" UsageVersion="0" '/
370  ' TextRole="StoredProcessSourceCode" StoredText="%put hello world!;" />'/
371  ' <TextStore IsHidden="0" Name="Stored Process" UsageVersion="0" '/
372  ' TextRole="StoredProcessConfiguration" TextType="XML" '/
373  ' StoredText="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&qu'@@
374  'ot;?&gt;&lt;StoredProcess&gt;&lt;ServerContext LogicalServerType=&quot;'@@
375  "&LogicalServerType"@@
376  '&quot; OtherAllowed=&quot;false&quot;/&gt;&lt;ResultCapabilities Packa'@@
377  'ge=&quot;' @@ "&package" @@ '&quot; Streaming=&quot;' @@ "&streaming" @@
378  '&quot;/&gt;&lt;OutputParameters/&gt;&lt;/StoredProcess&gt;" />' /
379  " </Notes> "/
380  " <Prompts> "/
381  ' <PromptGroup Name="Parameters" GroupType="2" IsHidden="0" '/
382  ' PublicType="Embedded:PromptGroup" UsageVersion="1000000" '/
383  ' GroupInfo="&lt;PromptGroup promptId=&quot;PromptGroup_1502797359253'@@
384  '_802080&quot; version=&quot;1.0&quot;&gt;&lt;Label&gt;&lt;Text xml:lang='@@
385  '&quot;en-US&quot;&gt;Parameters&lt;/Text&gt;&lt;/Label&gt;&lt;/PromptGro'@@
386  'up&gt;" />'/
387  " </Prompts> "/
388  "<Trees><Tree ObjRef=" treeuri "/></Trees>"/
389  "</ClassifierMap></Metadata><NS>SAS</NS>"/
390  "<Flags>268435456</Flags></AddMetadata>";
391  run;
392 
393  filename &frefout temp;
394 
395  proc metadata in= &frefin out=&frefout ;
396  run;
397 
398  %if &mdebug=1 %then %do;
399  /* write the response to the log for debugging */
400  data _null_;
401  infile &frefout lrecl=1048576;
402  input;
403  put _infile_;
404  run;
405  %end;
406 
407  /**
408  * Next, add the source code
409  */
410  %mm_updatestpsourcecode(stp=&tree/&stpname
411  ,stpcode="&directory/&filename"
412  ,mdebug=&mdebug
413  ,minify=&minify)
414 
415 
416 %end;
417 %else %do;
418  %put %str(WARN)ING: STPTYPE=*&stptype* not recognised!;
419 %end;
420 
421 %mend mm_createstp;