WSO2 scim api /Groups/roleId takes too much time to update - wso2

I am using WSO2 IS 5.10 version. As per requirement we can add and remove new roles form our application and call the following API to update groups with users. I am doing thi sas bulk operation as some time it requires to update as bulk. Even for 1 role update it takes 6 seconds where no of users are 30-40 k and in production where users are 90-95 k takes more than 12 seconds.
Is there any way to update in less time. Am I not following the correct way. Please suggest.
foreach (var role in roles)
{
var groupBulkOperation = new WSO2GroupBulkSCIMResourceOperationSchema();
groupBulkOperation.BulkId = Guid.NewGuid().ToString();
groupBulkOperation.Method = "PATCH";
groupBulkOperation.Path = "/Groups/" + role.value;
groupBulkOperation.Data = new WSO2GroupBulkSCIMResourceDataSchema
{
Operations = new List<WSO2GroupOperationSchema>
{
new WSO2GroupOperationSchema
{
op = "add",
path = "members",
value = new List<Members>
{
new Members
{
display = userName,
value = userId
}
}
}
}
};
requestModel.Operations.Add(groupBulkOperation);
}
var response = await CommonServiceResponceModel(
KeyObj.WSO2BaseURL + "scim2/Bulk",
Method.POST,
SerializeObject(requestModel)

Related

How to handle AWS IAM token to connect with database in ASP.NET Core Web API

I have MySQL database hosted in AWS and I am using IAM token to connect with it. I am using the following library and code to connect to this database and get tokens.
var token = Amazon.RDS.Util.RDSAuthTokenGenerator.GenerateAuthToken(awsOptions.Credentials, RegionEndpoint.USEast1, creds.Host, (int)creds.Port, creds.UserName);
The token I am getting back has an expiration of 15 minutes but it looks like a sliding expiration, so we keep using a database connection and it keeps adding 15 minutes from the last used token.
Initially, I was calling the library method to get tokens for each database request and it was very slow. It also started giving an error.
I implemented cache for 5 minutes and sliding token expiration is 15 minutes to be safe. It works for 3-4 hours and then looks like the token gets expired even if I continue to use it every 5 seconds. In this case, my application goes down for a maximum of 5 minutes every 3-4 hours.
Sample code for caching
public IDbConnection GetDbConnection()
{
var connectionString = GetConnectionString("connectionName");
var connection = new MySqlConnection(connectionString);
try
{
connection.Open();
}
catch (MySqlException ex)
{
// if exception remove cache and get new token
if (ex.Number == 1045)
{
connectionString = GetConnectionString("connectionName", true);
return new MySqlConnection(connectionString);
}
throw;
}
connection.Close();
return connection;
}
public string GetConnectionString(string connectionName, bool refreshCache = false)
{
if (refreshCache == true)
{
var connectionString = GetConnectionStringInternal(connectionName);
if (this.cache.Get("cacheName") != null)
{
this.cache.Remove("cacheName");
}
return connectionString;
}
var cacheEntry = this.cache.GetOrCreate("cacheName",
entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5);
return GetConnectionStringInternal(connectionName);
});
return cacheEntry;
}
private string GetConnectionStringInternal(string connectionName)
{
var token = Amazon.RDS.Util.RDSAuthTokenGenerator.GenerateAuthToken(awsOptions.Credentials, RegionEndpoint.USEast1, creds.Host, (int)creds.Port, creds.UserName);
return $"{connectionString};Uid={creds.UserName};Server={creds.Host}; Port={creds.Port};Password={token};";
}
What is a way to cache AWS RDS token?
Thank you for your help!

Has anyone got a guide on how to upgrade from PowerBi Embeded v2 to v3? Or a tutorial for v3?

