Get the groups of a customeruser in otrs - rest

I am extending OTRS with an app and need to get the groups a customeruser is in. I want to do this by communicating with the SessionGet-Endpoint (https://doc.otrs.com/doc/api/otrs/6.0/Perl/Kernel/GenericInterface/Operation/Session/SessionGet.pm.html)
The Endpoint for SessionGet returns a lot of information about the user but not the groups he is in. I am not talking about agents who can login to the backend of otrs but customerusers.
I am using OTRS 6 because it was the only one available in docker. I created the REST-endpoints in the backend and everything works well. There is a new functionality why I need to get the information about the groups.
Had a look at the otrs system-config but could not figure out if it is possible to include this information in the response.

Although I am a programmer, I did not want to write perl because of ... reasons.
I had a look at the file which handles the incoming request at /opt/otrs/Kernel/GenericInterface/Operation/Session/SessionGet.pm and traced the calls to the actual file where the information is collected from the database in /opt/otrs/Kernel/System/AuthSession/DB.pm. In line 169 the SQL-statement is written so it came to my mind that I just can extend this to also get the information of the groups, because, as I said, I did not want to write perl...
A typical response from this endpoint looks like this:
{
"SessionData": [
{
"Value": "2",
"Key": "ChangeBy"
},
{
"Value": "2019-06-26 13:43:18",
"Key": "ChangeTime"
},
{
"Value": "2",
"Key": "CreateBy"
},
{
"Value": "2019-06-26 13:43:18",
"Key": "CreateTime"
},
{
"Value": "XXX",
"Key": "CustomerCompanyCity"
},
{
"Value": "",
"Key": "CustomerCompanyComment"
}
...
}
A good thing would be to just insert another Value-Key-pair with the IDs of the groups. The SQL-statement queries only one table $Self->{SessionTable} mostly called otrs.sessions.
I used the following resources to create a SQL-statement which extends the existing SQL-statement with the needed information. You can find it here:
$DBObject->Prepare(
SQL => "
(
SELECT id, data_key, data_value, serialized FROM $Self->{SessionTable} WHERE session_id = ? ORDER BY id ASC
)
UNION ALL
(
SELECT
(
SELECT MAX(id) FROM $Self->{SessionTable} WHERE session_id = ?
) +1
AS id,
'UserGroupsID' AS data_key,
(
SELECT GROUP_CONCAT(DISTINCT group_id SEPARATOR ', ')
FROM otrs.group_customer_user
WHERE user_id =
(
SELECT data_value
FROM $Self->{SessionTable}
WHERE session_id = ?
AND data_key = 'UserID'
ORDER BY id ASC
)
)
AS data_value,
0 AS serialized
)",
Bind => [ \$Param{SessionID}, \$Param{SessionID}, \$Param{SessionID} ],
);
Whoever needs to get the groups of a customeruser can replace the existing code with the one provided. At least in my case it works very well. Now, I get the expected key-value-pair:
{
"Value": "10, 11, 6, 7, 8, 9",
"Key": "UserGroupsID"
},
I used the following resources:
Adding the results of multiple SQL selects?
Can I concatenate multiple MySQL rows into one field?
Add row to query result using select
Happy coding,
Nico

Related

How to use result of Lookup Activity in next Lookup of Azure Data Factory?

I have Lookup "Fetch Customers" with SQL statement:
Select Count(CustomerId) As 'Row_count' ,Min(sales_amount) as 'Min_Sales' From [sales].
[Customers]
It returns value
10, 5000
Next I have Lookup "Update Min Sales" with SQL statement, but getting error:
Update Sales_Min_Sales
SET Row_Count = #activity('Fetch Customers').output.Row_count,
Min_Sales = #activity('Fetch Customers').output.Min_Sales
Select 1
Same error occurs even I set Lookup to
Select #activity('Fetch Fetch Customers').output.Row_count
Error:
A database operation failed with the following error: 'Must declare the scalar variable
"#activity".',Source=,''Type=System.Data.SqlClient.SqlException,Message=Must declare the
scalar variable "#activity".,Source=.Net SqlClient Data
Provider,SqlErrorNumber=137,Class=15,ErrorCode=-2146232060,State=2,Errors=
[{Class=15,Number=137,State=2,Message=Must declare the scalar variable "#activity".,},],'
I have similar set up as yours. Two lookup activities.
First look up brings min ID and Max ID as shown
{
"count": 1,
"value": [
{
"Min": 1,
"Max": 30118
}
],
"effectiveIntegrationRuntime": "DefaultIntegrationRuntime (East US)",
"billingReference": {
"activityType": "PipelineActivity",
"billableDuration": [
{
"meterType": "AzureIR",
"duration": 0.016666666666666666,
"unit": "DIUHours"
}
]
},
"durationInQueue": {
"integrationRuntimeQueue": 22
}
}
in my second lookup i am using the below expression
Update saleslt.customer set somecol=someval where CustomerID=#{activity('Lookup1').output.Value[0]['Min']}
Select 1 as dummy
Just that we have to access lookup output using indices as mentioned and place the activity output inside {}.

Make dynamic request using Gatling

I want to make a dynamic request using Gatling.
using Gatling we are getting to rest-API to which we are posting an SMS request I want that each request should be done with a different sent of mob no.
exec(http("Sms Delivery")
.post(s"/deliveries")
.body(StringBody(
s"""{ "template":
|{ "type":"inline", "inline":
| { "channels":
| "sms":{ "body":{ "layout":"{greeting}", "fragments":{ "greeting":{"en-US":"ABC123 Interested to hear about some jobs?"}}}} }} },
| "recipients":[ { "contactDetails":{ "mobileNumber" : "+1${Random.nextInt(123123123)}" } }],
| "requester": "${UUID.random.toString}"}""".stripMargin)).asJson
.check(status.is(202))
.check(jsonPath("$.messageId").ofType[String])
)
}
setUp(
scenario("Create message-Cmd")
.exec(createMessageServiceSms()).inject(
rampConcurrentUsers(0) to (6) during(1),
constantConcurrentUsers(5) during (testTime seconds)
)
)
Let suppose Gatling sends 100 requests the for all those requests my mob no remains the same for each request is same let say +12011705515.
However, I want all no. should be different.
The string is created only once, then parsed as Expression Language, which allows you to substitute in session attributes.
If you want custom functions like creating a UUID and calling Random.nextInt, you need to pass in a function to StringBody.
StringBody(_ =>
s"""{ "template":
|{ "type":"inline", "inline":
| { "channels":
| "sms":{ "body":{ "layout":"{greeting}", "fragments":{ "greeting":{"en-US":"ABC123 Interested to hear about some jobs?"}}}} }} },
| "recipients":[ { "contactDetails":{ "mobileNumber" : "+1${Random.nextInt(123123123)}" } }],
| "requester": "${UUID.random.toString}"}""".stripMargin)
)
The parameter that we discarded is the virtual user's Session.
Worth noting that you may have too few digits for the mobileNumber, but that's another issue than creating a dynamic request.

