How can I get the SAS Metadata groups of the current user? - sas

I wish to set the value of a macro variable (in the autoexec file of a workspace server) based on the current user's metadata group membership.
Users can be in more than one group, and I need to be able to write logic that decides the variable's value based on the "highest" group of which the user is a member (for some definition of highest).
I have looked at generic methods for querying metadata from SAS code, but they seem to suggest the executing user should have an administrative role, and my users won't.

Users do not need to be admins in order to query metadata. They would need to have read access to the metadata objects. I'm just a user on our server, and I can get a list of all users and their associated groups, using an adaption of http://support.sas.com/kb/30/682.html:
data users_grps (keep=name group email);
length uri name groupuri group emailuri email $256 ;
call missing(uri, name, groupuri, group, emailuri, email) ;
/* Get URI of first person */
n=1;
nobj=metadata_getnobj("omsobj:Person?#Id contains '.'",n,uri);
if nobj=0 then put 'No Persons available.';
do while (nobj > 0);
call missing(name, groupuri, group, emailuri, email) ;
/* Retrieve the current persons name. */
rc=metadata_getattr(uri, "Name", Name);
/* get the persons email address (only first one)*/
rc2=metadata_getnasn(uri,"EmailAddresses",1,emailURI) ;
rc3=metadata_getattr(emailuri, "Address", email);
/* Get the group association information for the current person. */
a=1;
rcgrp=metadata_getnasn(uri,"IdentityGroups",a,groupuri);
/* If this person does not belong to any groups, set their group */
/* variable to 'No groups' and output the name. */
if rcgrp in (-3,-4) then do;
group="No groups";
output;
end;
/* If the person belongs to any groups, loop through the list */
/* and retrieve the name of each group, outputting each on a */
/* separate record. */
else do while (rcgrp > 0);
rc4=metadata_getattr(groupuri, "Name", group);
a+1;
output;
rcgrp=metadata_getnasn(uri,"IdentityGroups",a,groupuri);
end;
/* Get URI of next person. */
n+1;
nobj=metadata_getnobj("omsobj:Person?#Id contains '.'",n,uri);
end;
run;
Would think that could be adapted to look up the groups of the current user.

This macro will give you either all groups, or the direct-group memberships of a specific user:
https://core.sasjs.io/mm__getgroups_8sas.html

Related

IF statement doesn't allow Select statement to compare the given string