This appears to be a nightmare, sure its easy to upgrade the nuget package to 3.11 I think the latest is, but then nothing at all compiles. So you fix the compile errors, and then it doesn't work. I'm getting an error when it tries to create the PowerBI client.
Getting the token and also creating the client appears to be totally different to v2.
This is my code:
public PowerBiConfig GetPowerBiConfig(string reportId)
{
var result = new PowerBiConfig();
try
{
if (!Guid.TryParse(reportId, out var _))
{
result.ErrorMessage = $"Invalid report guid: {reportId}";
return result;
}
var credential = new UserPasswordCredential(_powerBiProMasterUsername, _powerBiProMasterPassword);
var authenticationContext = new AuthenticationContext(AuthorityUrl);
// Taken from https://stackoverflow.com/questions/5095183/how-would-i-run-an-async-taskt-method-synchronously
var authenticationResult = authenticationContext.AcquireTokenAsync(ResourceUrl, dataArchiverSettings.PowerBiApplicationId, credential).GetAwaiter().GetResult();
if (authenticationResult == null)
{
result.ErrorMessage = "Authentication Failed.";
return result;
}
var tokenCredentials = new TokenCredentials(authenticationResult.AccessToken, "Bearer");
using (var client = new PowerBIClient(new Uri(ApiUrl), tokenCredentials))
{
var report = client.Reports.GetReportInGroup(dataArchiverSettings.PowerBiWorkspaceId, reportId);
if (report == null)
{
result.ErrorMessage = $"No report with the ID {reportId} was found in the workspace.";
return result;
}
var datasets = client.Datasets.GetDatasetById(dataArchiverSettings.PowerBiWorkspaceId, report.DatasetId);
result.IsEffectiveIdentityRequired = datasets.IsEffectiveIdentityRequired;
result.IsEffectiveIdentityRolesRequired = datasets.IsEffectiveIdentityRolesRequired;
GenerateTokenRequest tokenRequest;
if (datasets.IsEffectiveIdentityRequired == true)
{
var username = UserHelper.GetCurrentUser();
var roles = _userService.GetRolesForUser(username);
tokenRequest = new GenerateTokenRequest(accessLevel: "view",
identities: new List<EffectiveIdentity>
{
new EffectiveIdentity(username: username,
roles: new List<string> (roles.Select(x=> x.RoleName)),
datasets: new List<string> {datasets.Id})
});
}
else
{
tokenRequest = new GenerateTokenRequest(accessLevel: "view");
}
var tokenResponse =
client.Reports.GenerateTokenInGroup(dataArchiverSettings.PowerBiWorkspaceId, report.Id,
tokenRequest);
if (tokenResponse == null)
{
result.ErrorMessage = "Failed to generate embed token.";
return result;
}
// Generate Embed Configuration.
result.EmbedToken = tokenResponse;
result.EmbedUrl = report.EmbedUrl;
result.Id = report.Id.ToString();
result.WorkloadResourceName = dataArchiverSettings.PowerBiWorkloadResourceName.Trim();
}
}
catch (HttpOperationException exc)
{
result.ErrorMessage =
$"Status: {exc.Response.StatusCode} ({(int)exc.Response.StatusCode})\r\n" +
$"Response: {exc.Response.Content}\r\n" +
$"RequestId: {exc.Response.Headers["RequestId"].FirstOrDefault()}";
}
catch (Exception exc)
{
result.ErrorMessage = exc.ToString();
}
return result;
}
The closest to "upgrade guide" is the announcement in Power BI blog. It looks like your code is using v2 (e.g. reportId is string, while in v3 it should be Guid).
Here is a brief summary of the changes:
What you should know about v3
Here are the key changes with this version update:
Namespaces renaming:
Microsoft.PowerBI.Api.V2 was changed to Microsoft.PowerBI.Api
Microsoft.PowerBI.Api.Extensions.V2 was changed to Microsoft.PowerBI.Api.Extensions
Microsoft.PowerBI.Api.V1 namespace was removed.
SetAllConnections and SetAllConnectionsInGroup operations are deprecated and marked as obsolete. You should use UpdateDatasources or UpdateParameters APIs instead.
PowerBI artifacts IDs typing was changed* from string to Guid, we recommend to work with Guid when possible.
*Dataset ID is an exception and it’s typing will remain string.
ODataResponse[List[Object]] types was changed to Objects, thus returning an objects collection on responses. For example, a response of ODataResponse[List[Report]] type will now return Reports collection as the return type.
New credentials classes allow easier build of credentialDetails. The new classes include: BasicCredentials, WindowsCredentials, OAuth2Credentials, and more.
Read Configure credentials article to learn more.
New encryption helper classes for easier encryption when creating CredentialDetails.
For example, using AsymmetricKeyEncryptor class with a gateway public key:
GatewayPublicKey publicKey = new GatewayPublicKey
{
Exponent = "...",
Modulus = "..."
};
CredentialsBase credentials = new BasicCredentials("<USER>", "<PASSWORD>");
var credentialsEncryptor = new AsymmetricKeyEncryptor(publicKey);
var credentialDetails = new CredentialDetails(credentials, PrivacyLevel.None, EncryptedConnection.Encrypted, credentialsEncryptor);
Read Configure credentials article to learn more.
Consistency on field names.
For example, reportKey, datasetKey, dashboardKey and tileKey was changed to reportId, datasetId, dashboardId and tileId.
Consistency on operations names.
For example, use GetDataset instead of GetDatasetById. The effected opertation names are imports, datasets, gateways and datasources.
Use enum class instead of string for enumerated types.
For example, In the generateTokenRequest, we recommend to use TokenAccessLevel.View, and not explicitly use “view” as value.
Required fields was marked – some fields was changed to required fields are not nullable anymore.
Examples
Change in Get Reports call if WorkspaceId is a string:
var reports = await client.Reports.GetReportsInGroupAsync(WorkspaceId);
var reports = await client.Reports.GetReportsInGroupAsync(new Guid( WorkspaceId ) );
Change in response handling if a string is expected:
report = reports.Value.FirstOrDefault(r => r.Id.Equals(ReportId, StringComparison.InvariantCultureIgnoreCase));
report = reports.Value.FirstOrDefault(r => r.Id .ToString() .Equals(ReportId, StringComparison.InvariantCultureIgnoreCase));
Change in Generate token:
var tokenResponse = await client.Reports.GenerateTokenInGroupAsync(WorkspaceId, report.Id, generateTokenRequestParameters);
var tokenResponse = await client.Reports.GenerateTokenInGroupAsync( new Guid( WorkspaceId ), report.Id, generateTokenRequestParameters);
Change in Generate token response handling if a string is expected:
m_embedConfig.Id = report.Id;
m_embedConfig.Id = report.Id .ToString() ;
Required fields are not nullable, i.e. Expiration is not nullable and the Value property should be removed:
var minutesToExpiration = EmbedToken.Expiration .Value – DateTime.UtcNow;
var minutesToExpiration = EmbedToken.Expiration – DateTime.UtcNow;
Consistency on operations names, i.e. use GetDataset instead of GetDatasetById:
var datasets = await client.Datasets.GetDataset ById InGroupAsync(WorkspaceId, report.DatasetId);
var datasets = await client.Datasets.GetDatasetInGroupAsync(new Guid(WorkspaceId), report.DatasetId);