Azure Pipelines: Bulk approve of deployments to environments

Is there any way to approve runs via the CLI or the API (or anything else)? I'm looking for a way to bulk approve multiple runs from different pipelines but it's not available in the UI.
Let's say I have 100 pipelines that have a deployment job to a production environment. I would like to approve all awaiting for approval runs.
Currently, I cannot find something like it in the docs of the Azure DevOps REST API or the CLI.
The feature docs:
https://learn.microsoft.com/en-us/azure/devops/pipelines/process/environments
https://learn.microsoft.com/en-us/azure/devops/pipelines/process/approvals
The following question is related but I'm looking for any way of solving it but not just via API:
Approve a yaml pipeline deployment in Azure DevOps using REST api
I was just searching for an answer for this regarding getting the approval id that you would need. In fact there is an undocumented API to approve an approval check.
This is as Merlin explain the following
https://dev.azure.com/{org}/{project}/_apis/pipelines/approvals/{approvalId}
The body has to look like this
[{
"approvalId": "{approvalId}",
"status": {approvalStatus},
"comment": ""
}]
where {approvalStatus} is telling the API if you approved or not. You probly have to try, but I had a 4 as a status. I guess there are only 2 possibilities. Either for "approved" or "denied".
The question is now how you get the approval ID? I found it. You get it by using the timeline API of a classic build. The build API documentation says that you get it by the following
https://dev.azure.com/{organization}/{project}/_apis/build/builds/{buildId}?api-version=5.1
the build timeline you get in the response of the build run, but it has a pattern which is
https://dev.azure.com/{organization}/{project}/_apis/build/builds/{buildId}/Timeline?api-version=5.1
Besides a flat array container a parent / child rleationship from stage, phase, job and tasks, you can find within it something like the following:
{
"records": [
{
"previousAttempts": [
],
"id": "95f5837e-769d-5a92-9ecb-0e7edb3ac322",
"parentId": "9e7965a8-d99d-5b8f-b47b-3ee7c58a5b1c",
"type": "Checkpoint",
"name": "Checkpoint",
"startTime": "2020-08-14T13:44:03.05Z",
"finishTime": null,
"currentOperation": null,
"percentComplete": null,
"state": "inProgress",
"result": null,
"resultCode": null,
"changeId": 73,
"lastModified": "0001-01-01T00:00:00",
"workerName": null,
"details": null,
"errorCount": 0,
"warningCount": 0,
"url": null,
"log": null,
"task": null,
"attempt": 1,
"identifier": "Checkpoint"
},
{
"previousAttempts": [
],
"id": "9e7965a8-d99d-5b8f-b47b-3ee7c58a5b1c",
"parentId": null,
"type": "Stage",
"name": "Power Platform Test (orgf92be262)",
"startTime": null,
"finishTime": null,
"currentOperation": null,
"percentComplete": null,
"state": "pending",
"result": null,
"resultCode": null,
"changeId": 1,
"lastModified": "0001-01-01T00:00:00",
"workerName": null,
"order": 2,
"details": null,
"errorCount": 0,
"warningCount": 0,
"url": null,
"log": null,
"task": null,
"attempt": 1,
"identifier": "Import_Test"
},
{
"previousAttempts": [
],
"id": "e54149c5-b5a7-4b82-8468-56ad493224b5",
"parentId": "95f5837e-769d-5a92-9ecb-0e7edb3ac322",
"type": "Checkpoint.Approval",
"name": "Checkpoint.Approval",
"startTime": "2020-08-14T13:44:03.02Z",
"finishTime": null,
"currentOperation": null,
"percentComplete": null,
"state": "inProgress",
"result": null,
"resultCode": null,
"changeId": 72,
"lastModified": "0001-01-01T00:00:00",
"workerName": null,
"details": null,
"errorCount": 0,
"warningCount": 0,
"url": null,
"log": null,
"task": null,
"attempt": 1,
"identifier": "e54149c5-b5a7-4b82-8468-56ad493224b5"
}
],
"lastChangedBy": "00000002-0000-8888-8000-000000000000",
"lastChangedOn": "2020-08-14T13:44:03.057Z",
"id": "86fb4204-9c5e-4e72-bdb1-eefe230480ec",
"changeId": 73,
"url": "https://dev.azure.com/***"
}
below you can see a step that is called "Checkpoint.Approval". The id of that step IS the approval Id you need to approve everything. If you want to know from which stage the approval is, then you can follow up the parentIds until the parentId property is null.
This will then be the stage.
With this you can successfully get the approval id and use it to approve with the said
What jessehouwing's guess is correct. Now multi-stage still be in preview, and the corresponding SDK/API/extension hasn't been expanded and provided to public.
You may think that what about not using API. I have checked the corresponding code from our backend, all of operations to multi-stage approval contain one required parameter: approvalId. I'm sure you have known that this value is unique and different approval map with different approvalId value. This means, no matter which method you want to try with, approvalId is the big trouble. And based on my known, until now, there's no any api/SDK, third tool or extension can achieve this value directly.
In addition, for multi-stage YAML, its release process logic is not same with the release that defined with UI. So, all of public APIs which can work with release(UI), are not suitable with the release of multi-stage.
We have one undisclosed api, can get Approval message of multi-stage:
https://dev.azure.com/{org}/{project}/_apis/pipelines/approvals/{approvalId}
You can try with listing approval without specifying approvalId: https://dev.azure.com/{org}/{project}/_apis/pipelines/approvals. And its response message: Query for approvals failed. A minimum of one query parameter is required.\r\nParameter name: queryParameters. This represents you must tell system the specified approval(the big trouble I mentioned previously).
In fact, for why approvalId is a necessary part, it is caused from our backend code structure. I'd suggest you raise suggestion on developing API/SDK for multi-stage here.
I can confirm that Sebastian's answer worked for me, even in Azure DevOps 2020 on-prem.
After retrieving the approvalId from either methods used above (I was specifically using a service hook for my integration), I used the following API PATCH call:
https://dev.azure.com/{organization}/{project}/_apis/pipelines/approvals/?api-version=6.0-preview
and in the body:
[
{
"approvalId": "{approvalId}",
"status": {status integer}, (4 - approved; 8 - rejected)
"comment": ""
}
]
The call is passed with the application/json Content-Type, but in some situations it did not like that I was using the [] brackets, so you will need to work around that, only then will the call work.
I was even able to integrate this call into my custom connector in MS Power Automate
I added support to the latest version of the AzurePipelinesPS Powershell module to support bulk pipeline approvals.
Code snippet without using the AzurePipelinesPS sessions
$instance = 'https://dev.azure.com'
$collection = 'your_project'
$project = 'your_project'
$apiVersion = '5.1-preview'
$securePat = 'your_personal_access_token' | ConvertTo-SecureString -Force -AsPlainText
Get-APPipelinePendingApprovalList -Instance $instance -Collection $collection -Project $project -PersonalAccessToken $securePat -ApiVersion $apiVersion | Out-GridView -Passthru | % { Update-APPipelineApproval -Instance $instance -Collection $collection -Project $project -PersonalAccessToken $securePat -ApiVersion $apiVersion -ApprovalId $PSitem.approvalId -status 'approved'}
Code snippet with AzurePipelinesPS sessions
$session = 'your_session'
Get-APPipelinePendingApprovalList $session | Out-GridView -Passthru | % { Update-APPipelineApproval $session -ApprovalId $PSitem.approvalId -status 'approved'}
See the AzurePipelinesPS project page for details on secure session handling.
Function Definitions used in the code above
Get-APPipelinePendingApprovalList
Loops through pipeline build runs with the status of 'notStarted' or 'inProgress' in a project. This build lookup supports filters like pipeline definition ids or a source branch name.
For each build it then looks up the timeline record where the approval id, the stage name and the stage identifier are found.
Optionally with the ExpandApproval switch it can expand each approval with details
The object returned from this function contains the following properties, the values have been mocked
pipelineDefinitionName : MyPipeline
pipelineDefinitionId : 100
pipelineRunId : 2001
pipelineUrl : https://dev.azure.com/your_project/_build/results?
sourceBranch : refs/heads/master
stageName : Prod Deployment
stageIdentifier : Prod_Deployment
approvalId : xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx
Out-GridView
Displays data in a Grid View where the results can be filtered, ordered and selected.
%
The percent sign is shorthand for Foreach-Object
Update-APPipelineApproval
Updates the status of an approval to approved or rejected.
Credit
Thanks to Sebastian Schütze for cracking the timeline part!
The az pipelines extension doesn't suport approvals yet, I suppose due to the fact that multi-stage pipelines are still in preview and the old release hub will eventually be replaced by it.
But there is a REST API you can use to list and update approvals. These can be called from PowerShell with relative ease.
Or use the vsteam powershell module and Get-VSTeamApproval and Set-VSTeamApproval.

