Swift: listBlobsSegmentedWithContinuationToken method generates errors in Swift sample - swift

I got this code from this github: azure-storage-ios/BlobSample/
I am able to create my container and add blobs to it. I verified they are there in the azure portal. I am using the SAS token method per this:
var containerURL = "https://<mystorage>.blob.core.windows.net/profiles?se=2016-09-05T00%3A00%3A00Z&sp=rw&sv=2015-02-21&sr=c&sig=<mysig>"
var usingSAS = true
self.container = AZSCloudBlobContainer(url: NSURL(string: containerURL)!, error: &error)
// ... later:
blob.uploadFromText(textTextField.text ?? "", completionHandler: { (error: NSError?) -> Void in
})
But now, I am trying to load blob but receive one or more errors!
func reloadBlobList() {
self.container.listBlobsSegmentedWithContinuationToken(nil, prefix: nil, useFlatBlobListing: true, blobListingDetails: AZSBlobListingDetails.All, maxResults: 50) { (error : NSError?, results : AZSBlobResultSegment?) -> Void in
if let error = error {
print("error code! \(error.code)")
print("error description! \(error.description)")
}
I get this error about AuthorizationPermissionMismatch mainly
error code! 3
error description! Error Domain=com.Microsoft.AzureStorage.ErrorDomain Code=3 "(null)" UserInfo={Code=AuthorizationPermissionMismatch, AdditionalErrorDetails={
}, rawErrorData={length = 279, capacity = 279, bytes = 0xefbbbf3c3f786d6c2076657273696f6e ... 3c2f4572726f723e}, OperationContext=, RequestResult=, HTTP Status Code=403, URLResponse= { URL: https://.blob.core.windows.net/profiles?sig=&api-version=2015-04-05&sp=rw&se=2016-09-05T00%3A00%3A00Z&sv=2015-02-21&sr=c&restype=container&comp=list&maxresults=50&include=snapshots,metadata,uncommittedblobs,copy } { status code: 403, headers {
"Content-Length" = 279;
"Content-Type" = "application/xml";
Date = "Fri, 27 May 2016 08:12:19 GMT";
Server = "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0";
"x-ms-request-id" = "90746e2f-0001-0114-7eef-b7be1e000000";
"x-ms-version" = "2015-04-05";
} }, Message=This request is not authorized to perform this operation using this permission.
RequestId:90746e2f-0001-0114-7eef-b
the key I generated earlier had the r/w permission and I was able to post blobs to the container, what else should I've done for the retrieval?

var containerURL =
"https://.blob.core.windows.net/profiles?se=2016-09-05T00%3A00%3A00Z&sp=rw&sv=2015-02-21&sr=c&sig="
In order to list blobs in a blob container, you need List (l) permission in your SAS. Currently you only have Read (r) and Write (w) permission. Can you try by creating a new SAS with List permission as well?

Related

Downloading a JSON file as an Unauthenticated user AWS/S3/Cognito

I am trying to download a JSON file which is in my bucket in my S3 on my AWS account. I created an Unauthenticated cognito pool and copied this into my app delegate from the sample code:
let credentialsProvider = AWSCognitoCredentialsProvider(regionType:.USWest2,
identityPoolId:"us-west-2:59a31a8f-ee6a-45fe-adaa-fa3eff871c80")
let configuration = AWSServiceConfiguration(region:.USWest2, credentialsProvider:credentialsProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration
In my view controller I have this code:
let transferManager = AWSS3TransferManager.default()
let downloadingFileURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("db_storage/costdb_latest.json ")
if let downloadRequest = AWSS3TransferManagerDownloadRequest(){
downloadRequest.bucket = "coast-s3-bucket"
downloadRequest.key = "db_storage/costdb_latest.json "
downloadRequest.downloadingFileURL = downloadingFileURL
transferManager.download(downloadRequest).continueWith(executor: AWSExecutor.default(), block: { (task: AWSTask<AnyObject>) -> Any? in
if( task.error != nil){
print(task.error!.localizedDescription)
return nil
}
print(task.result!)
if let data = NSData(contentsOf: downloadingFileURL){
DispatchQueue.main.async(execute: { () -> Void in
print(data)
})
}
return nil
})
}
Both the bucket and the pool are in the same region. USWest2 (Oregon). My bucket is public, and I've added AmazonS3FullAccess and AmazonS3ReadOnlyAccess to my policies. And I'm getting this error:
The operation couldn’t be completed. (com.amazonaws.AWSS3ErrorDomain error 4.)
Its not an authentication/authorization error (I think). From the looks of it, there is some issue with the configuration (region,endpoint etc.). Generally, these errors contain a full description of what's wrong. Try logging the full error message.
Also, see this example app for S3 TransferManager. Use your S3 & Cognito details. If this works, there is some code issue and you can find out by comparing both of these. If this does not work, then this could be a service issue.

AWS API Gateway Error Handling with Generated SDK (Swift)

I'm fairly new to AWS and using API Gateway -> Lambda -> RDS. Also, I'm using the generated SDK for Swift in my iOS mobile application that is consuming the API.
The straight forward paths work fine when returning success (200). However, I'm trying to add in more error handling for edge cases. If an API request doesn't find the resource it's expected, I'm trying to return a 404 error. I've added this to the API Integration Response and Method Response appropriately. I can test this and get my error model returned as expected along with the correct HTTP Status code of 404.
However, I'm struggling with how to handle this on the mobile application side. How would this be handled by the generated SDK? It just throws an error in the API call and I expected a way to retrieve my "Error" model object that I've defined.
Here is normal path
Here is failed path with 404
Generated SDK (Swift) method:
public func userUseridentityGet(useridentity: String) -> AWSTask<RSAPI_UserModel> {
let headerParameters = [
"Content-Type": "application/json",
"Accept": "application/json",
]
let queryParameters:[String:Any] = [:]
var pathParameters:[String:Any] = [:]
pathParameters["useridentity"] = useridentity
return self.invokeHTTPRequest("GET", urlString: "/user/{useridentity}", pathParameters: pathParameters, queryParameters: queryParameters, headerParameters: headerParameters, body: nil, responseClass: RSAPI_UserModel.self) as! AWSTask<RSAPI_UserModel>
}
At runtime, I get the following logged out when I print the error out:
Error occurred: Error
Domain=com.amazonaws.AWSAPIGatewayErrorDomain Code=1 "(null)"
UserInfo={HTTPBody={
code = 404;
message = "No Result Found.";
"request-id" = "132b8eaa-7b24-11e7-b1fd-d342f0413b7d";
type = NotFound; }, HTTPHeaderFields={type = immutable dict, count = 8, entries => 0 :
x-cache = {contents = "Error
from cloudfront"} 1 : Content-Type = {contents = "application/json"} 3 : x-amzn-requestid =
{contents =
"131a5075-7b24-11e7-87bd-9fcb4cb4e04e"} 4 : Via = {contents = "1.1
bd4761ff0774f9ee778140b91a0431c9.cloudfront.net (CloudFront)"} 6 :
Date = {contents = "Mon, 07 Aug
2017 03:54:13 GMT"} 10 : Content-Length = 134 11 : x-amzn-trace-id =
{contents =
"sampled=0;root=1-5987e464-d3d8f7801b7ae5aa6a52fc1b"} 12 :
x-amz-cf-id = {contents =
"QKpl64W1qDaeo0zlsx2iOwTW0oO_jyPRmMT7ByPKLnen04qiPEeD6w=="} } }
The question is how do I get that de-serialized into my "Error" object model that I've defined? How do I appropriately detect this error condition so I can write logic in my mobile application to handle it?
For a workaround, I've just handled this by manually extracting out the data. If anyone has a better way of dealing with this. I'd definitely be interested.
if let error = task?.error as NSError? {
print("Error occurred: \(error)")
if error.code == 1 {
let httpBody : NSDictionary = (error.userInfo["HTTPBody"] as? NSDictionary)!
print("Code \(httpBody["code"] ?? "none") - Type \(httpBody["type"] ?? "none") - Message \(httpBody["message"] ?? "none") - RequestID \(httpBody["request-id"] ?? "none")")
}
return nil
}

Upload Image to Amazon S3 Using Swift3

After looking around at different threads such as these (Swift - AWS S3 Upload Image from Photo Library and download it), and (Upload image AWS S3 bucket in swift), I got pretty close to getting an image upload to work but can't figure out what I'm doing wrong?
I get the following error below in my console.
Error: Error Domain=com.amazonaws.AWSS3TransferUtilityErrorDomain Code=1 "(null)" UserInfo={Server=AmazonS3, Transfer-Encoding=Identity, Connection=close, Content-Type=application/xml, Date=Tue, 13 Dec 2016 05:58:32 GMT, x-amz-request-id=2A76DF0FE33476C5, x-amz-id-2=QWZgOETbWQfddlqKmm0w3Z9HFGM2x1DWnrFjukiajTsIXfbSt9W0orTkoZeNXH/bI1xfc3mxI4Q=, x-amz-bucket-region=us-west-1}
My code is below:
let myIdentityPoolId = "us-west-2:dca2beb4-etcetcetc...."
let credentialsProvider:AWSCognitoCredentialsProvider = AWSCognitoCredentialsProvider(regionType: AWSRegionType.usWest2, identityPoolId: myIdentityPoolId)
let configuration = AWSServiceConfiguration(region: AWSRegionType.usWest2, credentialsProvider: credentialsProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration
I then call to upload my image with a function I made below
func uploadImage(filename:String){
print("AWS Upload Image Attempt...")
//defining bucket and upload file name
let S3BucketName: String = "distribution-tech-mobile"
let filepath = "\(AppDelegate.appDelegate.applicationDocumentsDirectory())/\(filename)"
let imageURL = URL(fileURLWithPath: filepath)
let S3UploadKeyName = filename //TODO: Change this later
let uploadRequest = AWSS3TransferManagerUploadRequest()
uploadRequest?.bucket = S3BucketName
uploadRequest?.key = filename
uploadRequest?.contentType = "image/jpeg"
uploadRequest?.body = imageURL
uploadRequest?.serverSideEncryption = AWSS3ServerSideEncryption.awsKms
uploadRequest?.uploadProgress = { (bytesSent, totalBytesSent, totalBytesExpectedToSend) -> Void in
DispatchQueue.main.async(execute: {
self.amountUploaded = totalBytesSent // To show the updating data status in label.
self.fileSize = totalBytesExpectedToSend
print("\(totalBytesSent)/\(totalBytesExpectedToSend)")
})
}
self.uploadCompletionHandler = { (task, error) -> Void in
DispatchQueue.main.async(execute: {
if ((error) != nil){
print("Failed with error")
print("Error: \(error!)");
}
else{
print("Sucess")
}
})
}
let transferUtility = AWSS3TransferUtility.default()
let expression = AWSS3TransferUtilityUploadExpression()
transferUtility.uploadFile(imageURL, bucket: S3BucketName, key: S3UploadKeyName, contentType: "image/jpeg", expression: expression, completionHander: uploadCompletionHandler).continue({ (task) -> AnyObject! in
if let error = task.error {
print("Error: \(error.localizedDescription)")
}
if let exception = task.exception {
print("Exception: \(exception.description)")
}
if let _ = task.result {
print("Upload Starting!")
}
return nil;
})
}
I get the console message "Upload Starting" and then a message "Failed with error" (which comes from my completion handler), followed by the error I assume from Amazon.
Any thoughts on what I'm doing wrong?
Okay I found the answer, but I have a different problem now that I'll post in another question regarding showing upload progress.
The answer was my bucket was created in the incorrect region. I created my credentials in Oregon, which is Us-West-2, and I created the bucket in Northern California by accident the first time. This apparently created the error.

Error in Listing my YouTube playlists

I got my user access token via Google signin iOS sdk.
Then I use the following source code to get my playlists in YouTube:
func listMyPlaylists(completionHandler: #escaping (_ result: Any?, _ error: Error?) -> ())
{
let url = URL(string: "https://www.googleapis.com/youtube/v3/playlists")!
let parameters: Parameters = ["part": "id","mine": "true"]
let headers: HTTPHeaders = ["Authorization": "Bearer " + accessToken, "Content-Type": "application/json"]
Alamofire.request(url, method: .get, parameters: parameters, encoding: URLEncoding.default, headers: headers).responseJSON { json in
if let error = json.result.error
{
completionHandler(nil, error)
}
else
{
completionHandler(json.result.value, nil)
}
}
}
However, I got the following error. What do I miss?
{
error = {
code = 403;
errors = (
{
domain = global;
message = "Insufficient Permission";
reason = insufficientPermissions;
}
);
message = "Insufficient Permission";
};
}
This usually happens when you didn't include the scope of your authorization as stated in Youtube API Common request errors
forbidden (403) - insufficientPermissions The OAuth 2.0 token provided
for the request specifies scopes that are insufficient for accessing
the requested data.
One of the Youtube scopes you can use is
https://www.googleapis.com/auth/youtube.force-ssl
I agree with #noogui answer and here I want to explain how you can achieve this
- Check your scope you set and if it is kGTLRAuthScopeYouTubeReadonly then change it to kGTLRAuthScopeYouTubeUpload
private let scopes = [kGTLRAuthScopeYouTubeUpload]
And set this in to the viewDidLoad
GIDSignIn.sharedInstance().scopes = scopes
Here is the list of scopes you can check
kGTLRAuthScopeYouTube
kGTLRAuthScopeYouTubeForceSsl
kGTLRAuthScopeYouTubeUpload
kGTLRAuthScopeYouTubeReadonly
kGTLRAuthScopeYouTubeYoutubepartner
kGTLRAuthScopeYouTubeYoutubepartnerChannelAudit

How to get 401 error from servicestack swift client?

First our codes
let req = SignUp()
req.loginName = "abc#abc.com"
req.passWord = "xxx"
do{
let resp = try client.put(req) <---Where we had an error
} catch {
//some error handling
//.....
}
And then, when we input the correct information, everything is fine but when the login credential is wrong, we had expected to get an 401 error with a proper error message, we didn't. And when we traced it trying to find the origin of this, we had traced it back to JsonServicClient.swift (generated from ServiceStack swift plugin for Xcode 7.2), line 266.
public func send<T : JsonSerializable>(intoResponse:T, request:NSMutableURLRequest) throws -> T {
var response:NSURLResponse? = nil
var data = NSData()
do {
data = try NSURLConnection.sendSynchronousRequest(request, returningResponse: &response)
var error:NSError? = NSError(domain: NSURLErrorDomain, code: NSURLErrorUnknown, userInfo: nil)
if let dto = self.handleResponse(intoResponse, data: data, response: response!, error: &error) {
//^^^error here, response is null/can't be null
return dto
}
if let e = error {
throw e
}
return T()
} catch var ex as NSError? {
if let e = self.handleResponse(intoResponse, data: data, response: response!, error: &ex) {
return e
}
throw ex!
}
}
So here is the question, how to properly use ServiceStack swift plugin to get 401 error? When the service returns 400, everything is fine. This problem only happens when the servicestack server api returns 401. Which is by design, we supposed to return 401 when a user authentication fails.
This is a bug in Swift's sendSynchronousRequest which sometimes returns a null response which makes it impossible to determine what the server error response was. I've added a fix for this issue in this commit where it will now throw an unknown Error instead of segfaulting but as Swift doesn't return a HTTP Response we can't query it to return any more info about the error, e.g:
do {
try client.post(request)
} catch let responseError as NSError {
//Swift Bug: 401 returns an unitialized response so status is nil
if let status:ResponseStatus = responseError.convertUserInfo() {
}
}
To get the latest version you can either replace to latest JsonServiceClient.swift or delete the JsonServiceClient.swift and Add/Remove a new Service Reference which will download the latest version.
If you can, you can also switch to use the Async API's which doesn't have this issue:
client.postAsync(request)
.error { responseError in
let status:ResponseStatus = responseError.convertUserInfo()!
status.errorCode //= Unauthorized
}