Rest service for Posting comment to document and upload Document as attachment.

vishal1
edited December 22, 2016 in Documentum #1

Hello Team

I am currently working on native iOS app (using objective c) and I am using documentum rest services. Currently I am able to use get services of documentum for fetching data from Server. But now I need  to use post services of Documentum for sending data from iOS Device to Server. Following task I need to achieve using post services of Documentum.

  • Adding Comments on documents using post service.
  • Uploading Document as Attachment to server using post service.

I had read Previous discussion regarding how to Post data using post services of Documentum but No success yet. Please guide me how can I accomplish this? Or give me lead that I can search and Implement.

Thanks in Advance.

Comments

  • abc123
    edited December 4, 2016 #2

    Hi Vishal, REST team will return to you soon with the iOS sample. Comment service is supported since from Documentum REST 7.3, so please upgrade REST server accordingly.

  • Michyo_SONG
    edited December 4, 2016 #3

    Hi Vishal,

    As for your require, here is the sample codes using AFNetworking to consume Rest Service.


    //
    //  ViewController.m
    //  OCRest
    //
    //  Created by Song, Michyo on 12/5/16.
    //  Copyright © 2016 EMC. All rights reserved.
    //

    #import "ViewController.h"
    #import "AFNetworking.h"

    @interface ViewController ()

    @end

    @implementation ViewController

    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
      
      
        // Sample Code for adding Comments
        NSDictionary *params = @{ @city : @""};
        NSString *url = @""; // Please replace with your own url posted to, eg. http://localhost:8080/repositories/REPO/objects/1/comments
        [self postRequest: url params: params];
      
        // Sample Code for uploading document as multipart
        NSString *filePath = @""; // Plase replace with your own file path.
        NSString *multiPartUrl = @""; // Please replace with your own url posted to, eg. http://localhost:8080/repositories/REPO/objects/1
        if([[NSFileManager defaultManager] fileExistsAtPath:filePath])
        {
            NSData *data = [[NSFileManager defaultManager] contentsAtPath:filePath];
            NSString *fileName = @""; // Plase replace with your own file name.
            [self postMultipart :multiPartUrl fileName: fileName file: data progress:nil params:nil]; // There is no UI here, you can point this Progress View to your own one.
        }
            else
        {
            NSLog(@File not exits);
        }
    }

    // MARK: - Helper functions

    - (void)postRequest: (NSString*) url params:(NSDictionary*) parametersDictionary {
        AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc]initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
        manager.requestSerializer = [AFJSONRequestSerializer serializer];
        [manager.requestSerializer setValue:@application/json forHTTPHeaderField:@Content-Type];
      
        [manager
         POST:url parameters:parametersDictionary
         progress:nil // You can set here if you need it do in progress
         success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            NSLog(@success!); // Do for after success
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            NSLog(@error: %@", error); // Do for catch failure
        }];
    }

    - (void)postMultipart: (NSString*) url fileName: (NSString*)name file:(NSData*) fileData progress:(UIProgressView*) progressView params:(NSDictionary*) parametersDictionary {
        NSDictionary *meta = @{@propreties: @{@object_name: name}}; // You can modify this part to add more detail.
        NSData *metaData = [NSKeyedArchiver archivedDataWithRootObject:meta];
        NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@POST URLString: url parameters: parametersDictionary constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
            [formData appendPartWithFileData:metaData name:@metadata fileName:@metadata mimeType:@application/vnd.emc.documentum+json];
            [formData appendPartWithFileData:fileData name:@binary fileName:name mimeType:@text/plain]; // Your file to upload
          
        } error:nil];
      
        AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
      
        NSURLSessionUploadTask *uploadTask; // Create resumable task
        uploadTask = [manager
                      uploadTaskWithStreamedRequest:request
                      progress:^(NSProgress * _Nonnull uploadProgress) {
                          dispatch_async(dispatch_get_main_queue(), ^{
                              [progressView setProgress:uploadProgress.fractionCompleted]; //Update the progress view
                          });
                      }
                      completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
                          if (error) {
                              NSLog(@Error: %@", error);
                          } else {
                              NSLog(@%@ %@", response, responseObject);
                          }
                      }];
      
        [uploadTask resume];
    }

    @end

    What's more, REST team is now working on iOS application sample for customers in Swift and also have one legacy in Objective-C. In future, there may come iOS framework in Swift.

    Since iOS is moving to Swift, we recommend using Swift for iOS development so that we can support better.

    May you have any question, please feel free to leave message here and we will do our best to support.

    Thank you.

  • vishal1
    edited December 5, 2016 #4

    Thanks Michyo SONG for your quick reply and Help. One more question, For uploading document to server Which path you had selected for selecting document means is it from Application's Document Directory or iCloud saved document?

  • vishal1
    edited December 5, 2016 #5

    Thanks William for your reply. But from iOS device side implementation what things I want to do to post comments to server?

  • Michyo_SONG
    edited December 5, 2016 #6

    Hi Visha1,

    As mentioned in my previous code snippet, the following codes can help you get a document on your device with a full file path.


    [[NSFileManager defaultManager] fileExistsAtPath:filePath]

    If you want to get access to your Application's Document Directory, you can use NSBundle to help get paths.

    Since I have not yet communicated with iCloud, I am not very familiar with this technology. After searching online, I found this iClouddocumentSync library may help you solve this problem. Please go to its page to see if it can help.

    For your question to William about comments, here is the tutorial for commenting with REST service. You may find your answer here.

    PS: If this a new application created for your organization, we recommend Swift rather than Objective-C so that you can get better support.

  • vishal1
    edited December 6, 2016 #7

    Thanks Michyo Song for your great Help. its really helpful for next implementation.

  • vishal1
    edited December 13, 2016 #8

    Hi Michyo SONG

    As per your given Code Snippet I had tried to upload Document to server using AFNetworking. But it did Not work. I had use following code to upload document and I had got the Error. Please help me to solve this issue.

    My Code:

    - (IBAction)btnPostDocumentToServer:(id)sender {

    NSString *authStr = [NSString stringWithFormat:@%@:%@",[[NSUserDefaults standardUserDefaults] stringForKey:@username],[[NSUserDefaults standardUserDefaults] stringForKey:@password]];

        NSData *authData = [authStr dataUsingEncoding:NSUTF8StringEncoding];

        NSString *authValue = [NSString stringWithFormat:@Basic %@", [authData base64Encoding]];

        NSDictionary *headers = @{ @content-type: @multipart/form-data; boundary=---011000010111000001101001,

                                   @authorization: authValue,

                                   @accept: @application/json,

                                   @cache-control: @no-cache,

                                   @postman-token: @6e3d3d97-0ca2-db81-844d-56d6fd72e4ac };

        NSData *pdfData = [NSData dataWithContentsOfFile:path];

        [self postMultipart:@"http://devser1:9090/dctm-rest/repositories/TPMobile/folders/0b03445d800bfa1f" fileName:@temCodeiOS file:pdfData progress:nil params:headers];

    }

    - (void)postMultipart: (NSString*) url fileName: (NSString*)name file:(NSData*) fileData progress:(UIProgressView*) progressView params:(NSDictionary*) parametersDictionary {

        NSDictionary *meta = @{@properties:@{@r_object_type: @ecr_attachment, @document_id: @0903445d80210726,@object_name: @temCodeiOS.pdf,@a_content_type:@pdf,@subject:@new Doc from iOS Device}}; //@{@propreties: @{@object_name: name}}; // You can modify this part to add more detail.

        NSData *metaData = [NSKeyedArchiver archivedDataWithRootObject:meta];

        NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@POST URLString: url parameters: parametersDictionary constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {

            [formData appendPartWithFileData:metaData name:@metadata fileName:@metadata mimeType:@application/json];//@application/vnd.emc.documentum+json

            [formData appendPartWithFileData:fileData name:@binary fileName:name mimeType:@text/plain]; // Your file to upload

           

        } error:nil];

       

        AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];

       

        NSURLSessionUploadTask *uploadTask; // Create resumable task

        uploadTask = [manager

                      uploadTaskWithStreamedRequest:request

                      progress:^(NSProgress * _Nonnull uploadProgress) {

                          dispatch_async(dispatch_get_main_queue(), ^{

                              [progressView setProgress:uploadProgress.fractionCompleted]; //Update the progress view

                          });

                      }

                      completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {

                          if (error) {

                              NSLog(@Error: %@", error);

                          } else {

                              NSLog(@%@ %@", response, responseObject);

                          }

                      }];

       

        [uploadTask resume];

    }

    Error:

    Error: Error Domain=com.alamofire.error.serialization.response Code=-1011 "Request failed: unauthorized (401)" UserInfo={NSUnderlyingError=0x60800044ae00 {Error Domain=com.alamofire.error.serialization.response Code=-1016 "Request failed: unacceptable content-type: application/vnd.emc.documentum+json" UserInfo={com.alamofire.serialization.response.error.response=<NSHTTPURLResponse: 0x60000042d900> { URL: http://devser1:9090/dctm-rest/repositories/TPMobile/folders/0b03445d800bfa1f } { status code: 401, headers {

        "Content-Type" = "application/vnd.emc.documentum+json;charset=UTF-8";

        Date = "Tue, 13 Dec 2016 06:36:29 GMT";

        Server = "Apache-Coyote/1.1";

        "Transfer-Encoding" = Identity;

        "Www-Authenticate" = "Basic realm=\"com.emc.documentum.rest\"";

    } }, NSErrorFailingURLKey=http://devser1:9090/dctm-rest/repositories/TPMobile/folders/0b03445d800bfa1f, com.alamofire.serialization.response.error.data=<7b227374 61747573 223a3430 312c2263 6f646522 3a22455f 47454e45 52414c5f 41555448 454e5449 43415449 4f4e5f45 52524f52 222c226d 65737361 6765223a 22417574 68656e74 69636174 696f6e20 6661696c 65642e22 2c226465 7461696c 73223a22 46756c6c 20617574 68656e74 69636174 696f6e20 69732072 65717569 72656420 746f2061 63636573 73207468 69732072 65736f75 72636522 7d>, NSLocalizedDescription=Request failed: unacceptable content-type: application/vnd.emc.documentum+json}}, com.alamofire.serialization.response.error.response=<NSHTTPURLResponse: 0x60000042d900> { URL: http://devser1:9090/dctm-rest/repositories/TPMobile/folders/0b03445d800bfa1f } { status code: 401, headers {

        "Content-Type" = "application/vnd.emc.documentum+json;charset=UTF-8";

        Date = "Tue, 13 Dec 2016 06:36:29 GMT";

        Server = "Apache-Coyote/1.1";

        "Transfer-Encoding" = Identity;

        "Www-Authenticate" = "Basic realm=\"com.emc.documentum.rest\"";

    } }, NSErrorFailingURLKey=http://devser1:9090/dctm-rest/repositories/TPMobile/folders/0b03445d800bfa1f, com.alamofire.serialization.response.error.data=<7b227374 61747573 223a3430 312c2263 6f646522 3a22455f 47454e45 52414c5f 41555448 454e5449 43415449 4f4e5f45 52524f52 222c226d 65737361 6765223a 22417574 68656e74 69636174 696f6e20 6661696c 65642e22 2c226465 7461696c 73223a22 46756c6c 20617574 68656e74 69636174 696f6e20 69732072 65717569 72656420 746f2061 63636573 73207468 69732072 65736f75 72636522 7d>, NSLocalizedDescription=Request failed: unauthorized (401)}

  • Michyo_SONG
    edited December 14, 2016 #9

    Hi Vishal,

    This error message is telling you that your authentication is failed.

    The main cause is that the server did not received the correct headers since you may misunderstanding params with headers. Please refer to AFNetworking's official manual. There is a particular part for "Creating an upload task for a multi-part request, with progress". The params in the function would be pass as request body for request not headers as you thought.

    The correct passing headers should like:


    AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc]initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
        manager.requestSerializer = [AFJSONRequestSerializer serializer];
        [manager.requestSerializer setAuthorizationHeaderFieldWithUsername:@dmadmin password:@password];
    NSMutableURLRequest *request = [manager.requestSerializer multipartFormRequestWithMethod:@POST URLString: url parameters: parametersDictionary...





    The tail is omitted to save space.

    The main idea of code above is that you should pass the headers with manager.requestSerializer like the function postRequest showed in example code snippet. And also use the manager.requestSerializer to form your request not initialize a new one.

    Additionally, for metadata form data part, since it is defined as JSON format, the raw dictionary should be converted into JSON format and then turned into NSData. Sorry forget to warn you since I have my own converter locally.

    Meanwhile, please check your info.plist as this tutorial for detail to enable http request with authentication.

    Another point needs to pay attention is the version of Xcode and AFNetworking. While I am using AFNetworking 3.1 and Xcode 7.3 with iOS 9.3, everything runs well. There is a big gap between AFNetworking 3.1 and 2.X. Please check its release note for details.

    Finally, since I am also new to iOS for several months and I write Swift for application, I am afraid I could not answer all your question properly. I recommend the ios developer fomular and http://stackoverflow.com for your future questions. Hope they can help you.

    Best regards,

    Michyo

  • vishal1
    edited December 15, 2016 #10

    Hi Michyo

    As per your suggestion I had made changes in code and modify the Headers, for your info I'm using Xcode 8.1 iOS 10.1 simulator and AFNetwoking 3.0 version. But still i had same issue. Actually I did not use AFNetwroking Library before as I'm prefer to write my own request methods using NSURLSession Class. Its OK if you cannot answer my question properly Your hint and Little  help will be valuable and sufficient. Thanks For all your Valuable Support till now.

    My Modified code is following, I had made changes in header, please check and tell me where I am going wrong.

    Code:

    - (IBAction)btnPostDocumentToServer:(id)sender {

        NSString *filePath=[[NSBundle mainBundle] pathForResource:@demotpcode ofType:@pdf];

        NSData *pdfData = [NSData dataWithContentsOfFile:filePath];

        [self postMultipart:@"http://devser1:9090/dctm-rest/repositories/TPMobile/folders/0b03445d800bfa1f" fileName:@temCodeiOS file:pdfData progress:nil params:nil];

    }

    - (void)postMultipart: (NSString*) url fileName: (NSString*)name file:(NSData*) fileData progress:(UIProgressView*) progressView params:(NSDictionary*) parametersDictionary {

        NSDictionary *meta = @{@properties:@{@r_object_type: @ecr_attachment, @document_id: @0903445d80210726,@object_name: @temCodeiOS.pdf,@a_content_type:@pdf,@subject:@new Doc from iOS Device}}; //@{@propreties: @{@object_name: name}}; // You can modify this part to add more detail.   

        AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc]initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];

        manager.requestSerializer = [AFJSONRequestSerializer serializer];

        [manager.requestSerializer setAuthorizationHeaderFieldWithUsername:[[NSUserDefaults standardUserDefaults] stringForKey:@username] password:[[NSUserDefaults standardUserDefaults] stringForKey:@password]];

       

        NSMutableURLRequest *request=[manager.requestSerializer multipartFormRequestWithMethod:@POST URLString:url parameters:parametersDictionary constructingBodyWithBlock:^(id<AFMultipartFormData> formData){

            [formData appendPartWithFileData:metaData name:@metaData fileName:@metaData mimeType:@application/json];

            [formData appendPartWithFileData:fileData name:@binary fileName:name mimeType:@text/plain];

        } error:nil];

       

        NSURLSessionUploadTask *uploadTask; // Create resumable task

        uploadTask = [manager uploadTaskWithStreamedRequest:request progress:^(NSProgress * _Nonnull uploadProgress) {

            dispatch_async(dispatch_get_main_queue(), ^{

                [progressView setProgress:uploadProgress.fractionCompleted]; //Update the progress view

            });

        }

                                          completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {

                                              if (error) {

                                                  NSLog(@Error: %@", error);

                                              } else {

                                                  NSLog(@%@ %@", response, responseObject);

                                              }

                                          }];

       

        [uploadTask resume];

    }


    ERROR:


    Error: Error Domain=com.alamofire.error.serialization.response Code=-1011 "Request failed: bad request (400)" UserInfo={NSUnderlyingError=0x600000056350 {Error Domain=com.alamofire.error.serialization.response Code=-1016 "Request failed: unacceptable content-type: application/vnd.emc.documentum+json" UserInfo={com.alamofire.serialization.response.error.response=<NSHTTPURLResponse: 0x6080004318c0> { URL: http://devser1:9090/dctm-rest/repositories/TPMobile/folders/0b03445d800bfa1f } { status code: 400, headers {

        Connection = close;

        "Content-Type" = "application/vnd.emc.documentum+json;charset=UTF-8";

        Date = "Thu, 15 Dec 2016 06:46:20 GMT";

        Server = "Apache-Coyote/1.1";

        "Transfer-Encoding" = Identity;

    } }, NSErrorFailingURLKey=http://devser1:9090/dctm-rest/repositories/TPMobile/folders/0b03445d800bfa1f, com.alamofire.serialization.response.error.data=<7b227374 61747573……

  • vishal1
    edited December 15, 2016 #11

    Hi William

    If it is any sample of iOS (objective c) specifically on how to post data, comments, upload document to server please share that. I really need help for this.

    Thanks

    Vishal

  • abc123
    edited December 15, 2016 #12

    Hi Vishal, our team does not have expertise in Objective C. from our team will try the best to help you on the coding guide. But in case it's urgent for you, we'd recommend you to check with Apple dev community as well since HTTP calls in Objective C is a common issue.

  • Michyo_SONG
    edited December 22, 2016 #13

    Hi Vishal,

    For your code, an obvious problem is the format of request headers. I have mentioned in last comment:


    Additionally, for metadata form data part, since it is defined as JSON format, the raw dictionary should be converted into JSON format and then turned into NSData. Sorry forget to warn you since I have my own converter locally.

    Please notice that you should announce in the formdata part about meta data should in format of "application/json+vnd.emc.documentum". Hence, you should provide JSON format metadata other than RAW NSDictionary.

    For transforming JSON, you can try for this kind of JSONKit or any other library you like. Please refer to its manual for details.

    One more advice, since your error code is 400 for bad request, it will be helpful to open DEBUG mode of Rest Service and find out detail error messages. You can use breakpoint to dig out true reason for your failure.

    Wish you can solve this problem ASAP.

    Best regards,

    Michyo