Stuck at Getting Survey Details from SurveyMonkey REST Connection in QlikSense

I'm trying to connect to the Survey Monkey API via a hard coded connection set in a variable but the connection is giving me such error:
QVX_UNEXPECTED_END_OF_DATA: HTTP protocol error 400 (Bad Request):
{
"error":
{
"docs": "https://developer.surveymonkey.com/api/v3/#error-codes",
"message": "Invalid URL parameters.", "id": "1003", "name": "Bad Request",
"http_status_code": 400
}
}
Although, if i try the same but while getting surveys bulk, it works
vID is equal to a survey id
let vURL2 = 'https://api.surveymonkey.com/v3/surveys/$(vID)/details';
RestConnectorMasterTable_SurveryFullDetails:
SQL SELECT
"response_count",
"page_count",
"date_created",
"folder_id",
"nickname",
"id" AS "id_u3",
"question_count" AS "question_count_u0",
"category",
"preview",
"is_owner",
"language",
"footer",
"date_modified",
"analyze_url",
"summary_url",
"href" AS "href_u1",
"title" AS "title_u0",
"collect_url",
"edit_url",
"__KEY_root",
(SELECT
"done_button",
"prev_button",
"exit_button",
"next_button",
"__FK_buttons_text"
FROM "buttons_text" FK "__FK_buttons_text"),
(SELECT
"__FK_custom_variables"
FROM "custom_variables" FK "__FK_custom_variables"),
(SELECT
"href" AS "href_u0",
"description" AS "description_u0",
"title",
"position" AS "position_u2",
"id" AS "id_u2",
"question_count",
"__KEY_pages",
"__FK_pages",
(SELECT
"sorting",
"family",
"subtype",
"visible" AS "visible_u1",
"href",
"position" AS "position_u1",
"validation",
"id" AS "id_u1",
"forced_ranking",
"required",
"__KEY_questions",
"__FK_questions",
(SELECT
"text",
"amount",
"type",
"__FK_required"
FROM "required" FK "__FK_required"),
(SELECT
"__KEY_answers",
"__FK_answers",
(SELECT
"visible",
"text" AS "text_u0",
"position",
"id",
"__FK_rows"
FROM "rows" FK "__FK_rows"),
(SELECT
"description",
"weight",
"visible" AS "visible_u0",
"id" AS "id_u0",
"is_na",
"text" AS "text_u1",
"position" AS "position_u0",
"__FK_choices"
FROM "choices" FK "__FK_choices")
FROM "answers" PK "__KEY_answers" FK "__FK_answers"),
(SELECT
"heading",
"__FK_headings"
FROM "headings" FK "__FK_headings")
FROM "questions" PK "__KEY_questions" FK "__FK_questions")
FROM "pages" PK "__KEY_pages" FK "__FK_pages")
FROM JSON (wrap on) "root" PK "__KEY_root"
WITH CONNECTION(Url "$(vURL2)");
Have you checked out this fairly exhaustive SurveyMonkey how to guide on the Qlik Community? Might we worth checking you've followed all those steps, including giving the user permission to access the API.
It is not enough to hardcode URL, you also need to specify authorization header
WITH CONNECTION (
Url "$(vURL2)",
HTTPHEADER "Authorization" "bearer YOUR_TOKEN"
);

