I'm trying to connect to
Bamboo HR's API, and I've managed to make this work with curl and Swift Foundation's URLRequest/URLSession (as well as an older Express node.js app).
However, when trying to utilize Vapor's client with the .get() method, I'm getting a successful 200 response from BambooHR - but the response's .body is empty.
Here's the code snippet:
key and {myDomain} are placeholders in the example
let encodedKey = "\(key):x".utf8.base64String
let directoryRootUrl = "https://api.bamboohr.com/api/gateway.php/{myDomain}/v1/employees/directory"
let response = try drop.client.get(
directoryRootURL,
headers: [
"Accept": "application/json",
"Authorization": "Basic \(encodedKey)",
"Host": "api.bamboohr.com"
])
When I do print(response), this is what's displayed:
Response
- HTTP/1.0 200 OK
- Headers:
Connection: close
Vary: User-Agent
Server: Apache
Content-Security-Policy: {...}
Date: Mon, 16 Jan 2017 00:26:31 GMT
Content-Type: application/json
X-Content-Type-Options: nosniff
- Body:
I'm wondering if I'm doing anything wrong with Vapor, or if it's a bug.
Like tobygriffin suggested, setting:
drop.client = FoundationClient.self
after creating the Droplet worked.
Related
I have created a dataset without major incidence
{\"tableName\": \"Sales\"
,\"columns\": [
{\"name\":\"ProductID\", \"dataType\":\"int64\", \"summarizeBy\":\"none\", \"isHidden\":\"True\", \"formatString\":\"#,##0\"}
, {\"name\":\"Date\", \"dataType\":\"DateTime\", \"formatString\":\"dd/mm/yyyy\"}
, {\"name\":\"TipID\", \"dataType\":\"int64\", \"summarizeBy\":\"none\", \"isHidden\":\"True\", \"formatString\":\"#,##0\"}
, {\"name\":\"Sales\", \"dataType\":\"double\", \"formatString\":\"#,##0\"}
]
}
, but when I try to put some measure, always get a "BadRequest".
{\"tableName\": \"Sales\"
,\"columns\": [
{\"name\":\"ProductID\", \"dataType\":\"int64\", \"summarizeBy\":\"none\", \"isHidden\":\"True\", \"formatString\":\"#,##0\"}
, {\"name\":\"Date\", \"dataType\":\"DateTime\", \"formatString\":\"dd/mm/yyyy\"}
, {\"name\":\"TipID\", \"dataType\":\"int64\", \"summarizeBy\":\"none\", \"isHidden\":\"True\", \"formatString\":\"#,##0\"}
, {\"name\":\"Sales\", \"dataType\":\"double\", \"formatString\":\"#,##0\"}
]
, \"measures\": [
{\"name\": \"TrySales\", \"expression\": \"SUM(Sales[Sales])\"}
]
}
I have created a "Push Dataset" where only add measures, with the same error.
HttpContent body = new StringContent(postBody);
body.Headers.ContentType = new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json");
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
HttpResponseMessage response = client.PutAsync(restUri, body).Result;
JSON
{\"tableName\": \"Sales\"
, \"measures\": [
{\"name\": \"TrySales\", \"expression\": \"CALCULATE(SUM(Ventas[Venta]);SUM(Sales[Sales])\"}
]
}
I only found an example code in this link..
The result from the PUT
Headers
{StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Pragma: no-cache
Transfer-Encoding: chunked
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Frame-Options: deny
X-Content-Type-Options: nosniff
RequestId: 6aa4198c-8315-4517-8f78-b5f90e6d1375
Access-Control-Expose-Headers: RequestId
request-redirected: true
home-cluster-uri: https://wabi-europe-north-b-redirect.analysis.windows.net/
Cache-Control: no-store, must-revalidate, no-cache
Date: Fri, 15 Nov 2019 13:08:36 GMT
Content-Type: application/json; charset=utf-8
}}
Request message
{Method: PUT, RequestUri: 'https://api.powerbi.com/v1.0/myorg/groups/1cd4f3cf-xxxx-41dd-83e4-c17708511d79/datasets/35b36723-ed24-xxxx-8e54-fc8e3a1503fe/tables/Sales'
, Version: 1.1, Content: System.Net.Http.StringContent, Headers:
{
Accept: application/json
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IkJCOE...
Content-Type: application/json
Content-Length: 147
}}
Any suggestions?
EDIT
Response
Id = 37, Status = RanToCompletion
, Method = "{null}"
, Result = "{\"error\":{
\"code\":\"BadRequest\"
,\"message\":\"Bad Request\"
,\"details\":[
{\"message\":\"The property 'tableName' does not exist on type 'Microsoft.PowerBI.ServiceContracts.Api.Table'
. Make sure to only use property names that are defined by the type.\"
,\"target\":\"tableSchema\"}
]
}
}
It seems a spelling error due to an error in the documentation. In this link it appears as "tableName" and I have not found any more examples.
It is an error in the documentation.
This example in the documentation has tableName in the json. However, when you click on Try it the body has name.
Using name instead of tableName worked for me.
I'm sending Cookie from Laravel application (http://backend.local) to Vue SPA (http://frontend.local:8080):
Laravel side (dummy route)
//api routes
Route::post('login', function () {
setcookie("name", 'value', time()+3600, "/", ".local");
return response('ok');
});
Vue side
axios.post('//backend.local/api/login')
.then(response => {
console.log(response)
}).catch(error => {
})
Response Headers:
HTTP/1.1 200 OK
Date: Mon, 21 May 2018 09:42:35 GMT
Server: Apache
Set-Cookie: name=value; expires=Mon, 21-May-2018 10:42:36 GMT; Max-Age=3600; path=/; domain=.local
Cache-Control: no-cache, private
Access-Control-Allow-Origin: http://frontend.local:8080
Vary: Origin
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
Content-Length: 2
Keep-Alive: timeout=10, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8
But there is no Cookies in broswer storage (Application tab in Developers Console).
What's wrong?
UPDATE: I think the problem is that there is port number in http://frontend.local:8080. Can I remove port number from url?
Original Answer
The browser will automatically reject any domain which doesn't have two parts to it. So using .local is not considered a domain at all. You need to two levels at least. Consider the below flask app I created for demoing the same
from flask import Flask, request, make_response
app = Flask(__name__)
#app.route("/")
def index():
resp = make_response()
resp.set_cookie('value1', ".frontend.local", domain=".frontend.local")
resp.set_cookie('value2', "frontend.local", domain="frontend.local")
resp.set_cookie('value3', ".local", domain=".local")
return resp
app.run(debug=True)
Now testing this through curl
$ curl -v frontend.local:5000
* Rebuilt URL to: frontend.local:5000/
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to frontend.local (127.0.0.1) port 5000 (#0)
> GET / HTTP/1.1
> Host: frontend.local:5000
> User-Agent: curl/7.54.0
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: text/html; charset=utf-8
< Set-Cookie: value1=.frontend.local; Domain=.frontend.local; Path=/
< Set-Cookie: value2=frontend.local; Domain=frontend.local; Path=/
< Set-Cookie: value3=.local; Domain=.local; Path=/
< Content-Length: 0
< Server: Werkzeug/0.14.1 Python/3.6.5
< Date: Tue, 29 May 2018 12:58:20 GMT
<
* Closing connection 0
When you navigate this in browser, you can see that the cookie with Domain=.local gets discarded but rest 2 remains
And later you can see that both the cookies have been sent to the reload of the same page
Update 1: 30th May 2018
Since you are using 2 different domains, you should use frontend.local.com and backend.local.com and the cookies should be returned with domain as .local.com, so that backend and frontend works as a subdomain of local.com. Then frontend.local.com gets the cookies set by backend.local.com through domain sharing as the cookie domain is set to .local.com
Since this is a CORS call, you will need to set setCredentials to true. This is a standard behavior: Standard CORS requests do not send or set any cookies by default.
The withCredentials property will include any cookies from the other domain in the request and also set any cookies from other domain.
So your Vue.js code will be:
axios.defaults.withCredentials = true
axios({
method: 'POST',
url: '//backend.local/api/login',
// THIS IS IMPORTANT
withCredentials: true
})
.then(response => {
console.log(response)
}).catch(error => {})
[Edit]: Read more about CORS at HTML Rocks.
Please let me know if it doesn't help.
My Angular4 app (running on http://127.0.0.1:4200 development server) is supposed to access a django REST backend on the web. The backend is under my control and is available only via HTTPS (running Apache that tunnels the request to a gunicorn server running on an internal port). Let's say that this is https://example.com/. For historical reasons, logging the user in is done using sessions, because I want the users to be able to also use Django's admin interface after they logged in. The workflow is as follows:
Users opens http://127.0.0.1:4200, I perform a GET request to https://example.com/REST/is_logged_in which returns a 403 when the user isn't logged in via sessions yet, 200 otherwise. In the former case, the user is redirected to https://example.com/login/, rendered by Django's template engine, allowing the user to log in. Once logged in, the user is redirected to http://127.0.0.1:4200
When clicking on some button in my Angular UI, a POST request is performed. This post request fails with 403, even though the preflight OPTIONS request explicitly lists POST as allowed actions.
Here is my CORS configuration in Django:
NG_APP_ABSOLUTE_URL = 'http://127.0.0.1:4200'
# adapt Django's to Angular's presumed XSRF cookie/header names
CSRF_COOKIE_NAME = "XSRF-TOKEN"
CSRF_HEADER_NAME = "HTTP_X_XSRF_TOKEN"
CORS_ORIGIN_WHITELIST = (
urlparse(NG_APP_ABSOLUTE_URL).netloc
)
CSRF_TRUSTED_ORIGINS = (
urlparse(NG_APP_ABSOLUTE_URL).netloc
)
CORS_ALLOW_HEADERS = default_headers + (
'x-xsrf-token',
)
CORS_ALLOW_CREDENTIALS = True
This is what Chrome reports for the (successful, 200) first REST GET request to check whether the user is logged in (after he successfully did) in the response:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Origin:http://127.0.0.1:4200
Allow:GET, HEAD, OPTIONS
Connection:close
Content-Type:application/json
Date:Wed, 26 Apr 2017 15:09:26 GMT
Server:gunicorn/19.6.0
Set-Cookie:XSRF-TOKEN=...; expires=Wed, 25-Apr-2018 15:09:26 GMT; Max-Age=31449600; Path=/
Transfer-Encoding:chunked
Vary:Accept,Cookie,Origin
X-Frame-Options:SAMEORIGIN
The corresponding request had this:
Cookie:sessionid=...; XSRF-TOKEN=...
Host:example.com
Origin:http://127.0.0.1:4200
Referer:http://127.0.0.1:4200/
Now, to the actual problem:
Preflight request:
Request URL:https://example.com/REST/change_user_data/
Request Method:OPTIONS
Status Code:200 OK
Access-Control-Request-Headers:content-type
Access-Control-Request-Method:POST
Connection:keep-alive
Host:example.com
Origin:http://127.0.0.1:4200
Referer:http://127.0.0.1:4200/dashboard/account
Preflight response:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:accept, accept-encoding, authorization, content-type, dnt, origin, user-agent, x-csrftoken, x-requested-with, x-xsrf-token
Access-Control-Allow-Methods:DELETE, GET, OPTIONS, PATCH, POST, PUT
Access-Control-Allow-Origin:http://127.0.0.1:4200
Access-Control-Max-Age:86400
Connection:close
Content-Length:0
Content-Type:text/html; charset=utf-8
Date:Wed, 26 Apr 2017 15:36:56 GMT
Server:gunicorn/19.6.0
Vary:Origin
X-Frame-Options:SAMEORIGIN
Now my failing (403) POST request:
Accept:application/json
Accept-Encoding:gzip, deflate, br
Accept-Language:de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
Connection:keep-alive
Content-Length:60
Content-Type:application/json
Cookie:sessionid=...; XSRF-TOKEN=...
Host:example.com
Origin:http://127.0.0.1:4200
Referer:http://127.0.0.1:4200/dashboard/account
The response headers:
HTTP/1.1 403 Forbidden
Date: Wed, 26 Apr 2017 15:36:56 GMT
Server: gunicorn/19.6.0
Vary: Accept,Cookie,Origin
X-Frame-Options: SAMEORIGIN
Content-Type: application/json
Access-Control-Allow-Credentials: true
Allow: POST, OPTIONS
Access-Control-Allow-Origin: http://127.0.0.1:4200
Set-Cookie: XSRF-TOKEN=...; expires=Wed, 25-Apr-2018 15:36:56 GMT; Max-Age=31449600; Path=/
Connection: close
Transfer-Encoding: chunked
Why wouldn't this request work? It makes little sense to me!
Best regards!
I had the same problem, trying to send a POST request to Django (port 8000) from my Angular CLI (port 4200). I thought it was a problem of Django so I installed cors package however the "problem" is with the browser (actually is not a problem, it is a security issue, see here). Anyway, I solved the problem adding a proxy rule for my Angular CLI, as follows:
First, instead of sending my requests to http://localhost:8000/api/... is send them to /api/ (i.e. to my ng server running at port 4200).
Then I added a file in my Angular project called "proxy.conf.json" with the following content:
{
"/api": {
"target": "http://localhost:8000",
"secure": false
}
}
Finally, run your ng server with the flag "--proxy-config":
ng serve --watch --proxy-config proxy.conf.json
All API requests will be sent to the port 4200 and Angular will internally redirect them to Django, avoiding the CORS problem.
Note that this is only valid for development and won't be used when you build your app code and add it as the static code of your Django server.
Finally, with this solution I didn't need anymore the python module for cors so you could remove it.
I am new to Load runner , Am facing am issue while play back of the script
LR 12.50
O.S Windows 7 SP2
Protocol is Mobile HTTP/HTML
Recording mode is Proxy
Let me explain my scenario
While executing following function:
web_custom_request("authenticate",
"URL=https://ws-xx.xxx.com/tcs/rest/authenticate?include=user,company",
"Method=POST",
"Resource=0",
"RecContentType=application/json",
"Referer=",
"Snapshot=t1.inf",
"Mode=HTTP",
"EncType=application/json",
"Body={\"password\":\"xxx\",\"username\":\"xxx\",\"version\":\"1.0.40\"}",
LAST);
For the above POST method , am getting response as below
HTTP/1.1 200 OK\r\n
Date: Tue, 13 Oct 2015 19:19:21 GMT\r\n
Server: Apache-Coyote/1.1\r\n
Content-Type: application/json\r\n
Set-Cookie: dtCookie=DBE9311E44E5C47902702DC762030583|TXlBcHB8MQ; Path=/;
Domain=.xxx.com\r\n
Connection: close\r\n
Transfer-Encoding: chunked\r\n
Which is fine ,Now the second custom request is shown below
web_custom_request("profiles",
"URL=https://ws-test.xxx.com/tcs/rest/profiles",
"Method=GET",
"Resource=1",
"RecContentType=application/json",
"Referer=",
"Snapshot=t2.inf",
LAST);
For the above GET requests in the replay logs am getting:
401 unauthorized error.
GET /tcs/rest/profiles HTTP/1.1\r\n
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT)\r\n
Accept: */*\r\n
Connection: Keep-Alive\r\n
Host: ws-test.xxx.com\r\n
Cookie: dtCookie=DBE9311E44E5C47902702DC762030583|TXlBcHB8MQ\r\n
\r\n
t=5921ms: 172-byte response headers for "https://ws-test.xxx.com/tcs/rest/profiles" (RelFrameId=1, Internal ID=2)
HTTP/1.1 401 Unauthorized\r\n
Date: Tue, 13 Oct 2015 19:19:22 GMT\r\n
Server: Apache-Coyote/1.1\r\n
Content-Type: application/json\r\n
Connection: close\r\n
Transfer-Encoding: chunked\r\n
\r\n
t=5922ms: 4-byte chunked response overhead for "https://ws-test.xxx.com/tcs/rest/profiles" (RelFrameId=1, Internal ID=2)
8b\r\n
t=5923ms: 139-byte chunked response body for "https://ws-test.xxx.com/tcs/rest/profiles" (RelFrameId=1, Internal ID=2)
{"errors":[{"message":"Authentication required to access endpoint","status":"401","code":"
NotAuthenticated","header":"Not Authenticated"}]}
I refereed this link.
My understanding from the above custom request , login is success but the next
subsequent requests are getting failed.
I have used web_cleanup_cookies() function but didn't solve the issue .
I tried to capture the Cookie ID using the below function
web_reg_save_param("COOKIE_ID",
"LR= Cookie: dtCookie=" ,
"RB= |TXlBcHB8MQ\r\n",
"Ord=All",
"RelFrameId=1",
"Search=All",
LAST);
web_add_header("Cookie",lr_eval_string("{COOKIE_ID}"));
Now question is where to place parameter "COOKIE_ID" in my script while there is
no value in script for COOKIE_ID?
How to handle this issue ? Can anybody please help me .
Please add below headers to the script
web_set_sockets_option("SSL_VERSION","TLS");
web_set_user("username", "password", "domain:portno" );
web_set_sockets_option("INITIAL_BASIC_AUTH","1");
In Vugen, Select snapshot view and compare both record and replay requests, suspecting there might be a missing of header in replay request.
If cookie is the only thing changing you can add it by using web_add_cookie function.
We are noticing that group members cannot be removed using the new Directory API if they are not Google accounts. We've tested this with both Dito GAM (note I am the author of GAM) and the Google API Explorer.
The operation functions correctly if the member to be removed is a Google account:
C:\gam-64>gam update group group99#jay.powerposters.org remove google-user#jay.powerposters.org
removing google-user#jay.powerposters.org
connect: (www.googleapis.com, 443)
send: 'DELETE /admin/directory/v1/groups/group99#jay.powerposters.org/members/google-user#jay.powerposters.org?quotaUser=1ee51612c9a0220af0cf5516a990b206e2a619e8&prettyPrint=true
HTTP/1.1
Host: www.googleapis.com
content-length: 0
authorization: Bearer <valid access token>
accept-encoding: gzip, deflate
accept: */*
user-agent: Dito GAM 3.01 / jay#ditoweb.com (Jay Lee) / Python 2.7.5 final / Windows-7-6.1.7601-SP1 AMD64 / google-api-python-client/1.2 (gzip)
reply: 'HTTP/1.1 204 No Content
header: Cache-Control: no-cache, no-store, max-age=0, must-revalidate
header: Pragma: no-cache
header: Expires: Fri, 01 Jan 1990 00:00:00 GMT
header: Date: Wed, 28 Aug 2013 12:57:06 GMT
header: ETag: "cZnI-gy4eI-n1-_cqk7okAteLZk/vyGp6PvFo4RvsFtPoIWeCReyIC8"
header: Server: GSE
this is the expected behavior. However, if the account to be removed is not a Google Account (Google Apps or consumer), an error is returned:
C:\gam-64>gam update group group99#jay.powerposters.org remove not-a-google-account#not-a-google-domain.com
removing not-a-google-account#not-a-google-domain.com
connect: (www.googleapis.com, 443)
send: 'DELETE /admin/directory/v1/groups/group99#jay.powerposters.org/members/not-a-google-account#not-a-google-domain.com?quotaUser=1ee51612c9a0220af0cf5516a990b206e2a619e8&prettyPrint=true
HTTP/1.1
Host: www.googleapis.com\r\ncontent-length: 0
authorization: Bearer <valid oauth 2.0 token>
accept-encoding: gzip, deflate
accept: */*
user-agent: Dito GAM 3.01 / jay#ditoweb.com (Jay Lee) / Python 2.7.5 final / Windows-7-6.1.7601-SP1 AMD64 / google-api-python-client/1.2 (gzip)
reply: 'HTTP/1.1 400 Bad Request
header: Content-Type: application/json; charset=UTF-8
header: Content-Encoding: gzip
header: Date: Wed, 28 Aug 2013 13:00:02 GMT
header: Expires: Wed, 28 Aug 2013 13:00:02 GMT
header: Cache-Control: private, max-age=0
header: X-Content-Type-Options: nosniff
header: X-Frame-Options: SAMEORIGIN
header: X-XSS-Protection: 1; mode=block
header: Content-Length: 122
header: Server: GSE
body: {
"error": {
"errors": [
{
"domain": "global",
"reason": "badRequest",
"message": "Bad Request"
}
],
"code": 400,
"message": "Bad Request"
}
}
Error 400: Bad Request - badRequest
Note that this same behavior shows in the Google API Explorer also:
http://screencast.com/t/fQZCnMYYs
https://developers.google.com/apis-explorer/#s/admin/directory_v1/directory.members.delete?groupKey=group99%2540jay.powerposters.org&memberKey=not-a-google-account%2540not-a-google-domain.com&_h=1&
It should be possible to remove ANY address from a Google Group via the Directory API
I spoke with Google Enterprise support about this and they acknowledged this was a bug (as Silvano has here). After speaking with the dev team, they told me that a work around for this is to delete using the ID of the user instead of email address.
A quick example of how you can delete all members using member ids.
Directory directory =
new Directory.Builder(httpTransport, jsonFactory, credentials)
.setApplicationName(APPLICATION_NAME)
.build();
Members members = directory.members().list(groupKey).execute();
for(Member member : members.getMembers()) {
String memberId = member.getId();
Delete delete = directory.members().delete(groupKey, memberId);
delete.execute();
}
Hope this helps until they can release a fix.
Google has resolved this issue on their end. Removing non-Google email addresses from a Google Group now works without issue.
Here is a working example in PHP with Add and Delete.
function gmail_provision($email, $email_list, $requested_operation) {
// requested_operation should be either add or delete
require_once "google-api-php-client/src/Google_Client.php";
require_once "google-api-php-client/src/contrib/Google_DirectoryService.php";
require_once "google-api-php-client/src/contrib/Google_Oauth2Service.php";
session_start();
$group_scope = 'https://www.googleapis.com/auth/admin.directory.group';
$service_account_email = '.....#developer.gserviceaccount.com';
$service_account_pkcs12_file_path = '/path/to/...-privatekey.p12';
$client_id = '......apps.googleusercontent.com';
$adminEmail = 'admin-account#email.com;
$key = file_get_contents($service_account_pkcs12_file_path);
$auth = new Google_AssertionCredentials($service_account_email, array($group_scope), $key, 'notasecret', 'http://oauth.net/grant_type/jwt/1.0/bearer', $adminEmail);
$client = new Google_Client();
$client->setClientId($client_id); // from API console
$client->setApplicationName("API Project Name");
$client->setUseObjects(true);
$client->setAssertionCredentials($auth);
try {
// Adding or removing from specified list
$sync_status = '';
$service = new Google_DirectoryService($client);
switch ($requested_operation) {
case "add": // Add
$member = new Google_Member(array('email' => $email,
'kind' => 'admin#directory#member',
'role' => 'MEMBER',
'type' => 'USER'));
$service->members->insert($email_list, $member);
break;
case "remove": // Remove
// Users must be removed by ID not by email address
// This is slow and should be changed when
// Google fixes the bug and allows delete by email.
// Get a list of email addresses in the list
$results = $service->members->listMembers($email_list);
// Push results into an array
$arrResults = get_object_vars($results);
// Recursive iterator
$arrIt = new RecursiveIteratorIterator(new RecursiveArrayIterator($arrResults));
// Cycle through the array searching for the email address
foreach ($arrIt as $sub) {
$subArray = $arrIt->getSubIterator();
if ($subArray['email'] === $email) {
// Store an array of the details associated
// with the email address
$outputArray[] = iterator_to_array($subArray);
}
}
// Delete by id
$service->members->delete($email_list, $outputArray[0]['id']);
break;
}
}
catch (Google_ServiceException $e) {
// $e->getCode()
// $e->getMessage()
}
catch (Google_Exception $e) {
// $e->getCode()
// $e->getMessage()
}
catch (Zend_Gdata_Gapps_ServiceException $e) {
// $e->getCode()
// $e->getMessage()
}
}