none
[Node.js][Mobile Service] Easy tables - get SAS on insert RRS feed

Answers

  • You can install node modules using Visual Studio Online - open the console and type your npm commands directly. One small caveat is that it installs using an older version of npm (not a problem for most packages).

    • Marked as answer by ivan.icin Tuesday, March 15, 2016 4:48 PM
    • Unmarked as answer by ivan.icin Tuesday, March 15, 2016 10:57 PM
    • Marked as answer by ivan.icin Wednesday, March 16, 2016 1:22 AM
    Tuesday, March 15, 2016 12:06 AM
  • I've made it finally. It seems that SAS string is required. However it can be extracted from URL, so there doesn't have to be a column for that.

    All in all this is the code that works:

    var azureMobileApps = require('azure-mobile-apps');
    var table = azureMobileApps.table();
    var queries = require('azure-mobile-apps/src/query');
    module.exports = table;
    var azure = require('azure-storage');
    var storageName = '....';
    var storageKey = '....';
    
    
    table.insert(function (context) {
      	var blobService = azure.createBlobService(
    	    storageName,
    	    storageKey
    	);
    	
               blobService.createContainerIfNotExists('texts', {publicAccessLevel : 'blob'}, function(error, result, response){
                 if(!error){
                   console.log('Container exists and is private');
                 }
               });
    	
    	var sharedAccessPolicy = {
    	    AccessPolicy: {
    	        Permissions: azure.BlobUtilities.SharedAccessPermissions.WRITE,
    	        Expiry: new Date(new Date().getTime() + 5 * 60 * 1000)
    	    }
    	};
    	 var sasToken  = 
                    blobService.generateSharedAccessSignature('texts', 
                    context.item.Id, sharedAccessPolicy);
    	context.item.WebPath = blobService.getUrl('texts', context.item.Id, sasToken , true);
      return context.execute();
    });

                     StorageCredentials cred = new StorageCredentials(newTextPath.WebPath.Split('?')[1]);
    
                                var webUri = new Uri(newTextPath.WebPath);
    
                                CloudBlobContainer container = new CloudBlobContainer(new Uri(string.Format("https://{0}/{1}", webUri.Host, "texts")), cred);
    
                                using (var inputStream = await file.OpenReadAsync())
                                {
                                    CloudBlockBlob blobFromSASCredential =
                                        container.GetBlockBlobReference(newTextPath.Id);
                                    await blobFromSASCredential.UploadFromStreamAsync(inputStream);
                                }
    Thanks Dale, you were of great help!


    Check my apps: Share to Speech and File Cards


    • Edited by ivan.icin Wednesday, March 16, 2016 1:21 AM
    • Marked as answer by ivan.icin Wednesday, March 16, 2016 1:21 AM
    Wednesday, March 16, 2016 1:19 AM

