Etag support for S/4 EX - sap-cloud-sdk

Etag is supported in SDK: https://sap.github.io/cloud-sdk/docs/java/features/odata/use-typed-odata-v4-client-in-sap-cloud-sdk-for-java/#handling-of-etags
So experimenting it by using BusinessPartner entity in S/4 EX.
But seems there's no If-Match header:
How come the header doesn't show up - any prerequisite with etag?
(entering on behalf of the implementation partner team)
I checked the VersionIdentifier of the response and it was not set to a value.
I also checked the response's JSON __metadeta and header, but there were no values that appeared to correspond to the ETag value.
[Code]
BusinessPartner bp1 = new DefaultBusinessPartnerService().getBusinessPartnerByKey(bpId).execute(dest);
log.debug("get 1: {}", bp1);
log.debug("get 1 VersionIdentifier: {}", bp1.getVersionIdentifier());
bp1.setOrganizationBPName1("SCP Update 1st:" + System.currentTimeMillis());
ODataUpdateResult result1 = new DefaultBusinessPartnerService().updateBusinessPartner(bp1).execute(dest);
log.debug("Update1 Http Status: {}", result1.getHttpStatusCode());
bp1.setOrganizationBPName1("SCP Update 2nd:" + System.currentTimeMillis());
bp1.setVersionIdentifier("dummy");
ODataUpdateResult result2 = new DefaultBusinessPartnerService().updateBusinessPartner(bp1).execute(dest);
log.debug("Update2 Http Status: {}", result2.getHttpStatusCode());
[Log]
get 1: BusinessPartner(super=VdmObject(customFields={}, changedOriginal...
get 1 VersionIdentifier: None
Update1 Http Status: 204
Update2 Http Status: 204
[GET Response JSON(__metadata) / Response Header]
(It has masked the IP address.)
"__metadata": {
"id": "https://xxx.xxx.xxx.xxx:xxxxxx/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner('1000001')",
"uri": "https://xxx.xxx.xxx.xxx:xxxxxx/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner('1000001')",
"type": "API_BUSINESS_PARTNER.A_BusinessPartnerType"
},
HTTP/1.1 200 OK
content-type: application/json; charset=utf-8
content-length: 3152
dataserviceversion: 2.0
sap-metadata-last-modified: Thu, 14 May 2020 23:58:07 GMT
cache-control: no-store, no-cache
sap-processing-info: ODataBEP=,crp=,RAL=,st=,MedCacheHub=SHM,codeployed=X,softstate=
sap-server: true
sap-perf-fesrec: 243070.000000
I tried setting the VersionIdentifier to a meaningless value in my test code (2nd update).
The update process seems to be successful, although the request header now has "If-Match" added to it.
(I was expecting the update to fail because the values never match, so I was hoping the update would fail.)
[2nd Update(setVersionIdenfifier)]
(It has masked some of the values.)
PATCH http://xxx.xxx.xxx.xxx:xxxxxx/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner(BusinessPartner='1000001') HTTP/1.1
x-csrf-token: xxx
Content-Type: application/json
Accept: application/json
If-Match: dummy
Authorization: Basic xxx
SAP-Connectivity-SCC-Location_ID: xxx
Proxy-Authorization: Bearer xxx
sap-language: en
sap-client: xxx
Content-Length: 55

If ETags are not part of OData service responses, then you should approach the IT/administrators who maintain the S/4 backend. The SAP Cloud SDK is only consuming the OData service. Unfortunately it can't leverage ETag support if it's disabled.

Related

DocuSign_eSign::ApiError: Bad Request

I'm using the Docusign Ruby SDK to make calls on the API. I have my code working when pointed to the developer docusign, when pointed to prod I'm getting the DocuSign_eSign::ApiError: Bad Request error.
I have already gotten my integration key/client id approved in production and I've also already done the authorization grant part, where you allow the integration key to send envelopes on a user's behalf. This has been done in dev and production.
desc 'Perform API call to list envelopes'
task list: :initialize do
options = DocuSign_eSign::ListStatusChangesOptions.new
options.from_date = (Date.today - 10).strftime('%Y/%m/%d')
options.status = 'completed'
#list_results = #envelopes_api.list_status_changes #account_id, options
end
desc 'Build list results hash'
task build_list_hash: :list do
#list_results_hash = []
#list_results.envelopes.each do |list_hash|
#list_results_hash << { envelope_id: list_hash.envelope_id, status: 'pending', archive_at: Time.now + (86400 *30) }
end
puts "List results hash:\n #{#list_results_hash}"
File.open(ENV['MASTER_LIST_FILE'], 'w') { |file| file.write(#list_results_hash.to_yaml) }
end
I expect the output to be a list of envelopes which is what happens when running on demo.docusign
Update: Thanks for showing me how to get logs. It seems the error is with GetUserProfileImage
GET https://na3-app.docusign.net:8832/restapi/v2.1/accounts/91608b5d-d418-4c45-89f0-cd088504f99d/users/63757e15-8365-4fb9-9e7d-d9a8309f8e94/profile/image
TraceToken: 996d3b3f-2857-47d6-9d75-26a75b6b0a37
Timestamp: 2019-10-17T20:58:32.0932823Z
Content-Length: 0
Connection: keep-alive
Accept: application/json;text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzipdeflatebr
Accept-Language: en-USen; q=0.5
Authorization: Bearer [omitted]
Host: na3-app.docusign.net
Referer: https://app.docusign.com/preferences/security
User-Agent: Mozilla/5.0(Windows NT 10.0; Win64; x64; rv:69.0)Gecko/20100101Firefox/69.0
x-docusign-clienttransactionid: fa4e1202-09a4-431b-9abf-fe4d00acf565
x-csrf-token: c34f42c5aad288063afc8a6615be03c9
x-forwarded-for: 170.140.186.226, 162.248.185.11
x-docusign-authentication: {"IntegratorKey":"[omitted]"}
x-docusign-prettyprint: false
content-transfer-encoding: base64
x-forwarded-for-martini: 170.140.186.226
x-docusign-diagnostics: {"storedProcedureEventLogThreshold":"300"}
x-docusign-timetrack: CONN_START,2019-10-17T20:58:31.946Z;;REQ_SENT,2019-10-17T20:58:31.950Z;REST0_Start,2019-10-17T20:58:32.0620315Z
x-docusign-correlationtoken: fa4e1202-09a4-431b-9abf-fe4d00acf565
X-SecurityProtocol-Version: TLSv1.2
X-SecurityProtocol-CipherSuite: ECDHE-RSA-AES256-GCM-SHA384
404 NotFound
Content-Type: application/json; charset=utf-8
Content-Length: 95
X-DocuSign-ClientTransactionId: fa4e1202-09a4-431b-9abf-fe4d00acf565
X-DocuSign-TimeTrack: CONN_START,2019-10-17T20:58:31.946Z;;REQ_SENT,2019-10-17T20:58:31.950Z;;REST0_Start,2019-10-17T20:58:32.0620315Z;REST0_End,2019-10-17T20:58:32.0932823Z
X-DocuSign-TraceToken: 996d3b3f-2857-47d6-9d75-26a75b6b0a37
{"errorCode":"RESOURCE_NOT_FOUND","message":"The URL provided does not resolve to a resource."}```

Getting the API Key from ServiceStack request

Have a simple get Customer api that's returning list of customers fine.
Setting up for service to service authentication, if I make this [Authenticated] and try to implement using ApiKeyAuthProvider, the req.GetApiKey returns null and I get an error;
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 POST https://localhost:44347/api/customers application/json 0
Microsoft.AspNetCore.Hosting.Internal.WebHost:2019-07-01 16:50:34,004 [16] INFO Microsoft.AspNetCore.Hosting.Internal.WebHost - Request starting HTTP/1.1 POST https://localhost:44347/api/customers application/json 0
The thread 0x42cc has exited with code 0 (0x0).
The thread 0x302c has exited with code 0 (0x0).
ServiceStack.ServiceStackHost:2019-07-01 17:01:14,601 [16] ERROR ServiceStack.ServiceStackHost - ServiceBase<TRequest>::Service Exception
System.ArgumentOutOfRangeException: Length cannot be less than zero.
Parameter name: length
at System.String.Substring(Int32 startIndex, Int32 length)
at ServiceStack.Host.HttpRequestAuthentication.GetBasicAuth(IRequest httpReq) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack\Host\HttpRequestAuthentication.cs:line 45
at ServiceStack.Host.HttpRequestAuthentication.GetBasicAuthUserAndPassword(IRequest httpReq) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack\Host\HttpRequestAuthentication.cs:line 50
at ServiceStack.Auth.ApiKeyAuthProvider.PreAuthenticate(IRequest req, IResponse res) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack\Auth\ApiKeyAuthProvider.cs:line 232
at ServiceStack.AuthenticateAttribute.PreAuthenticate(IRequest req, IEnumerable`1 authProviders) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack\AuthenticateAttribute.cs:line 96
at ServiceStack.AuthenticateAttribute.ExecuteAsync(IRequest req, IResponse res, Object requestDto) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack\AuthenticateAttribute.cs:line 74
at ServiceStack.Host.ServiceRunner`1.ExecuteAsync(IRequest req, Object instance, TRequest requestDto) in C:\BuildAgent\work\3481147c480f4a2f\src\ServiceStack\Host\ServiceRunner.cs:line 127
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 640574.8754ms 400 application/json; charset=utf-8
Microsoft.AspNetCore.Hosting.Internal.WebHost:2019-07-01 17:01:14,607 [16] INFO Microsoft.AspNetCore.Hosting.Internal.WebHost - Request finished in 640574.8754ms 400 application/json; charset=utf-8
Clearly I have missed something obvious...any pointers appreciated.
// Register ORMLite connection
container.Register<IDbConnectionFactory>(dbFactory);
//Tell ServiceStack you want to persist User Auth Info in SQL Server
container.Register<IAuthRepository>(c => new OrmLiteAuthRepository(dbFactory));
// See https://docs.servicestack.net/api-key-authprovider
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new ApiKeyAuthProvider(AppSettings) {
SessionCacheDuration = TimeSpan.FromMinutes(10),
AllowInHttpParams = true, // Whether to allow API Keys in 'apikey' QueryString or FormData (e.g. `?apikey={APIKEY}`)
RequireSecureConnection = true,
},
}
) {
IncludeRegistrationService = true,
});
GlobalRequestFilters.Add((req, res, dto) =>
{
LastApiKey = req.GetApiKey();
});
Request
POST https://localhost:44347/api/customers HTTP/1.1
Host: localhost:44347
Connection: keep-alive
Content-Length: 2
Accept: application/json
Origin: https://localhost:44347
Authorization: yDOr26HsxyhpuRB3qbG07qfCmDhqutnA-yDOr26HsxyhpuRB3qbG07qfCmDhqutnA-yDOr26HsxyhpuRB3qbG07qfCmDhqutnA
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36
Content-Type: application/json
Referer: https://localhost:44347/swagger-ui/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en;q=0.9,en-US;q=0.8
{}
Response
HTTP/1.1 400 ArgumentOutOfRangeException
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8
Vary: Accept,Origin
Server: Microsoft-IIS/10.0
X-Powered-By: ServiceStack/5.50 NetCore/Windows
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS
Access-Control-Allow-Headers: Content-Type
X-Startup-Errors: 1
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Content-Disposition
X-SourceFiles: =?UTF-8?B?QzpcUmVwb3NcTUJXZWJccnZhcGlcUnZXZWJcUnZBcGlcYXBpXGN1c3RvbWVycw==?=
X-Powered-By: ASP.NET
Date: Wed, 03 Jul 2019 08:07:40 GMT
13e
{"responseStatus":{"errorCode":"ArgumentOutOfRangeException","message":"Length cannot be less than zero.\r\nParameter name: length","errors":[{"errorCode":"ArgumentOutOfRangeException","fieldName":"length","message":"Length cannot be less than zero.\r\n"}]},"responseCreatedUtcDateTime":"2019-07-03T08:07:40.7955827Z"}
0
Your client is sending an invalid Authorization Bearer Token, it needs to have the Authroization Bearer Token format:
Authorization: Bearer {Token}
If you're sending an Authenticated API Key or JWT Request via Open API it needs to have the Bearer prefix as per the Open API docs:
OK I had manually created a User and APIKey in the underlying tables and had used a UserAuthId 'SomeAuthId' i.e. letter in them, and the ORM repository code is expecting these to be integers. Its cool that I can see the code in github and debug this myself - thanks for the comment as it got me thinking and looking into my Auth setup.

Getting code=OrganizationFromTenantGuidNotFound while updating group using Microsoft Graph

I am getting the below exception
"error": {
"code": "OrganizationFromTenantGuidNotFound",
"message": "The tenant for tenant guid '****' does not exist.",
"innerError": {
"request-id": "2acd6af1-99f8-4aab-b71d-cffc60263a05",
"date": "2019-04-20T11:33:04"
}
while updating the group using open extension as shown below: -
POST https://graph.microsoft.com/v1.0/groups/db5f4dbe-40e0-4352-84fb-9a20c131cfaf/extensions HTTP/1.1
Authorization: Bearer ****
Content-Type: text/plain; charset=utf-8
Host: graph.microsoft.com
Content-Length: 111
Expect: 100-continue
Connection: Keep-Alive
{ "#odata.type":"microsoft.graph.openTypeExtension","extensionName":"com.test.nickName","date":"OpenExtension"}
I am using user***#outlook.com id and having active azure subscription. I have given all delegated permission to microsoft graph.
Content type was not specified and it was going as text. Error handling could be improved. After changing content type to "application/json", it is working perfectly fine.
POST https://graph.microsoft.com/v1.0/groups/db5f4dbe-40e0-4352-84fb-9a20c131cfaf/extensions HTTP/1.1 Authorization: Bearer **** Content-Type: application/json; charset=utf-8 Host: graph.microsoft.com Content-Length: 111 Expect: 100-continue Connection: Keep-Alive
{ "#odata.type":"microsoft.graph.openTypeExtension","extensionName":"com.test.nickName","date":"OpenExtension"}

Use international characters in azure storage metadata?

When I run this request using this azure library:
blobURL.PutBlob(ctx, strings.NewReader("Some text"), azblob.BlobHTTPHeaders{}, azblob.Metadata{"Foo": "/愛知県/bar"}, azblob.BlobAccessConditions{})
I get this error:
===== RESPONSE ERROR (ServiceCode=AuthenticationFailed) =====
Description=Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
RequestId:daf8a672-001e-000e-2f4b-a033f3000000
Time:2018-02-07T19:38:09.6740273Z, Details:
AuthenticationErrorDetail: The MAC signature found in the HTTP request 'REDACTED' is not the same as any computed signature. Server used following string to sign: 'PUT
9
x-ms-blob-cache-control:
x-ms-blob-content-disposition:
x-ms-blob-content-encoding:
x-ms-blob-content-language:
x-ms-blob-content-type:
x-ms-blob-type:BlockBlob
x-ms-client-request-id:f18fd538-3780-4f62-5236-777ac244affa
x-ms-date:Wed, 07 Feb 2018 19:38:09 GMT
x-ms-meta-foo:/愛知県/bar
x-ms-version:2016-05-31
/MYACCOUNT/MYCONTAINER/ReadMe.txt
timeout:61.
PUT https://MYACCOUNT.blob.core.cloudapi.de/MYCONTAINER/ReadMe.txt?timeout=61
Authorization: REDACTED
Content-Length: [9]
User-Agent: [Azure-Storage/0.1 (go1.9.3; darwin)]
X-Ms-Blob-Cache-Control: []
X-Ms-Blob-Content-Disposition: []
X-Ms-Blob-Content-Encoding: []
X-Ms-Blob-Content-Language: []
X-Ms-Blob-Content-Type: []
X-Ms-Blob-Type: [BlockBlob]
X-Ms-Client-Request-Id: [f18fd538-3780-4f62-5236-777ac244affa]
X-Ms-Date: [Wed, 07 Feb 2018 19:38:09 GMT]
X-Ms-Meta-Foo: [/愛知県/bar]
X-Ms-Version: [2016-05-31]
--------------------------------------------------------------------------------
RESPONSE Status: 403 Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
Content-Length: [936]
Content-Type: [application/xml]
Date: [Wed, 07 Feb 2018 19:38:09 GMT]
Server: [Microsoft-HTTPAPI/2.0]
X-Ms-Request-Id: [daf8a672-001e-000e-2f4b-a033f3000000]
exit status 1
Is this because "/愛知県/bar" != "/愛知県/bar"?
Do you see any way to set non-ascii character like "/愛知県/bar" as a metadata value?
Since you mentioned Go in the tag, I assume you are looking for using a transliterator, there is this one in github which you should try
: https://github.com/rainycape/unidecode

Create AD user and add to group at the same time?

I know how to create an AD B2C user, as well as add them to a group via the graph API. I'm doing this in my Azure Function. What I'm wondering is, is it possible to create the user and add them to the group at the same time? If not, then I suppose I'll have to handle the potential case of a user being created but failing to be added to a group. How likely is this sort of scenario? I'm trying to make sure I cover all my bases for all failure conditions, so any input would be appreciated. Thanks.
It seems like you want to use Batch Processing for these kinds of requests.
Here is the sample request they post in the article:
The following example shows a batch request that contains five items:
A change set that creates a user, testuser#contoso.onmicrosoft.com (POST). This operation includes the Prefer: response-no-content header to suppress the newly created user being returned.
A change set that updates the Department and Job Title properties of the new user (PATCH), and sets its manager navigation property (PUT).
A query for the manager of the new user (GET).
A change set that deletes the new user (DELETE).
A query for the user (GET). This operation will fail because the user was deleted in the previous step.
POST https://graph.windows.net/contoso.onmicrosoft.com/$batch?api-version=1.5 HTTP/1.1
Authorization: Bearer ey … jQA
Content-Type: multipart/mixed; boundary=batch_36522ad7-fc75-4b56-8c71-56071383e77b
Host: graph.windows.net
Content-Length: 2961
--batch_36522ad7-fc75-4b56-8c71-56071383e77b
Content-Type: multipart/mixed; boundary=changeset_77162fcd-b8da-41ac-a9f8-9357efbbd620
Content-Length: 631
--changeset_77162fcd-b8da-41ac-a9f8-9357efbbd620
Content-Type: application/http
Content-Transfer-Encoding: binary
POST /contoso.onmicrosoft.com/users?api-version=1.5 HTTP/1.1
Content-Type: application/json
Accept: application/json
Content-Length: 256
Prefer: return-no-content
Host: graph.windows.net
{
"accountEnabled": true,
"displayName": "Test User",
"mailNickname": "testuser",
"passwordProfile": { "password" : "Test1234", "forceChangePasswordNextLogin": false },
"userPrincipalName": "testuser#contoso.onmicrosoft.com"
}
--changeset_77162fcd-b8da-41ac-a9f8-9357efbbd620----batch_36522ad7-fc75-4b56-8c71-56071383e77b
Content-Type: multipart/mixed; boundary=changeset_4b2cbfb7-011d-4edb-8bbf-e044f9830aaf
Content-Length: 909
--changeset_4b2cbfb7-011d-4edb-8bbf-e044f9830aaf
Content-Type: application/http
Content-Transfer-Encoding: binary
PATCH /contoso.onmicrosoft.com/users/testuser#contoso.onmicrosoft.com?api-version=1.5 HTTP/1.1
Content-Type: application/json
Accept: application/json
Content-Length: 72
Host: graph.windows.net
{
"department": "Engineering",
"jobTitle": "Test Engineer"
}
--changeset_4b2cbfb7-011d-4edb-8bbf-e044f9830aaf
Content-Type: application/http
Content-Transfer-Encoding: binary
PUT /contoso.onmicrosoft.com/users/testuser#contoso.onmicrosoft.com/$links/manager?api-version=1.5 HTTP/1.1
Content-Type: application/json
Accept: application/json
Content-Length: 112
Host: graph.windows.net
{
"url":"https://graph.windows.net/contoso.onmicrosoft.com/users/a71e4d1c-ce99-40dc-8d4b-390eac63e039"
}
--changeset_4b2cbfb7-011d-4edb-8bbf-e044f9830aaf----batch_36522ad7-fc75-4b56-8c71-56071383e77b
Content-Type: application/http
Content-Transfer-Encoding:binary
GET /contoso.onmicrosoft.com/users/testuser#contoso.onmicrosoft.com/$links/manager?api-version=1.5 HTTP/1.1
Accept: application/json
Host: graph.windows.net
--batch_36522ad7-fc75-4b56-8c71-56071383e77b
Content-Type: multipart/mixed; boundary=changeset_9a0b5878-0f4a-4f57-91c5-9792cdd5ef20
Content-Length: 331
--changeset_9a0b5878-0f4a-4f57-91c5-9792cdd5ef20
Content-Type: application/http
Content-Transfer-Encoding: binary
DELETE /contoso.onmicrosoft.com/users/testuser#contoso.onmicrosoft.com?api-version=1.5 HTTP/1.1
Accept: application/json
Host: graph.windows.net
--changeset_9a0b5878-0f4a-4f57-91c5-9792cdd5ef20----batch_36522ad7-fc75-4b56-8c71-56071383e77b
Content-Type: application/http
Content-Transfer-Encoding:binary
GET /contoso.onmicrosoft.com/users/testuser#contoso.onmicrosoft.com?api-version=1.5 HTTP/1.1
Accept: application/json
Host: graph.windows.net
--batch_36522ad7-fc75-4b56-8c71-56071383e77b--

Resources