Google Classroom API : Unable to receive push notifications for courseworks and student submissions (COURSE_WORK_CHANGES)

We are seriously blocked. We have followed the documentation below (among many others) to set up the pub/sub pipelines, create service accounts, assign permissions and use the right scopes and feed types for registrations.
https://developers.google.com/classroom/guides/push-notifications
So programmatically we are able to do the following in .net using the API :
We can Create Courses
We can create registrations for a given courseid
We create/update courseworks for the course that we have created a registration.
All good so far,
BUT, we don't receive notifications for that created/update course work.
some code for clarity :
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer("sa-something#precise-asset-259113.iam.gserviceaccount.com")
{
User = "impersonated user",
Scopes = new string[] { "https://www.googleapis.com/auth/classroom.coursework.students" ,
"https://www.googleapis.com/auth/classroom.courses",
"https://www.googleapis.com/auth/classroom.push-notifications" }}
.FromPrivateKey("My private key"));
//Authorize request
var result = credential.RequestAccessTokenAsync(CancellationToken.None).Result;
var service = new ClassroomService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
});
// We get courses
var courses = service.Courses.List().Execute();
// We get one course for registration
var course = courses.Courses.First();
// We create registration
var registration = service.Registrations.Create(new Google.Apis.Classroom.v1.Data.Registration()
{
Feed = new Google.Apis.Classroom.v1.Data.Feed()
{
FeedType = "COURSE_WORK_CHANGES",
CourseWorkChangesInfo = new Google.Apis.Classroom.v1.Data.CourseWorkChangesInfo()
{
CourseId = course.Id
},
},
CloudPubsubTopic = new Google.Apis.Classroom.v1.Data.CloudPubsubTopic()
{
TopicName = "projects/precise-asset-259113/topics/test"
},
});
//Successful response - We get a registrationID
var response = registration.Execute();
var courseWork = new CourseWork()
{
CourseId = course.Id,
Title = "Ver Test",
Description = "Calculus",
WorkType = "ASSIGNMENT",
MaxPoints = 20.0,
State = "PUBLISHED",
AlternateLink = "www.uni.com",
CreatorUserId = course.OwnerId,
CreationTime = DateTime.UtcNow,
DueTime = new TimeOfDay() { Hours = 5, Minutes = 10, Nanos = 10, Seconds = 10 },
DueDate = new Date() { Day = 3, Month = 12, Year = 2019 },
Assignment = new Assignment() { StudentWorkFolder = new DriveFolder() { AlternateLink = "Somewhere", Title = "My Calculus" } }
};
//Create course work for the course that we registered
var courseWorkResponse = service.Courses.CourseWork.Create(courseWork, course.Id).Execute();
SubscriberServiceApiClient subscriber = SubscriberServiceApiClient.Create();
SubscriptionName subscriptionName = new SubscriptionName("precise-asset-259113", "test");
PullResponse pullResponse = subscriber.Pull(
subscriptionName, returnImmediately: true, maxMessages: 20);
// Check for push notifications BUT ....NADA!!!
foreach (ReceivedMessage msg in pullResponse.ReceivedMessages)
{
string text = Encoding.UTF8.GetString(msg.Message.Data.ToArray());
Console.WriteLine($"Message {msg.Message.MessageId}: {text}");
}
Can you please assist?
Thanks
There are several things you need to change in order to ensure you get the notifications:
You need to create your subscription before you send the request that will generate the Pub/Sub message. Only messages published after the successful creation of a subscription are guaranteed to be received by subscribers for that subscription.
A single pull request with returnImmediately set to true is unlikely to return any messages, even if a message has been published. With this property set, if there are no messages immediately in memory of the server reached, the empty response is returned. You should always set returnImmediately to false. This still won't guarantee that messages are returned in a single request/response, even if there are messages available, but it will make it more likely.
Ideally, you would use the asynchronous client library, which opens a stream to the Cloud Pub/Sub service and receives messages as soon as they are available. If you are going to use the synchronous Pull method directly, then you need to keep many of them outstanding simultaneously in order to ensure delivery of messages with minimal latency. As soon as you receive a PullResponse for any of the outstanding requests, you should immediately open up another request to replace it. The goal of the asynchronous client library is to prevent one from having to take all of these step manually to ensure efficient delivery.

