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