Write a procedure (oracle plsql) to do any one of the following: (a) update the table course and set the fee of the input course name equal to fee of java course. (b) insert a new row for given input course and set the fee lowest of all courses available in the table. Condition is: do (a) if the input course name is already present in the table otherwise do (b) if the input course name is not in the table.
I am providing here the basic details of table:
create table course(cid number primary key, cname varchar2(100), duration number, fee number);
insert into course (CID, CNAME, DURATION, FEE)
values (101, 'java', 30, 13000);
insert into course (CID, CNAME, DURATION, FEE)
values (102, 'c', 20, 5000);
insert into course (CID, CNAME, DURATION, FEE)
values (104, 'oracle', 20, 20000);
insert into course (CID, CNAME, DURATION, FEE)
values (105, 'python', 20, 30000);
insert into course (CID, CNAME, DURATION, FEE)
values (106, 'sql', 20, 1000);
I tried the below code but i don't know how to compare the given name for each rows in the table inside IF statement. Please take a look in the code and help me.
create or replace procedure proc_CourseFeeUpdateTry(coursename in course.cname%type,
java_fee out number) is
n_fee number;
j_fee number;
begin
if course.cname = coursename then --i'm getting error here
select t.fee into j_fee from course t where t.cname = 'java';
java_fee := j_fee;
update course t set t.fee = java_fee where t.cname = coursename;
dbms_output.put_line('new course added');
else
dbms_output.put_line(sqlerrm || '-' || sqlcode);
select min(t.fee) into n_fee from course t;
java_fee := n_fee;
insert into course values (103, coursename, 40, java_fee);
end if;
commit;
end;
The error you get seems to flow from a major misconception of available table data to procedure. An IF statement has no problem allowing subsequent selects. The problem here is you referenced a table column (course.cname) without having previously select anything from the course table. Just because a table exists does not give access to the data within it, you must select before referencing column values. So before that IF at you need at least a select and since it's a procedure a Select .. into specifically.
Now a select into makes column values available if it exist but if not it throws NO_DATA_FOUND exception. We this fact to avoid that IF entirely. Further there are 2 instances where you use the structure:
select data_value into local variable;
output_variable = local_variable;
This is not necessary as you can just select directly into the output_variable.
The following contains 2 revisions to your procedure. The 1st leaving your code as is as much as possible. The 2nd revising the code to make use of all the above mentioned. I hope this helps you understand further.
The minimum necessary change requires you to select the course table prior to your IF statement and handle the no_data_found_error.
create or replace procedure proc_CourseFeeUpdateTry(coursename in course.cname%type,
java_fee out number) is
n_fee number;
j_fee number;
l_course_name course.cname%type;
begin
begin
select c.cname
into l_course_name
from course c
where c.cname = coursename;
exception
when no_data_found then
null;
end ;
if l_course_name = coursename then
select t.fee into j_fee from course t where t.cname = 'java';
java_fee := j_fee;
update course t set t.fee = java_fee where t.cname = coursename;
dbms_output.put_line('course fee updated'); --- course was not added just updted
else
dbms_output.put_line(sqlerrm || '-' || sqlcode);
select min(t.fee) into n_fee from course t;
java_fee := n_fee;
insert into course values (103, coursename, 40, java_fee); --- leave message
dbms_output.put_line('new course added');
end if;
commit;
end;
The second version uses the above mentions topics and restructures.
create or replace procedure proc_CourseFeeUpdateTry(coursename in course.cname%type,
java_fee out number) is
l_cid course.cid%type;
begin
select c.cid
into l_cid
from course c
where c.cname = coursename;
begin -- inner block to trap java not found
-- if we get here then we know that the implied test cource.cname = coursename is true
select t.fee into java_fee
from course t where t.cname = 'java';
update course t set t.fee = java_fee where cid = l_cid;
dbms_output.put_line( coursename || ' fee updated');
commit;
exception
when no_data_found then
raise_application_error( -20109, 'course name ''java'' is not in course table');
end ; -- inner block
exception
when no_data_found then
-- if we get here the we know that the course name does not exist
select min(t.fee) into java_fee from course t;
insert into course values (103, coursename, 40, java_fee);
commit;
end;
Notice the insert in both procedures. It hard codes id. As a result your procedure is able to add a row exactly 1 time. This is an extremely poor process. Either pass the new id as a parameter (still not good, but better), or redefine the table to auto generate the key. Depending on your version of Oracle look up sequences and insert triggers (prior to 12c) or Identity Columns (12c and later).

Assign a value from Gen Business Posting Group Table

I have got Gen Bus. Posting Group field showing on the subform of Sales Order. This Subform is also showing Location Code for the Line.
I am attempting to write a logic as such that if the User selects Location Code as DROPSHIP, the Gen Bus. Posting Group should be LOCAL-DROPSHIP.
LOCAL-DROPSHIP is a record in the Gen. Bus. Posting Group. I have created it before writing this code. However, it would not assign to my rec.Gen Bus. Posting Group.
Here is the code on the Subform;
modify("Location Code")
{
trigger OnAfterValidate()
var
recLocation: Record Location;
recCustomer: Record Customer;
recSalesLine: Record "Sales Line";
recGenPosting: Record "Gen. Business Posting Group";
begin
recGenPosting.Get('LOCAL-DS');
if rec."Location Code" = 'DROPSHIP' then begin
Message(recGenPosting.Code);
// Validate("Gen. Bus. Posting Group", recGenPosting.Code);
Rec."Gen. Bus. Posting Group" := recGenPosting.Code;
CurrPage.Update();
end;
end;
}
You should do this as Table Extension of Table Sales Line instead of Page Extension. Validate is important in this case.
Extended Sales Line table, on the OnModify trigger, wrote;
if rec."Location Code" = 'DROPSHIP' then begin
If recCustomer.Get(REC."Sell-to Customer No.") THEN begin
if recCustomer."Country/Region Code" = 'CITY' then begin
rec."Gen. Bus. Posting Group" := 'DS';
rec.Modify();
end
end

adding a meta user to a meta group in sas