Establishing session for Acumatica web services

Establishing session for Acumatica web services
I have a requirement where a session is to be established using web services, and then use that session to perform subsequent actions. E.g. Creating SOOrder and Shipment using web services using a previously created session/token.
SOOrder.Screen content = new SOOrder.Screen();
content.Url = InstanceUrl + “/Soap/SO301000.asmx";
content.CookieContainer = new System.Net.CookieContainer();
SOOrder.LoginResult lresult= content.Login(Username, password);
Using this, I have already obtained a session in lresult.Session.
Now, I would like to use this session in below shipmentcontent without calling login again.
SOShipment.Screen shipmentcontent = new SOShipment.Screen();
shipmentcontent.Url = InstanceUrl + "(W(3))/Soap/SO302000.asmx";
shipmentcontent.CookieContainer = new System.Net.CookieContainer();
shipmentcontent.Login(Username, password);
By package various screens in Web Services (SM.20.70.40) found under System -> Integration -> Configure -> Web Services you create a single end-point for all these screens. Consume this service end-point in you app, login only once and call out to all those screens.
thnx
If I understand you corectly you want to persist your login connection between different actions in Acumatia. In order to achieve this, I used the following approach:
Create graph:
public class Importer : PXGraph
{
}
Inside graph created following code:
public string GetHostUrl()
{
var nextIndex = HttpContext.Current.Request.Url.ToString().IndexOf("/", 7, StringComparison.Ordinal) + 1;
var urlStart = HttpContext.Current.Request.Url.ToString().IndexOf("/", nextIndex + 1, StringComparison.Ordinal);
var url = HttpContext.Current.Request.Url.ToString().Substring(0, urlStart);
return url;
}
public Screen Screen
{
get
{
var context = new Screen
{
CookieContainer = new CookieContainer(),
AllowAutoRedirect = true,
EnableDecompression = true,
Timeout = 1000000
};
var nextIndex = HttpContext.Current.Request.Url.ToString().IndexOf("/", 7, StringComparison.Ordinal) + 1;
var urlStart = HttpContext.Current.Request.Url.ToString().IndexOf("/", nextIndex + 1, StringComparison.Ordinal);
context.Url = HttpContext.Current.Request.Url.ToString().Substring(0, urlStart) + "/Soap/IMPORTET.asmx";
return context;
}
}
and then between different screens shared context of the Screen.
For example like this:
var scr = Screen;
var userName = PXAccess.GetUserName();
var password = GetUserPassword();
var context = Screen;
var lgRes = context.Login(userName, password);
but I preserved user password between different sessions

