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