My iOS app interacts with a google app engine backend. I have the option for user to report an issue. When user enters the text describing the problem and presses the Submit button, I want to start a background upload of the issue description plus logs being collected in the app using CocoaLumberjack.
My current approach (almost working) is as follows. iOS sends a multipart/form-data POST request that contains a String with bug description and log file content (NSData) with each part separated by a boundary. The GAE server is able to successfully decode each part and I am able to see the file content when I print it out using logging.info(). However, when I try to store the file to GCS, I get an error. The code used to store to GCS and error are below.
I have one storage bucket configured and this has class = Durable Reduced Availability.
Can someone point me to what I'm doing wrong (I suspect it is something about how I set up the authorization lists in the GCS container)?
Alternatively, I am all ears if someone has an easier way to solve this problem.
Code used to store into GCS is:
logging.info('Creating file %s\n' % (filename))
write_retry_params = gcs.RetryParams(initial_delay=0.2,
max_delay=5.0,
backoff_factor=1.2,
max_retry_period=15)
gcs_file = gcs.open(filename,
'w',
content_type='text/plain',
retry_params=write_retry_params)
gcs_file.write(filename=getattr(request, 'fileAttached'))
gcs_file.close()
Error seen in GAE:
ForbiddenError: Expect status [201] from Google Storage. But got status 403.
Path: '/var/mobile/Containers/Data/Application/4FB6C1D7-9504-4215-BC25-FC490298EEF6/Library/Caches/Logs/com.apm.smartiothome.chatime%202016-01-20%2008-01.log'.
Request headers: {'x-goog-resumable': 'start', 'x-goog-api-version': '2', 'content-type': 'text/plain', 'accept-encoding': 'gzip, *'}.
Response headers: {'content-type': 'application/xml; charset=UTF-8', 'content-length': '195', 'vary': 'Origin', 'x-guploader-uploadid': 'AEnB2Uo1b-z2VGlHOnurusG2F9bgKcBVwmYWZrQFG4d4NBrHA_tk9wTPoa4kB1Aici7XP7Z6fNtuSJlGDokUmxtCFAl8aMnXGA'}.
Body: "AccessDeniedAccess denied.Caller does not have storage.objects.create access to bucket var.".
Extra info: None.
I opened the menu "IAM & Admin" > Service accounts and copied the "Service account ID" from the row "App Engine default service account". The name was my app followed by "#appspot.gserviceaccount.com".
Next, I opened Storage and click the "..." next to the default bucket > Edit bucket permissions. I added the service account as a user with Writer access.
Related
I understand the whole process of dialogflow and I have a working deployed bot with 2 different intents. How do I actually get the response from the bot when a user answers questions? (I set the bot on fulfillment to go to my domain). Using rails 5 app and it's deployed with Heroku.
Thanks!
If you have already set the GOOGLE_APPLICATION_CREDENTIALS path to the jso file, now you can test using a ruby script.
Create a ruby file -> ex: chatbot.rb
Write the code bellow in the file.
project_id = "Your Google Cloud project ID"
session_id = "mysession"
texts = ["hello"]
language_code = "en-US"
require "google/cloud/dialogflow"
session_client = Google::Cloud::Dialogflow::Sessions.new
session = session_client.class.session_path project_id, session_id
puts "Session path: #{session}"
texts.each do |text|
query_input = { text: { text: text, language_code: language_code } }
response = session_client.detect_intent session, query_input
query_result = response.query_result
puts "Query text: #{query_result.query_text}"
puts "Intent detected: #{query_result.intent.display_name}"
puts "Intent confidence: #{query_result.intent_detection_confidence}"
puts "Fulfillment text: #{query_result.fulfillment_text}\n"
end
Insert your project_id. You can find this information on your agent on Dialogflow. Click on the gear on the right side of the Agent's name in the left menu.
Run the ruby file in the terminal or in whatever you using to run ruby files. Then you see the bot replying to the "hello" message you have sent.
Obs: Do not forget to install the google-cloud gem:
Not Entirely familiar with Dilogflow, but if you want to receive a response when an action occurs on another app this usually mean you need to receive web-hooks from them
A WebHook is an HTTP callback: an HTTP POST that occurs when something happens; a simple event-notification via HTTP POST. A web application implementing WebHooks will POST a message to a URL when certain things happen.
I would recommend checking their fulfillment documentation for an example. Hope this helps you out.
Hi I am trying to save data in timeseries db but I am facing an error at timeseries ingest when I enter wss://gateway-predix-data-services.run.aws-usw02-pr.ice.predix.io/v1/stream/messages in websocket uri it shows an error like this when I click on open socket
The request failed with status code: 500
{
"error": "Error: unexpected server response (401)",
"url": "wss://gateway-predix-data-services.run.aws-usw02-pr.ice.predix.io/v1/stream/messages"
}
and see this screenshot once.
Thanks®ards
Eswar G
From the error log, it looks you got authorization error. Check if you are using correct predix-zone-id of the time-series instance in the request header or you have configured correct UAA instance in predix tool kit.
Have you updated your UAA client with timeseries authorities?
To inserting time series data with particular user or client need permission. For getting permission, you need to add authorities into your UAA client.
Following are the authorities for timeseries.
Experiencing a similar problem. I'm posting only one of several received error messages. I've cycled through combos of the following for uri and zone id:
uri:
wss://gateway-predix-data-services.run.aws-usw02-pr.ice.predix.io/v1/stream/messages
https://time-series-store-predix.run.aws-usw02-pr.ice.predix.io/v1/datapoints
zone-id:
44853a54-3ecd-4589-85da-b54f2fda7cce
and the zone id shown in the photo, with .ingest at the end.
so, 401 unauthorized.
Did you happen to re-create the timeseries or uaa? Perhaps they are not associated. Also, I'm assuming you did "login as client" in the top menu.
Might be a good idea to create a new timeseries tied to that uaa. Then use the new zoneId.
Did you add predix..ingest and predix..user to the scope and authorities section in uaa client in your predix.io account. Please check the client id to add permission for in websocketriver.config file.
I'm working to download a file via the Google Drive API using the gem google-api-client.
x = Google::Apis::DriveV2
drive = x::DriveService.new
drive.authorization = auth
files = drive.list_files
files.items.each_with_index do |file, index|
url_to_index = file.export_links.select { |k, v| v if k == 'text/plain' }
file_content = open(url_to_index["text/plain"]).read
end
The problem is file_content is returning the Google login screen not the file in text/plain format. It appears that when my Rails app opens the URL it does not have access to the text file.
What's the right way to enable my Rails app to grab the file in the text format?
Stated in Download Files
Depending on the type of download you'd like to perform — a file, a Google Document, or a content link — you'll use one of the following URLs:
Download a file — files.get with alt=media file resource
Download and export a Google Doc — files.export
Link a user to a file — webContentLink from the file resource
Downloading the file requires the user to have at least read access. Additionally, your app must be authorized with a scope that allows reading of file content.
You may go through the documentation for more information and examples.
See the log output: Two factor autentifications need to you permit access from you app to your google drive account.
In log you will see needed info to do this: link and secret_key.
tail -f log/*.log
I just realize that mimetype for download is different from the file meta.
Please check this url for available mimetype : https://developers.google.com/drive/api/v3/integrate-open
for example mimeType : 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
'
I'm trying to POST data to a Google Web App to automatically enter some tedious data. Whenever I try I get a response back asking me to log in, the Web App is deployed to be accessible by anyone.
Can I POST data to the form without authentication? If not, what type of authentication is required?
Edit: Quick code sample I threw together:
WebClient client = new WebClient();
var keyValue = new NameValueCollection();
keyValue.Add("agentName", "John Doe");
keyValue.Add("dayOff", "Sunday");
keyValue.Add("startTime", "8:00 AM");
Uri uri = new Uri("mywebapp url");
byte[] response = client.UploadValues(uri, "POST", keyValue);
string result = Encoding.UTF8.GetString(response);
In order to allow anyone to execute your script, even if they are not signed in to a Google account, you need to use the following settings in the "Deploy as web app" dialog:
Project version: if in doubt, select "New" to ensure that you are deploying the latest copy of the script's code.
Execute the app as: select "Me". This ensures that the "even anonymous" option will be available.
Who has access to the app: select "Anyone, even anonymous". (Note that if you select "Anyone", only users that are signed-in to a Google account will be able to execute your script.)
Once you have selected these options, click the "Update" button and copy the script URL. It should look like https://script.google.com/a/macros/<APPS DOMAIN>/s/<SCRIPT KEY>/exec. At this point an unauthenticated GET or POST to the script's URL should be successful.
Be aware that the script's execution will count against your daily Apps Script quota.
I've read the whole Box.com developers api guide and spent hours on the web researching this particular question but I can't seem to find a definitive answer and I don't want to start creating a solution if I'm going down the wrong path. We have a production environment where as once we are finished working with files our production software system zips them up and saves them into a local server directory for archival purposes. This local path cannot be changed. My question is how can I programmatically upload these files to our Box.com account so we can archive these on the cloud? Everything I've read regarding this involves using OAuth2 to gain access to our account which I understand but it also requires the user to login. Since this is an internal process that is NOT exposed to outside users I want to be able to automate this otherwise it would not be feasable for us. I have no issues creating the programs to trigger everytime a new files gets saved all I need is to streamline the Box.com access.
I just went through the exact same set of questions and found out that currently you CANNOT bypass the OAuth process. However, their refresh token is now valid for 60 days which should make any custom setup a bit more sturdy. I still think, though, that having to use OAuth for an Enterprise setup is a very brittle implementation -- for the exact reason you stated: it's not feasible for some middleware application to have to rely on an OAuth authentication process.
My Solution:
Here's what I came up with. The following are the same steps as outlined in various box API docs and videos:
use this URL https://www.box.com/api/oauth2/authorize?response_type=code&client_id=[YOUR_CLIENT_ID]&state=[box-generated_state_security_token]
(go to https://developers.box.com/oauth/ to find the original one)
paste that URL into the browser and GO
authenticate and grant access
grab the resulting URL: http://0.0.0.0/?state=[box-generated_state_security_token]&code=[SOME_CODE]
and note the "code=" value.
open POSTMAN or Fiddler (or some other HTTP sniffer) and enter the following:
URL: https://www.box.com/api/oauth2/token
create URL encoded post data:
grant_type=authorization_code
client_id=[YOUR CLIENT ID]
client_secret=[YOUR CLIENT SECRET]
code= < enter the code from step 4 >
send the request and retrieve the resulting JSON data:
{
"access_token": "[YOUR SHINY NEW ACCESS TOKEN]",
"expires_in": 4255,
"restricted_to": [],
"refresh_token": "[YOUR HELPFUL REFRESH TOKEN]",
"token_type": "bearer"
}
In my application I save both auth token and refresh token in a format where I can easily go and replace them if something goes awry down the road. Then, I check my authentication each time I call into the API. If I get an authorization exception back I refresh my token programmatically, which you can do! Using the BoxApi.V2 .NET SDK this happens like so:
var authenticator = new TokenProvider(_clientId, _clientSecret);
// calling the 'RefreshAccessToken' method in the SDK
var newAuthToken = authenticator.RefreshAccessToken([YOUR EXISTING REFRESH TOKEN]);
// write the new token back to my data store.
Save(newAuthToken);
Hope this helped!
If I understand correctly you want the entire process to be automated so it would not require a user login (i.e run a script and the file is uploaded).
Well, it is possible. I am a rookie developer so excuse me if I'm not using the correct terms.
Anyway, this can be accomplished by using cURL.
First you need to define some variables, your user credentials (username and password), your client id and client secret given by Box (found in your app), your redirect URI and state (used for extra safety if I understand correctly).
The oAuth2.0 is a 4 step authentication process and you're going to need to go through each step individually.
The first step would be setting a curl instance:
curl_setopt_array($curl, array(
CURLOPT_URL => "https://app.box.com/api/oauth2/authorize",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "content-type: application/x-www-form-urlencoded",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS =>
"response_type=code&client_id=".$CLIENT_ID."&state=".$STATE,
));
This will return an html text with a request token, you will need it for the next step so I would save the entire output to a variable and grep the tag with the request token (the tag has a "name" = "request_token" and a "value" which is the actual token).
Next step you will need to send another curl request to the same url, this time the post fields should include the request token, user name and password as follows:
CURLOPT_POSTFIELDS => "response_type=code&client_id=".$CLIENT_ID."&state=".$STATE."&request_token=".$REQ_TOKEN."&login=".$USER_LOGIN."&password=".$PASSWORD
At this point you should also set a cookie file:
CURLOPT_COOKIEFILE => $COOKIE, (where $COOKIE is the path to the cookie file)
This will return another html text output, use the same method to grep the token which has the name "ic".
For the next step you're going to need to send a post request to the same url. It should include the postfields:
response_type=code&client_id=".$CLIENT_ID."&state=".$STATE."&redirect_uri=".$REDIRECT_URI."&doconsent=doconsent&scope=root_readwrite&ic=".$IC
Be sure to set the curl request to use the cookie file you set earlier like this:
CURLOPT_COOKIEFILE => $COOKIE,
and include the header in the request:
CURLOPT_HEADER => true,
At step (if done by browser) you will be redirected to a URL which looks as described above:
http://0.0.0.0(*redirect uri*)/?state=[box-generated_state_security_token]&code=[SOME_CODE] and note the "code=" value.
Grab the value of "code".
Final step!
send a new cur request to https//app.box.com/api/oauth2/token
This should include fields:
CURLOPT_POSTFIELDS => "grant_type=authorization_code&code=".$CODE."&client_id=".$CLIENT_ID."&client_secret=".$CLIENT_SECRET,
This will return a string containing "access token", "Expiration" and "Refresh token".
These are the tokens needed for the upload.
read about the use of them here:
https://box-content.readme.io/reference#upload-a-file
Hope this is somewhat helpful.
P.S,
I separated the https on purpuse (Stackoverflow wont let me post an answer with more than 1 url :D)
this is for PHP cURL. It is also possible to do the same using Bash cURL.
For anyone looking into this recently, the best way to do this is to create a Limited Access App in Box.
This will let you create an access token which you can use for server to server communication. It's simple to then upload a file (example in NodeJS):
import box from "box-node-sdk";
import fs from "fs";
(async function (){
const client = box.getBasicClient(YOUR_ACCESS_TOKEN);
await client.files.uploadFile(BOX_FOLDER_ID, FILE_NAME, fs.createReadStream(LOCAL_FILE_PATH));
})();
Have you thought about creating a box 'integration' user for this particular purpose. It seems like uploads have to be made with a Box account. It sounds like you are trying to do an anonymous upload. I think box, like most services, including stackoverflow don't want anonymous uploads.
You could create a system user. Go do the Oauth2 dance and store just the refresh token somewhere safe. Then as the first step of your script waking up go use the refresh token and store the new refresh token. Then upload all your files.