I've around 600 meta users in SAS EGRC 6.1 in the platform in SAS 9.4.
I want to add those users to a meta-group. for this, I'm using code below
libname current '/tmp/temp1'; /* for the current metadata */
libname addgrps '/tmp/temp2'; /* current augmented with the changes */
libname updates '/tmp/temp3'; /* for the updates created by the mducmp macro */
options metaserver=abc
metaport=8561
metauser='sasadm#saspw'
metapass='xyz123'
metaprotocol=bridge
metarepository=foundation;
%mduextr(libref=current);
proc copy in = current out = addgrps;
/*copy the current data to the directory for the target */
run;
data investigators_1;
set current.person;
where name in ('5806036');
rename objid = memkeyid;
keep objid;
run;
data investigator_group_1;
set current.group_info;
where name='Enterprise GRC: Incident Investigation';
rename id = grpkeyid;
keep id;
run;
proc sql;
create table grpmems as
select * from investigators_1, investigator_group_1;
quit;
proc append base = addgrps.grpmems data = grpmems;
run;
/* use the mducmp macro to create the updates data */
%mducmp(master=addgrps, target=current, change=updates)
/* validate the change data sets */
%mduchgv(change=updates, target=current, temp=work, errorsds=work.mduchgverrors)
/* apply the updates */
%mduchgl(change=updates);
for the final updated I tried both %mduchgl and %mduchglb but with both, I'm not able to get the desired results. I test it with one user.
with %mduchgl I get the below error
The symbolic reference for A52PDIUF.$A52PDIUF.AP0000NI did not resolve.
with %mduchglb I get the below error
The object reference to Person was requested without an identifier.
Errors returned from Proc Metadata prevented objects from being Added, Updated, or Deleted. Table: work.mduchglb_failedobjs
identifies 1 such objects. Consult the SAS Log for the specific Metadata Server errors returned.
Any suggestions that how can I resolve the error or another approach that I should try to achieve this.
Thanks.
I don't think you should ever modify those datasets! Everything you need to achieve should be possible using proc metadata (or data step functions as last resort).
Here is a relevant SAS Communities thread. To summarise - the following snippet will add a group to a user, so long as you have the group / user URI:
<Person Id="A5NUQPXO.AP00002V">
<IdentityGroups>
<IdentityGroup ObjRef="A5NUQPXO.A500001C" />
</IdentityGroups>
</Person>
UPDATE - for completeness, I turned this into a macro, described here
https://core.sasjs.io/mm__adduser2group_8sas.html

Looking to see if a particular character exists in a string SAS

I am working with customer data, part of which looks at customers email addresses. Unfortunately there are next to none controls on fields where customer data is input in the system and therefore requires scrubbing.
Using the current email field, I want to create a new field populated with the customer's email address based on the condition "if # exists" and then if it doesn't exist, I will populate the email address with a blank value.
For example:
Current Email Address New Email Address
customer1#business1.com customer1#business1.com
customer2#business2.com customer2#business2.com
customer3business3.com
Can anyone help - I have scoured the internet and cannot find anything that would do this!!
Thanks
You'd probably want more controls than this to validate an email address, but here you go:
data have;
infile cards;
input cur_email:$50.;
cards4;
customer1#business1.com
customer2#business2.com
customer3business3.com
;;;;
run;
data want;
set have;
if index(cur_email,"#") then new_email=cur_email;
run;
If you want to search for a string within the email address like 'gmail' then you can use this:
if COMPRESS(TRANWRD(cur_email,'gmail','~'),'~','k')='~' then new_email=cur_email;
or to be in keeping with the first answer:
if INDEX(TRANWRD(cur_email,'gmail','~'),'~') then new_email=cur_email;

Kettle database lookup case insensitive

I've a table "City" with more than 100k records.
The field "name" contains strings like "Roma", "La Valletta".
I receive a file with the city name, all in upper case as in "ROMA".
I need to get the id of the record that contains "Roma" when I search for "ROMA".
In SQL, I must do something like:
select id from city where upper(name) = upper(%name%)
How can I do this in kettle?
Note: if the city is not found, I use an Insert/update field to create it, so I must avoid duplicates generated by case-sensitive names.
You can make use of the String Operations steps in Pentaho Kettle. Set the Lower/Upper option to Y
Pass the city (name) from the City table to the String operations steps which will do the Upper case of your data stream i.e. city name. Join/lookup with the received file and get the required id.
More on String Operations step in pentaho wiki.
You can use a 'Database join' step. Here you can write the sql:
select id from city where upper(name) = upper(?)
and specify the city field name from the text file as parameter. With 'Number of rows to return' and 'Outer join?' you can control the join behaviour.
This solution doesn't work well with a large number of rows, as it will execute one query per row. In those cases Rishu's solution is better.
This is how I did:
First "Modified JavaScript value" step for create a query:
var queryDest="select coalesce( (select id as idcity from city where upper(name) = upper('"+replace(mycity,"'","\'\'")+"') and upper(cap) = upper('"+mycap+"') ), 0) as idcitydest";
Then I use this string as a query in a Dynamic SQL row.
After that,
IF idcitydest == 0 then
insert new city;
else
use the found record
This system make a query for file's row but it use few memory cache