All replies

  • You can directly use Azure storage node sdk in East Tables. To implement this requirement, we need install the azure storage node sdk module in the mobile apps application.

    Then you can try the following code snippet:

    var azureMobileApps = require('azure-mobile-apps');
    var table = azureMobileApps.table();
    var queries = require('azure-mobile-apps/src/query');
    var azure = require('azure-storage');
    var storageName = '<storage_name>';
    var storageKey = '<storage_key>';
    
    table.insert(function (context) {
      	var blobService = azure.createBlobService(
    	    storageName,
    	    storageKey
    	);
    	var sharedAccessPolicy = {
    	    AccessPolicy: {
    	        Permissions: azure.BlobUtilities.SharedAccessPermissions.WRITE,
    	        Expiry: new Date(new Date().getTime() + 5 * 60 * 1000)
    	    }
    	};
    	 var sasToken  = 
                    blobService.generateSharedAccessSignature(context.item.containerName, 
                    context.item.resourceName, sharedAccessPolicy);
    	context.item.sasUrl = blobService.getUrl(context.item.containerName, context.item.resourceName, sasToken , true);
      return context.execute();
    });
    Any further concern, please feel free to let me know.

    • Marked as answer by ivan.icin Monday, March 14, 2016 8:51 PM
    • Unmarked as answer by ivan.icin Tuesday, March 15, 2016 8:54 PM
    Monday, March 14, 2016 8:17 AM
    Moderator
  • Thanks for your answer. Azure supposed to be a turn key solution and mobile services were that at least for my project. It will be easier for me to recreate the project as a mobile service than to try to figure out on how to do all of the above and what implications it might have.

    Check my apps: Share to Speech and File Cards


    • Edited by ivan.icin Monday, March 14, 2016 8:52 PM
    Monday, March 14, 2016 8:51 PM
  • Hi Ivan,

    I may not understand your question properly - I'm not clear on how the older Azure Mobile Services product (as opposed to the newer Azure Mobile Apps) made accessing storage easier. The code that you used to access storage with a SAS should be very similar to what is required for Azure Mobile Apps.

    Could you post what existing code you have for accessing storage in your Mobile Service and I can explain how to do the same in Mobile Apps?

    Thanks,

    Dale Anderson (Microsoft)

    Monday, March 14, 2016 10:37 PM
  • Hi,

    I will have to test Mobile Services as I haven't tried them, but according to the article I have referenced all you have to do is copy paste the code from that article into the insert script.

    As far as it is explained in the solution above, in Easy tables you have to locally deploy node.js solution and install some packages and update them to the server and possibly do some more. Doing every step of that on Windows is a big hassle and I have after a lot of trials and errors pass some of them, but I don't feel like resolving remaining issues if I don't have to.


    Check my apps: Share to Speech and File Cards

    Monday, March 14, 2016 11:47 PM
  • Hi Ivan,

    You shouldn't need to work locally on Windows - Azure Mobile Apps provides a comprehensive online editing experience through Visual Studio Online.

    In EasyTables, there is an "Edit Script" button along the top row of buttons. Clicking on this will open Visual Studio Online and allow direct editing of code. Changes are immediately applied.

    For a sample workflow that allows you to work both online and locally, check out http://www.danderson00.com/2016/03/development-workflows-for-azure-mobile.html.

    Hope this helps!

    Monday, March 14, 2016 11:53 PM
  • Thanks, I ain't expert on the topic above (that's why I am asking), but the person that has answered seem to have some expertize and he has answered that I 'need install the azure storage node sdk module in the mobile apps application'. That's not possible in Visual Studio Online as far as I know. If it was just about copying the code it wouldn't be the problem obviously.

    Thanks for providing that link, I would like to know that for sure, but for now I'll still just try to avoid that hussle. I have read already dozen of articles and even when putting puzzles from them together it still doesn't completely work on my device. Node.js just wasn't built for Windows.


    Check my apps: Share to Speech and File Cards


    • Edited by ivan.icin Tuesday, March 15, 2016 12:04 AM
    Tuesday, March 15, 2016 12:03 AM
  • You can install node modules using Visual Studio Online - open the console and type your npm commands directly. One small caveat is that it installs using an older version of npm (not a problem for most packages).

    • Marked as answer by ivan.icin Tuesday, March 15, 2016 4:48 PM
    • Unmarked as answer by ivan.icin Tuesday, March 15, 2016 10:57 PM
    • Marked as answer by ivan.icin Wednesday, March 16, 2016 1:22 AM
    Tuesday, March 15, 2016 12:06 AM
  • After trying to make this, I see that in example I have referenced item gets from server Url and token. In your example it only gets Url. Obviously there is a token above, but there is one more step to associate it to the item that I don't know.

    Check my apps: Share to Speech and File Cards

    Tuesday, March 15, 2016 4:21 PM
  • I'm not overly familiar with the storage API, but the access token is embedded in the URL. The URL should be all you need to access the storage container.
    Tuesday, March 15, 2016 4:31 PM
  • After Dale persuaded me to try, I have installed Azure storage node SDK, and this doesn't seem to work.

    But suppose I made some errors during the installation, I have tried much more simple script which doesn't work either:

    var azureMobileApps = require('azure-mobile-apps');
    var table = azureMobileApps.table();
    var queries = require('azure-mobile-apps/src/query');
    
    table.insert(function (context) {
    	context.item.WebPath = 'new';
      return context.execute();
    });

    Just to note that there is a WebPath column in the table and a field in the definition of the class in C#. And it is a string.

    Item gets inserted in syncing from the local database, but the WebPath column stays empty.


    Check my apps: Share to Speech and File Cards


    • Edited by ivan.icin Tuesday, March 15, 2016 9:00 PM
    Tuesday, March 15, 2016 8:58 PM
  • If this is your table definition and it is stored in a separate file (like Easy Tables would create), it is missing the module.exports = table
    Tuesday, March 15, 2016 9:23 PM
  • You are right, but it ain't like that in the suggested code either. OK, now I receive SAS URL. Still I can't upload the file. Maybe I need a SAS as it is used in the article, or maybe I have altered something wrongly, but I don't think so, here is the code on the server and on the client:

    var azureMobileApps = require('azure-mobile-apps');
    var table = azureMobileApps.table();
    var queries = require('azure-mobile-apps/src/query');
    module.exports = table;
    var azure = require('azure-storage');
    var storageName = '....';
    var storageKey = '....';
    
    
    table.insert(function (context) {
      	var blobService = azure.createBlobService(
    	    storageName,
    	    storageKey
    	);
    	var sharedAccessPolicy = {
    	    AccessPolicy: {
    	        Permissions: azure.BlobUtilities.SharedAccessPermissions.WRITE,
    	        Expiry: new Date(new Date().getTime() + 5 * 60 * 1000)
    	    }
    	};
    	 var sasToken  = 
                    blobService.generateSharedAccessSignature('texts', 
                    context.item.Id, sharedAccessPolicy);
    	context.item.WebPath = blobService.getUrl('texts', context.item.Id, sasToken , true);
      return context.execute();
    });


                                var webUri = new Uri(newTextPath.WebPath);
    
                                CloudBlobContainer container = new CloudBlobContainer(new Uri(string.Format("https://{0}/{1}", webUri.Host, "texts")));
    
                                using (var inputStream = await file.OpenReadAsync())
                                {                                
                                    CloudBlockBlob blobFromSASCredential =
                                        container.GetBlockBlobReference(newTextPath.Id);
                                    await blobFromSASCredential.UploadFromStreamAsync(inputStream);
                                }

    And this is the exception:

    <?xml version="1.0" encoding="utf-16"?>
    <!--An exception has occurred. For more information please deserialize this message via RequestResult.TranslateFromExceptionMessage.-->
    <RequestResult>
      <HTTPStatusCode>404</HTTPStatusCode>
      <HttpStatusMessage>The specified resource does not exist.</HttpStatusMessage>
      <TargetLocation>Primary</TargetLocation>
      <ServiceRequestID>1e0d828d-0001-003b-5209-7fb348000000</ServiceRequestID>
      <ContentMd5 />
      <Etag />
      <RequestDate>Tue, 15 Mar 2016 23:25:02 GMT</RequestDate>
      <StartTime>Tue, 15 Mar 2016 22:25:01 GMT</StartTime>
      <EndTime>Tue, 15 Mar 2016 22:25:03 GMT</EndTime>
      <Error>
        <Code>ResourceNotFound</Code>
        <Message>The specified resource does not exist.
    RequestId:1e0d828d-0001-003b-5209-7fb348000000
    Time:2016-03-15T22:25:01.3733548Z</Message>
      </Error>
      <ExceptionInfo>
        <Type>StorageException</Type>
        <HResult>-2147467259</HResult>
        <Message>The specified resource does not exist.</Message>
        <Source>Microsoft.WindowsAzure.Storage</Source>
        <StackTrace>   at Microsoft.WindowsAzure.Storage.Core.Executor.Executor.&lt;ExecuteAsyncInternal&gt;d__c`1.MoveNext()</StackTrace>
      </ExceptionInfo>
    </RequestResult>
    P.S. I've unmarked some answers because they were marked at the point when I thought I don't need any more information and I don't want to try. I'll remark them properly when full solution comes in.


    Check my apps: Share to Speech and File Cards



    • Edited by ivan.icin Tuesday, March 15, 2016 10:59 PM
    Tuesday, March 15, 2016 10:33 PM
  • I'm on the Mobile Apps team and the storage SDK is not my strong point. However, it looks like the blob storage container has been created with either a different name or incorrect permissions.

    Keep in mind that the container must have been created (you can do this in code) and the container name must match the first argument to generateSharedAccessSignature exactly. Whether write access is permitted depends on the security policy set on the blob storage account and the access key provided (shared or full access).

    You can read more about using blob storage with SAS at the following URL: https://azure.microsoft.com/en-us/documentation/articles/storage-nodejs-how-to-use-blob-storage/.

    Tuesday, March 15, 2016 11:29 PM
  • Thanks for your help. I don't think it is that, I have added this code to insure there is a proper container, (and deleted any container before running it), and I still get the same message...

    	blobService.createContainerIfNotExists('texts', {publicAccessLevel : 'blob'}, function(error, result, response){
      if(!error){
        console.log('Container exists and is private');
      }
    });


    Check my apps: Share to Speech and File Cards

    Wednesday, March 16, 2016 12:09 AM
  • A 404 indicates that the storage account, blob container or blob does not exist. Without seeing how you storage account is configured, it is difficult for me to ascertain what is happening.

    I suggest you find the exact line of code that is returning with a 404 and validate the settings of that particular call as there are a number of calls to the storage account in the supplied code. If you are unable to solve the problem, please feel free to post in the Azure Storage forum at https://social.msdn.microsoft.com/Forums/azure/en-US/home?forum=windowsazuredata (this is the Mobile Apps forum).

    Wednesday, March 16, 2016 12:41 AM
  • I've made it finally. It seems that SAS string is required. However it can be extracted from URL, so there doesn't have to be a column for that.

    All in all this is the code that works:

    var azureMobileApps = require('azure-mobile-apps');
    var table = azureMobileApps.table();
    var queries = require('azure-mobile-apps/src/query');
    module.exports = table;
    var azure = require('azure-storage');
    var storageName = '....';
    var storageKey = '....';
    
    
    table.insert(function (context) {
      	var blobService = azure.createBlobService(
    	    storageName,
    	    storageKey
    	);
    	
               blobService.createContainerIfNotExists('texts', {publicAccessLevel : 'blob'}, function(error, result, response){
                 if(!error){
                   console.log('Container exists and is private');
                 }
               });
    	
    	var sharedAccessPolicy = {
    	    AccessPolicy: {
    	        Permissions: azure.BlobUtilities.SharedAccessPermissions.WRITE,
    	        Expiry: new Date(new Date().getTime() + 5 * 60 * 1000)
    	    }
    	};
    	 var sasToken  = 
                    blobService.generateSharedAccessSignature('texts', 
                    context.item.Id, sharedAccessPolicy);
    	context.item.WebPath = blobService.getUrl('texts', context.item.Id, sasToken , true);
      return context.execute();
    });

                     StorageCredentials cred = new StorageCredentials(newTextPath.WebPath.Split('?')[1]);
    
                                var webUri = new Uri(newTextPath.WebPath);
    
                                CloudBlobContainer container = new CloudBlobContainer(new Uri(string.Format("https://{0}/{1}", webUri.Host, "texts")), cred);
    
                                using (var inputStream = await file.OpenReadAsync())
                                {
                                    CloudBlockBlob blobFromSASCredential =
                                        container.GetBlockBlobReference(newTextPath.Id);
                                    await blobFromSASCredential.UploadFromStreamAsync(inputStream);
                                }
    Thanks Dale, you were of great help!


    Check my apps: Share to Speech and File Cards


    • Edited by ivan.icin Wednesday, March 16, 2016 1:21 AM
    • Marked as answer by ivan.icin Wednesday, March 16, 2016 1:21 AM
    Wednesday, March 16, 2016 1:19 AM
  • Awesome, glad you got things working!
    Wednesday, March 16, 2016 1:20 AM