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> SAS Macros </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 */
114data _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');
119run;
120%if &foldertype ne Tree %then %do;
121 %put %str(WARN)ING: Tree &tree does not exist!;
122 %return;
123%end;
124
125/**
126 * Check STP does not exist already
127 */
128%local cmtype;
129data _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');
134run;
135%if &cmtype = ClassifierMap %then %do;
136 %put %str(WARN)ING: 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 %str(WARN)ING: 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 %str(WARN)ING: 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 %str(WARN)ING: 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 "%str(WARN)ING: 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 "%str(WARN)ING: 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 "%str(WARN)ING: 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=
214 "<PromptGroup promptId='PromptGroup_%sysfunc(datetime())_&sysprocessid'"
215 !!" version='1.0'><Label><Text xml:lang='en-GB'>Parameters</Text>"
216 !!"</Label></PromptGroup>";
217 rc6 = METADATA_SETATTR(prompturi, 'GroupInfo',groupinfo);
218
219 if sum(of rc1-rc6) ne 0 then do;
220 putlog "%str(WARN)ING: Issue creating prompt.";
221 if prompturi ne . then do;
222 putlog ' Removing orphan: ' prompturi;
223 rc = METADATA_DELOBJ(prompturi);
224 put rc=;
225 end;
226 stop;
227 end;
228
229 /* create a file uri */
230 rc7=METADATA_NEWOBJ('File',fileuri,'SP Source File');
231 rc8=METADATA_SETATTR(fileuri, 'FileName',"&filename");
232 rc9=METADATA_SETATTR(fileuri, 'IsARelativeName','1');
233 rc10=METADATA_SETASSN(fileuri, 'Directories','MODIFY',directoryuri);
234 if sum(of rc7-rc10) ne 0 then do;
235 putlog "%str(WARN)ING: Issue creating file.";
236 if fileuri ne . then do;
237 putlog ' Removing orphans:' prompturi fileuri;
238 rc = METADATA_DELOBJ(prompturi);
239 rc = METADATA_DELOBJ(fileuri);
240 put (_all_)(=);
241 end;
242 stop;
243 end;
244
245 /* create a TextStore object */
246 rc11= METADATA_NEWOBJ('TextStore',texturi,'Stored Process');
247 rc12= METADATA_SETATTR(texturi, 'TextRole','StoredProcessConfiguration');
248 rc13= METADATA_SETATTR(texturi, 'TextType','XML');
249 storedtext='<?xml version="1.0" encoding="UTF-8"?><StoredProcess>'
250 !!"<ResultCapabilities Package='&package' Streaming='&streaming'/>"
251 !!"<OutputParameters/></StoredProcess>";
252 rc14= METADATA_SETATTR(texturi, 'StoredText',storedtext);
253 if sum(of rc11-rc14) ne 0 then do;
254 putlog "%str(WARN)ING: Issue creating TextStore.";
255 if texturi ne . then do;
256 putlog ' Removing orphans: ' prompturi fileuri texturi;
257 rc = METADATA_DELOBJ(prompturi);
258 rc = METADATA_DELOBJ(fileuri);
259 rc = METADATA_DELOBJ(texturi);
260 put (_all_)(=);
261 end;
262 stop;
263 end;
264
265 /* create meta obj */
266 rc15= METADATA_NEWOBJ('ClassifierMap',stpuri,"&stpname");
267 rc16= METADATA_SETASSN(stpuri, 'Trees','MODIFY',treeuri);
268 rc17= METADATA_SETASSN(stpuri, 'ComputeLocations','MODIFY',serveruri);
269 rc18= METADATA_SETASSN(stpuri, 'SourceCode','MODIFY',fileuri);
270 rc19= METADATA_SETASSN(stpuri, 'Prompts','MODIFY',prompturi);
271 rc20= METADATA_SETASSN(stpuri, 'Notes','MODIFY',texturi);
272 rc21= METADATA_SETATTR(stpuri, 'PublicType', 'StoredProcess');
273 rc22= METADATA_SETATTR(stpuri, 'TransformRole', 'StoredProcess');
274 rc23= METADATA_SETATTR(stpuri, 'UsageVersion', '1000000');
275 rc24= METADATA_SETATTR(stpuri, 'Desc', "&stpdesc");
276
277 /* tidy up if err */
278 if sum(of rc15-rc24) ne 0 then do;
279 putlog "%str(WARN)ING: Issue creating STP.";
280 if stpuri ne . then do;
281 putlog ' Removing orphans: ' prompturi fileuri texturi stpuri;
282 rc = METADATA_DELOBJ(prompturi);
283 rc = METADATA_DELOBJ(fileuri);
284 rc = METADATA_DELOBJ(texturi);
285 rc = METADATA_DELOBJ(stpuri);
286 put (_all_)(=);
287 end;
288 end;
289 else do;
290 fullpath=cats('_program=',treepath,"/&stpname");
291 putlog "NOTE: Stored Process Created!";
292 putlog "NOTE- "; putlog "NOTE-"; putlog "NOTE-" fullpath;
293 putlog "NOTE- "; putlog "NOTE-";
294 end;
295 output;
296 stop;
297 run;
298%end;
299%else %if &stptype=2 %then %do;
300 /* type 2 stp - code is stored in metadata */
301 %if %sysevalf(&sysver lt 9.3) %then %do;
302 %put %str(WARN)ING: SAS version 9.3 or later required to create type2 STPs;
303 %return;
304 %end;
305 /* check we have the correct ServerContext */
306 %mm_getservercontexts(outds=contexts)
307 %local serveruri; %let serveruri=NOTFOUND;
308 data _null_;
309 set contexts;
310 where upcase(servername)="%upcase(&server)";
311 call symputx('serveruri',serveruri);
312 run;
313 %if &serveruri=NOTFOUND %then %do;
314 %put %str(WARN)ING: ServerContext *&server* not found!;
315 %return;
316 %end;
317
318 /**
319 * First, create a Hello World type 2 stored process
320 */
321 filename &frefin temp;
322 data _null_;
323 file &frefin;
324 treeuri=quote(symget('treeuri'));
325 serveruri=quote(symget('serveruri'));
326 stpdesc=quote(symget('stpdesc'));
327 stpname=quote(symget('stpname'));
328
329 put "<AddMetadata><Reposid>$METAREPOSITORY</Reposid><Metadata> "/
330 '<ClassifierMap UsageVersion="2000000" IsHidden="0" IsUserDefined="0" '/
331 ' IsActive="1" PublicType="StoredProcess" TransformRole="StoredProcess" '/
332 ' Name=' stpname ' Desc=' stpdesc '>'/
333 " <ComputeLocations>"/
334 " <ServerContext ObjRef=" serveruri "/>"/
335 " </ComputeLocations>"/
336 "<Notes> "/
337 ' <TextStore IsHidden="0" Name="SourceCode" UsageVersion="0" '/
338 ' TextRole="StoredProcessSourceCode" StoredText="%put hello world!;" />'/
339 ' <TextStore IsHidden="0" Name="Stored Process" UsageVersion="0" '/
340 ' TextRole="StoredProcessConfiguration" TextType="XML" '/
341 ' StoredText="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&qu'@@
342 'ot;?&gt;&lt;StoredProcess&gt;&lt;ServerContext LogicalServerType=&quot;S'@@
343 'ps&quot; OtherAllowed=&quot;false&quot;/&gt;&lt;ResultCapabilities Packa'@@
344 'ge=&quot;' @@ "&package" @@ '&quot; Streaming=&quot;' @@ "&streaming" @@
345 '&quot;/&gt;&lt;OutputParameters/&gt;&lt;/StoredProcess&gt;" />' /
346 " </Notes> "/
347 " <Prompts> "/
348 ' <PromptGroup Name="Parameters" GroupType="2" IsHidden="0" '/
349 ' PublicType="Embedded:PromptGroup" UsageVersion="1000000" '/
350 ' GroupInfo="&lt;PromptGroup promptId=&quot;PromptGroup_1502797359253'@@
351 '_802080&quot; version=&quot;1.0&quot;&gt;&lt;Label&gt;&lt;Text xml:lang='@@
352 '&quot;en-US&quot;&gt;Parameters&lt;/Text&gt;&lt;/Label&gt;&lt;/PromptGro'@@
353 'up&gt;" />'/
354 " </Prompts> "/
355 "<Trees><Tree ObjRef=" treeuri "/></Trees>"/
356 "</ClassifierMap></Metadata><NS>SAS</NS>"/
357 "<Flags>268435456</Flags></AddMetadata>";
358 run;
359
360 filename &frefout temp;
361
362 proc metadata in= &frefin out=&frefout ;
363 run;
364
365 %if &mdebug=1 %then %do;
366 /* write the response to the log for debugging */
367 data _null_;
368 infile &frefout lrecl=1048576;
369 input;
370 put _infile_;
371 run;
372 %end;
373
374 /**
375 * Next, add the source code
376 */
377 %mm_updatestpsourcecode(stp=&tree/&stpname
378 ,stpcode="&directory/&filename"
379 ,mdebug=&mdebug
380 ,minify=&minify)
381
382
383%end;
384%else %do;
385 %put %str(WARN)ING: STPTYPE=*&stptype* not recognised!;
386%end;
387
388%mend mm_createstp;