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