Listing Activities via Web Services

I'm trying to reproduce the Activities page in Microsoft CRM 4.0 via web services. I can retrieve a list of activities, and I believe I need to use ActivityPointers to retrieve the entities but have so far been unsuccessful. Would I need to loop through every single entity returned from the first query to retrieve the ActivityPointer for it? And if so, how would I then get the "Regarding" field or Subject of the activity (eg: email).
The code to retrieve the activities is:
var svc = GetCrmService();
var cols = new ColumnSet();
cols.Attributes = new[] { "activityid", "addressused", "scheduledstart", "scheduledend", "partyid", "activitypartyid", "participationtypemask", "ownerid" };
var query = new QueryExpression();
query.EntityName = EntityName.activityparty.ToString();
query.ColumnSet = cols;
LinkEntity link = new LinkEntity();
//link.LinkCriteria = filter;
link.LinkFromEntityName = EntityName.activitypointer.ToString();
link.LinkFromAttributeName = "activityid";
link.LinkToEntityName = EntityName.activityparty.ToString();
link.LinkToAttributeName = "activityid";
query.LinkEntities = new[] {link};
var activities = svc.RetrieveMultiple(query);
var entities = new List<ICWebServices.activityparty>();
RetrieveMultipleResponse retrieved = (RetrieveMultipleResponse) svc.Execute(request);
//var pointers = new List<activitypointer>();
foreach (activityparty c in activities.BusinessEntities)
{
entities.Add(((activityparty)c));
//the entities don't seem to contain a link to the email which they came from
}
Not sure if I understand your problem, but the field "activityid" in the activitypointer object is the same activityid as the underlying activity (email, task, phonecall, etc). The regardingobjectid is the link to the regarding entity.
Heres what you need to get the equivalent of the Activities page
ColumnSet cols = new ColumnSet()
{
Attributes = new string[] { "subject", "regardingobjectid", "regardingobjectidname", "regardingobjectidtypecode", "activitytypecodename", "createdon", "scheduledstart", "scheduledend" }
};
ConditionExpression condition = new ConditionExpression()
{
AttributeName = "ownerid",
Operator = ConditionOperator.Equal,
Values = new object[] { CurrentUser.systemuserid.Value } //CurrentUser is an systemuser object that represents the current user (WhoAmIRequest)
};
FilterExpression filter = new FilterExpression()
{
Conditions = new ConditionExpression[] { condition },
FilterOperator = LogicalOperator.And
};
QueryExpression query = new QueryExpression()
{
EntityName = EntityName.activitypointer.ToString(),
ColumnSet = cols,
Criteria = filter
};
BusinessEntityCollection activities = svc.RetrieveMultiple(query);
foreach (activitypointer activity in activities)
{
//do something with the activity
//or get the email object
email originalEmail = (email)svc.Retrieve(EntityName.email.ToString(), activity.activityid.Value, new AllColumns());
}