Extracting Client ID as a custom dimension with audience dimensions through API

I'm looking to get some answers here regarding the matter. There is no formal documentation available, would like some answers to my dilemma. Currently on analytics, I have Client ID setup as a custom dimension, session scope, I'm currently trying to match this Client ID with other dimensions via the Analytics Reporting API v4. (Reason having done so is that because in order for Client ID to be available outside of User Explorer on Analytics, one has to setup a custom dimension for this)
It's come to my attention that when I try to match Client ID, with an Audience Dimension, such as Affinity, nothing comes up. But say I do so with another dimension like PagePath + Affinity, the table exist. So I know that it is possible to pull Audience dimensions with other dimensions and it's possible for me to pull Client ID together with other dimensions. But what I'm trying to understand is why can't I pull Client ID together with Audience dimensions?
Some clarification on the matter would truly be appreciated, thanks.
For example (Can't show everything, but this is the response body of the python script)
In the case that i try to match my custom dimension (Client ID, session scope) with Affinity.
request_report = {
'viewId': VIEW_ID,
'pageSize' : 100000,
'dateRanges': [{'startDate': '2018-12-14',
'endDate': 'today'}],
'metrics': [{'expression': 'ga:users'}
],
'dimensions': [{'name': 'ga:dateHour'},
{'name':'ga:dimension1'},
{'name': 'ga:interestAffinityCategory'}
]
}
response = api_client.reports().batchGet(
body={
'reportRequests': request_report
}).execute()
Output:
ga:dateHour ga:dimension1 ga:interestAffinityCategory ga:users
Changing my dimensions, to pagePath + Affinity
request_report = {
'viewId': VIEW_ID,
'pageSize' : 100000,
'dateRanges': [{'startDate': '2018-12-14',
'endDate': 'today'}],
'metrics': [{'expression': 'ga:users'}
],
'dimensions': [{'name': 'ga:dateHour'},
{'name': 'ga:pagePath'},
{'name': 'ga:interestAffinityCategory'}
]
}
response = api_client.reports().batchGet(
body={
'reportRequests': request_report
}).execute()
Output:
ga:dateHour ga:pagePath ga:interestAffinityCategory ga:users
2018121415 homepage Business Professionals 10
2019011715 join-beta Beauty Mavens 16
2019011715 join-beta Frequently Visits Salons 21
Now say I change my combination to custom dimension + device category
request_report = {
'viewId': VIEW_ID,
'pageSize' : 100000,
'dateRanges': [{'startDate': '2018-12-14',
'endDate': 'today'}],
'metrics': [{'expression': 'ga:users'}
],
'dimensions': [{'name': 'ga:dateHour'},
{'name': 'ga:adContent'},
{'name': 'ga:deviceCategory'}
]
}
response = api_client.reports().batchGet(
body={
'reportRequests': request_report
}).execute()
Output:
ga:dateHour ga:dimension1 ga:adContent ga:deviceCategory ga:users
2018121410 10 ad1 desktop 1
2018121410 111 ad1 mobile 1
2018121410 119 ad4 mobile 1
2018121410 15 ad3 desktop 1
2018121410 157 ad3 mobile 1
In conclusion:
What I'd like to achieve is being able to pair my custom dimensions (Client ID) together with audience dimensions in order to be able to do segmentations. But first things first, if this permutation is not possible, I would like to understand as to why it's not possible? Is this a limitation from the API side? Or is this a policy thing (taking a guess here as I understand that there are identity protection policies)?
The BigQuery integration does not contain demographics/affinitys. The User Interface contains a variety of mechanisms to prevent you from isolating individual users, so in short no.