Office 365 addins as microservices on Docker containers


Office 365 Addins on Docker

This example shows how to run office 365 addins as microservices on Docker containers

https://github.com/spbreed/O365OnDocker

watch this video to see this in action

Below sections would be covered in this sample

  1. Why Docker?
  2. Prerequisites
  3. Configure Docker
  4. Docker Components
  5. Docker Commands
  6. Build NodeJS Base Image
  7. Build Office 365 NodeJS Microservice
  8. Dockerize Office365 Microservice
  9. Publish Microservice to Azure Docker Container
  10. How to run this sample

1. Why Docker?

  • Docker is an exciting new technology to build platform agnostic light weight apps/micro services/containers which can be provisioned, deployed, scaled faster than traditional VM’s.
  • Building Cloud hosted/provider hosted SharePoint/Office365 addins requires additional infrastructure to the mix.
  • Docker’s X-Plat CLI tools to provide continous integration and delivery thus making DevOps relatively simpler.
    For more details read docker docs

2. Prerequisites

  1. Windows 7 or Above
  2. Office 365 subscription
  3. Azure subscription
  4. Visual Studio Code

3. Configure Docker

  1. Install docker tool kit from docker docs
  2. Run docker quick start terminal
  3. To run this sample straightaway skip to “10. How to run this sample”.

4. Docker Components

  1. Docker Images > Blueprints of our application
  2. Docker Container > Created from docker images and are real instances of our application
  3. Docker Daemon > Building, running and distributing Docker containers
  4. Docker Client > Run on our local machine and connect to the daemon
  5. Docker Hub > A registry of docker images

5. Docker Commands

  1. $docker build
  2. $docker run
  3. $docker search
  4. $docker ps
  5. $docker images
  6. $docker rmi
  7. $docker pull
  8. $docker push
  9. $docker-machine env
  10. $docker-machine create
  11. $docker attach

6. Build NodeJS Base Image

FROM ubuntu:latest
RUN apt-get update
RUN apt-get install -y nodejs nodejs-legacy npm
RUN apt-get clean

7. Build Office 365 NodeJS Microservice

Run the sample Office 365 Add in from your local machine following this article

8. Dockerize Office365 addin

  • First install the required packages from package.json
COPY ./package.json src/
RUN cd src && npm install
  • Then copy all the source code
COPY . /src
  • Set working directory for docker daemon
WORKDIR src/
  • Set default comment on start
CMD ["npm","start"]
  • Now build the Docker Image and Run it
docker build -t o365addin-docker:0.1 .
docker run -d -p 80:3000 o365addin-docker:0.1
  • Change the AppID and Redirection URL’s in Azure and authHelper.js

  • Test the app in browser

9. Publish Microservice to Azure

1) Create SSL certs using Open SSL

openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout o365-docker.pem -out o365-docker.pem
openssl x509 -inform pem -in o365-docker.pem -outform der -out o365-docker.cer
openssl pkcs12 -export -out o365-docker.pfx -in o365-docker.pem -name "o365-docker Certificate"

2) Upload o365-docker.pem to Azure -> Settings -> Management Certificates -> Upload

3) Now Create a docker Microservice in Azure

docker-machine create -d azure --azure-subscription-id="d48ccdca-d4ab-4579-89fa-ed113033bf74" --azure-subscription-cert="o365-docker.pem" --azure-location="East US" o365ondocker

4) Connect to Azure Docker machine

eval "$(C:/Program\ Files/Docker\ Toolbox/docker-machine.exe env o365ondocker)"

5) Change the AppID and Redirection URL’s in Azure and authHelper.js

6) Build and Run the app

docker build -t o365addin-docker:0.1 .
docker run -d -p 80:3000 o365addin-docker:0.1

7) Test the app in browser

10. How to run this sample

1) Open Docker quick start terminal
2) Clone this repo

git clone https://github.com/spbreed/O365OnDocker.git

3) Update Office 365 App permissions and authhelper.js with Docker IP

docker-machine ip default

4) Build the docker image

docker build -t o365addin-docker:0.1 .

5) Run the docker container

docker run -d -p 80:3000 o365addin-docker:0.1
Advertisements

demystifying User Profile picture sync in #Office365


User profile pictures is one of the key factor for collaboration with in Office365. User Pictures are surfaced on almost of the service with in Office 365 such as Exchange Online, SharePoint Online, Lync (Skype) Online as well their client applications such as Outlook, Office, Skype for business etc.

To provide seamless experience to the end users its important to understand how it pictures are synchronized internally with in Office 365, Some notable issues and possible ways of automation.

Typical User Profile Sync process:

Most common scenario is of user profile picture sync process is through DirSync. When Dirsync completes the Synchronization to Azure AD, EXO gets a copy from AzureAD then SPO gets a copy from EXO. Though Outlook/Lync online is updated instantly, SPO might take up to 72 hours to show the picture.  EXO uses single image throughout the application. But SPO online uses three different images of single single user profile. For an example SPO ribbon uses a small image where as MySite/Delve uses a larger image.  So the User profile Service takes extra time to scale the images.

SPO Image variations:
200px * 200 px
_layouts/15/userphoto.aspx?size=L&username=firstname.lastname@spbreed.com

72px * 72px
_layouts/15/userphoto.aspx?size=M&username=firstname.lastname@spbreed.com

48px * 48px
_layouts/15/userphoto.aspx?size=S&username=firstname.lastname@spbreed.com

Issues:

Its not always a smooth ride from DirSync to SPO. If the process fails at any of the three stages, user’s may not get a profile pic in SPO. Here are some of the scenarios

  • AD images greater than 10KB or with wrong dimensions
  • Licensed for SPO but not EXO
  • Poor picture resolution in SPO Delve/Mysites

Resolution:

I often faced scenarios where the customers would require their pics across all the services in Office 365 in the next day. In those cases PowerShell is your friend. Below script uses Set-UserPhoto to upload high resolution pictures to EXO and ProfilePictureUploader project from Office365 Developer Patterns and Practices (PNP) to upload the Profile pictures to SharePoint Online.

Features:

Now the only thing you needed is the high resolution images on your local file share in “Firstname Lastname.JPG” format and configure the variables. This script

  • Saves the credentials securely for future use
  • Generates the UPN values from the User profile picture names. (Eg: John Doe.jpg => jdoe@domain.com)
  • Uploads the image to Exchange online
  • Scales the source image small (48px), medium (72px), Large (200 PX) images and uploads to SharePoint online
  • Archive the processed images

How to run this:

Download the Zip file and Configure the below values in configuration.xml

Configuration values:

For details please refer ProfilePictureUploader project

  1. tenantName: eg: spbreed.onmicrosoft.com
  2. pictureSourceCsv: C:\Scripts\UpdateProfilePics\Release\userlist.csv
  3. thumbs upload3Thumbs: True
  4. additionalProfileProperties:
  5. logFile path:“C:\Scripts\UpdateProfilePics\Release\log.txt”
  6. uploadDelay:500
  7. credFile:Location to store the O365 credentials
  8. psLogfile: Location to store PS logs
  9. O365AdminAccount:o365admin@spbreed.com (Global admin)
  10. localPhotosFolder:\\fs.corp.local\Public\Employee Photos\O365\ready
  11. localPhotosArchiveFolder:\\fs.corp.local\Public\Employee Photos\O365\archive
  12. domainName:spbreed
  13. exeLocation:C:\Scripts\UpdateProfilePics\Release (Location of ProfilePictureUploader Binaries)

Troubleshooting tips:

  •  O365AdminAccount should be a Office 365 global administrator
  • SetExecutionPolicy RemoteSigned should be enabled
  • Powershell modules for Azure AD and MSOL Sign in assistant should be installed.

 

AutoSPInstaller with #SP2016 on #Win2016 and #SQL2016


Since the release AutoSPInstaller by Brain Lalancette in 2010 , I am an ardent fan of this tool and used it in every other SharePoint deployment.

Not surprisingly AutoSPInstaller for SharePoint 2016 is out immediately after few days of #SP2016 Preview release.

Recently AutoSPinstaller online is release as well to generate the farm configuration file to run this tool.

For more details refer  https://autospinstaller.com/Guide

Recently I had a chance to install SP2016 on Win2016 and SQL 2016. AutoSpInstaller worked fine without any glitch and I was able to configure a single server farm in 4-5 hrs on Azure.

Here are the steps:

  1. Launch a VM using Win Server 2016 Technical preview 3 base image or install it on a Local VM2016_image_select
  2. Provision E:\ and F:\ for SQL and Logs
  3. Join the server to a domain
  4. Fully functional SharePoint install requires atleast 9 service accounts. Below PowerShell script can be used to automate Service account creation and to configure “No password expiry” policies. Update the CSV file with required values: Create-ADUsers.ps1
  5. Add replicating directory changes permission to spprofilesync account https://technet.microsoft.com/en-us/library/Hh296982.aspx
  6. Though SharePoint adds required firewall ports during setup, I used the below scripts to add required firewall ports in advance
    1. Create-FWRulesDB.ps1
    2. Create-FWRulesWFE.ps1
  7. Download and Install SQL server 2016 preview from https://www.microsoft.com/en-us/evalcenter/evaluate-sql-server-2016. Use SQLService accounts created in step 3 while configuring SQL services
  8. Pre configure app domain using http://sharepointchick.com/archive/2012/07/29/setting-up-your-app-domain-for-sharepoint-2013.aspx
  9. Download AutoSPSourcebuilder from http://autospsourcebuilder.codeplex.com/ and unzip in the same drive
  10. Download AutoSPInstaller from http://autospinstaller.codeplex.com/ and unzip in the same drive 
  11. Download #SP2016 preview from http://www.microsoft.com/en-us/download/details.aspx?id=48712 and mount it as 

    MOUNT-DISKIMAGE C:\ISO\SharePoint Server 2016 IT Preview.ISO

  12. Now navigate to AutoSPSourceBuilder  and run PS F:\AutoSPSourceBuilder> .\AutoSPSourceBuilder.ps1 -GetPrerequisites:$true  -Destination “F:\AutoSPInstaller\SP\2016”   to create SlipStreamed version of #SP2016

    AutoSourceBuilder

  13. Now prepare AutoSPInstallerInput.xml for SharePoint 2016. Its highly recommended use https://autospinstaller.com/  to generate this config today. But as of today (09/03/2016) online    tool to generate config file for SP2016 is not supported. Only notable difference wrt to AutoSPInstaller between #SP2013 and #SP2016 is the introduction MinRoles to simplify the deployment and scaling. For more details refer http://blogs.technet.com/b/wbaer/archive/2015/05/12/what-s-new-in-sharepoint-server-2016-installation-and-deployment.aspx
  14. Select <SingleServerFarm Provision=”true” /> for single server farm setup. Attached is the sample Config file for SP2016 for your reference. AutoSpInstallerInput.xml minroles
  15. Now run the .\AutoSPInstallerLaunch.bat. Installation will be a breeze.

portal

Jumpstart AngularJS app development for O365 & E2E testing using Protractor


BDD (Behaviour Driven Development) and TDD ( Test Driven Development) has become cornerstone for modern Javascript app development but adopting these development strategies to SharePoint/Office 365 is not a trivial task.

I was intrigued to discover #Protractor, an AngularJS testing framework. With client side MVC/MVVM frameworks getting popular among the developers, AngularJS is the premier framework to develop Office365/SharePoint 2013 apps. Integrating TDD methodologies to app development model will help developers to deliver maintainable, flexible and extensible solutions and interfaces.

Below is the RevealJS presentation on how to create AngularJS based SharePoint app and execute end to end testing using #Protractor. I would like to thank Andrew Connell for introducing  RevealJS and AngularJS Apps

http://spbreed.github.io/index.html

This presentation includes training videos and Source code to build SharePoint
1) Expenses AngularApp(SharePoint hosted App)
2) ExpensesAngularApp test (NodeJS project created using VS2013)
1. Expenses AngularApp:
– This is a SharePoint hosted app developed using popular angular framework.
– Utilizes best patterns and practises for angular framework based on HotTowel framework and Learning-Path- Manager-Code-Sample from Andrew Connell.
– Uses ShareCoffee Javascript library to make REST calls to SharePoint
– Available as a nuget package for easy install
2. ExpensesAngularApp test:
– This Project runs on NodeJS and executes End to End testing on Expenses Angular App
– Utilizes Protractor framework to do E2E testing
– Jasmine is used to write test specs
Source code: https://github.com/spbreed/spangularapp

Powershell to change UPN/Sign-in names for Office 365 users


Issue:

In many cases after running the DirSync, Office 365 users are created with user@domain.onmicrosoft.com as Primary UPN.

WAAD_1

Anlaysis:

Internally MS maintains two domains for federated users. One is “user@domain.com” which is the replica of on premise AD and other with “user@domain.onmicrosoft.com” on Azure AD.

It seems if the Dirsync is ran without “E-Mail” attribute on AD, Azure assigns “onmicrosoft.com” as the default domain and primary UPN. Once the initial DirSync is complete, adding “Email” value to AD user object wont help.

WAAD_2

Resolution:

As usual PowerShell comes to rescue. Idea is to use Windows Azure AD Module for Powershell and change the UPN of all the objects with .onmicrosoft UPN.

Steps:

1) Download and Install Azure AD modules from http://technet.microsoft.com/library/jj151815.aspx

2) Connect to WAAD  service using Office 365 admin credentials

3) Filter all the users ending with .onmicrosoft.com  as their UPN

4) Change the UPN using Set-MsolUserPrincipalName

5) Generate reports before and after updates

Download the scripts here: Update-msolUpn.ps1 

Powershell:

#
#.SYNOPSIS ./Update-msolUpn.ps1
#PowerShell script to automate this task to change the all Office 365 user accounts with user@domain.onmicrosoft.com. to user@domain.com
#Install Azure AD modules from http://technet.microsoft.com/library/jj151815.aspx before running this.
#

#Get Modules
$env:PSModulePath=$env:PSModulePath+";"+"C:\Program Files (x86)\Microsoft SDKs\Windows Azure\PowerShell"
$env:PSModulePath=$env:PSModulePath+";"+"C:\Windows\System32\WindowsPowerShell\v1.0\Modules\"
Import-Module Azure
Import-Module MSOnline

Get-Credential "office365admin@GSI.com" | Export-Clixml C:\GSI\scripts\GSIcred.xml #Store Credentials

#$count = 1 #For Testing the first result

$cred = Import-Clixml C:\GSI\scripts\GSIcred.xml

Connect-MsolService -Credential $cred

Get-MsolUser -All | Select-Object UserPrincipalName, Title, DisplayName, IsLicensed | export-csv –path C:\GSI\scripts\GSI_MSOL_Users_BeforeUpdate.csv

Get-MsolUser -All |
 Where { $_.UserPrincipalName.ToLower().EndsWith("onmicrosoft.com") } |
 ForEach {
 #if($count -eq 1) #For Testing the first result
 # {
 $upnVal = $_.UserPrincipalName.Split("@")[0] + "@GSI.com"
 Write-Host "Changing UPN value from: "$_.UserPrincipalName" to: " $upnVal -ForegroundColor Magenta
 Set-MsolUserPrincipalName -ObjectId $_.ObjectId -NewUserPrincipalName ($upnVal)
 $count++
 # }
 }

Get-MsolUser -All | Select-Object UserPrincipalName, Title, DisplayName, IsLicensed | export-csv –path C:\GSI\scripts\GSI_MSOL_Users_AfterUpdate.csv

 

Azure hosted SharePoint apps using AngularJS and WebAPI – Part 3


This is final post of a three part series on exploring Azure hosted SharePoint apps using AngularJS and WebAPI.

Part 1: Why Cloud and AppModel?

Part 2: SQLAzure data via WebAPI:

Part 3: Azure hosted apps using AngularJS and WebAPI

Why AngularJS:

 Traditionally managed server-side programming used to develop enterprise apps. But now this pattern is changing in the enterprise application development industry.  It’s a change away from logic on the server side, and towards logic on the client side. To address these complex business logic Javascript should evolve from just a language to make DOM manipulations to powerful client side MVC/MVVM framework. AngularJS framework makes this shift at ease.

AngularVSKnckout

 

 

 

There are also few other frameworks on the same course but AngularJS is a clear leader owing to these facts:

1)       Dependency injection:

This is huge and my personal favorite. Using this, custom modules can be injected just like adding assemblies on the server side code. This makes life little easier design MVC/MVVM based projects with separation of concern, modularity etc. An Analogy to server side code is represented below

Step38_ServerClient

 

 

2)      HTML templating:

Over the past few years, Single Page App’s (SPA’s) are gaining of a lot of traction from front end developers and end users. HTML templating feature in Angular makes it possible to easily switch the HTML in SPA’s.

 3)      Two way binding:

Fellow Silverlight developers can understand the complexity of two-way binding using propertychange events back in the old days. But AngularJS databinding makes this right OOB

 4)      Community support:

Google continues to develop and maintain the library. Large community of developers has embraced the framework so ample support is available on Stackoverflow and AngularJS official site.

5)      Advance error handling and logging features

Why AngularJS on Provider hosted App:

 Instead of using server side code on Provider hosted app, client side code is used for a sole reason to utilize AngularJS framework. Some of the advantages include

  •  Single Page App (SPA)
  • Clean URL for easy navigation
  • Responsive design
  • No server side code
  • Scalable for further enhancements

Future enhancements may include:

1)       Adding Chrome controls to give SharePoint look and feel

2)      Authentication for WebAPI

3)      Functions to delete/update O365 and WebApi Data. etc

 

Design of this app is based on Hot Towel app from John Papa and the pluralsight course from Andrew Connell.

It requires a bit of learning to understand the AngularJS. Go through these resources for deep understanding

http://pluralsight.com/training/courses/TableOfContents?courseName=building-sharepoint-apps-spa-angularjs

http://pluralsight.com/training/courses/TableOfContents?courseName=build-apps-angular-breeze

http://www.johnpapa.net/hot-towel-angular/

SPAngular APP:

1)       Create a new provider host app project in VS2013

2)      New Project -> App for SharePoint -> Provider Hosted App -> ASP.NET Web Forms Application -> Use Windows Azure Access Control Service ->finish

Step39_ProviderHosted

3)      Now click the web project -> Manage NuGet Packages

Nugets

4)      Search “hot towel” and install HotTowel.Angular.

Nugets_HotTowel

 

5)      Main benefit of using this project template is all the heavy lifting of implementing MVC project structure, logging, exception handling, angularJS, configuration comes OOB. This makes this bootstrap project easily extensible for custom functionalities.

Step25_App3

6)      By default App project creates Query String tokens to store URL’s of Appweb , hostweb etc for reference in the future.

Step35_AppXML

7)      AngularJS navigation is based on the URL manipulations. Its little hard to implement the navigation with these long URL’s. So the idea is to use a applauncherpage to store the Querystring values to cookies for future use and redirect to the main page with clean URL.

8)      Create applauncher.html and applauncher.js. Create an empty controller and all the JS references from index.html.


<!DOCTYPEhtml><!--// ** Step 1: Add AppLauncher:  **//-->

<htmldata-ng-app="app">

<head>

<title>App Launcher</title>

</head>

<bodydata-ng-controller="applauncher as vm">

 

<!--// ** Step 2: Add Angular libs:  **//-->

<!-- Vendor Scripts -->

<scriptsrc="../scripts/jquery-2.1.1.js"></script>

<scriptsrc="../Scripts/jquery.extensions.js"></script>

<scriptsrc="../scripts/jquery.cookie.js"></script>

<scriptsrc="../scripts/angular.js"></script>

<scriptsrc="../scripts/angular-animate.js"></script>

<scriptsrc="../scripts/angular-resource.js"></script>

<scriptsrc="../scripts/angular-route.js"></script>

<scriptsrc="../scripts/angular-sanitize.js"></script>

<scriptsrc="../scripts/bootstrap.js"></script>

<scriptsrc="../scripts/toastr.js"></script>

<scriptsrc="../scripts/moment.js"></script>

<scriptsrc="../scripts/ui-bootstrap-tpls-0.10.0.js"></script>

<scriptsrc="../scripts/spin.js"></script>

 

<!-- Bootstrapping -->

<scriptsrc="../app/app.js"></script>

<scriptsrc="../app/config.js"></script>

<scriptsrc="../app/config.exceptionHandler.js"></script>

<scriptsrc="../app/config.route.js"></script>

 

<!-- common Modules -->

<scriptsrc="../app/common/common.js"></script>

<scriptsrc="../app/common/logger.js"></script>

<scriptsrc="../app/common/spinner.js"></script>

 

<!-- common.bootstrap Modules -->

<scriptsrc="../app/common/bootstrap/bootstrap.dialog.js"></script>

 

<!-- app -->

<scriptsrc="../app/admin/admin.js"></script>

<scriptsrc="../app/dashboard/dashboard.js"></script>

<scriptsrc="../app/layout/shell.js"></script>

<scriptsrc="../app/layout/sidebar.js"></script>

 

<!--// ** Step 5: Create Add references **// -->

<!-- app Services -->

<scriptsrc="../app/applauncher.js"></script>

<scriptsrc="../app/services/spappcontext.js"></script>

 

</body>

</html>

 

 

9)      In the applauncher.js call spappcontext angular service.


// ** Step 3: Add AppLauncher JS:  **//

(function(){

'use strict';

var controllerId ='applauncher';

var app = angular.module('app');

app.controller(controllerId,['common','spappcontext', applauncher]);

 

function applauncher(common, spappcontext){

var getLogFn = common.logger.getLogFn;

var log = getLogFn(controllerId);

activate();

function activate(){

common.activateController([], controllerId)

.then(function(){ log('Activated App Launcher View');});

}

 

}

})();

10)   Create SPappcontext.js and create createSPAppContext()and loadSPAppContext() functions to read the query string and store it to cookies. This also redirects to \index.html, a Single page app.


(function(){

'use strict';

var serviceId ='spappcontext';

var app = angular.module('app');

app.service(serviceId,['common','$window','$location', spappcontext]);

function spappcontext(common, $window, $location){

var getLogFn = common.logger.getLogFn;

var log = getLogFn(serviceId);

var service =this;

var spweb ={

providerUrl:'',

SPAppWebUrl:'',

SPHostUrl:''

};

service.hostWeb = spweb;

init();

function init(){

var test = jQuery.getQueryStringValue('SPHostUrl');

if(decodeURIComponent(jQuery.getQueryStringValue('SPHostUrl'))==="undefined")

{

loadSPAppContext();

}

else

{

createSPAppContext();

}

}

function loadSPAppContext(){

log('loading spcontext cookie');

service.hostWeb.providerUrl = $.cookie("ProviderUrl");

service.hostWeb.SPAppWebUrl = $.cookie("SPAppWebUrl");

service.hostWeb.SPHostUrl = $.cookie("SPHostUrl");

}

function createSPAppContext(){

log('writing spcontext cookie');

var ProviderUrl = $window.location.protocol +"//"+ $window.location.host;

$.cookie("ProviderUrl", ProviderUrl,{ path:'/'});

var appWebUrl = decodeURIComponent(jQuery.getQueryStringValue('SPAppWebUrl'));

$.cookie("SPAppWebUrl", appWebUrl,{ path:'/'});

var url = decodeURIComponent(jQuery.getQueryStringValue('SPHostUrl'));

$.cookie('SPHostUrl', url,{ path:'/'});

$window.location.href = ProviderUrl +"/index.html";

}

}

})();

11)    Now add host web SP JS libraries to index.html to for make JSOM calls against host web.


<!--// ** Step 7: Add SharePoint libs for JSOM calls **// -->

<!--SharePoint-->

<scriptsrc="https://spbreed.sharepoint.com/sites/dev/_layouts/15/SP.RunTime.js"></script>

<scriptsrc="https://spbreed.sharepoint.com/sites/dev/_layouts/15/SP.js"></script>

<scriptsrc="https://spbreed.sharepoint.com/sites/dev/_layouts/15/SP.RequestExecutor.js"></script>

Now add the following code to web.config remove the static file issue on described here http://rainerat.spirit.de/2012/09/03/SharePoint-2013-App-HTTP-Error-405.0/

<!--// ** Step: 8: To Resolve IIS Static file issue**//-->

<system.webServer>

<modulesrunAllManagedModulesForAllRequests="true"/>

<handlers>

<addname="AspNetStaticFileHandler" path="*" verb="*" type="System.Web.StaticFileHandler" />

<addname="StaticHTML" path="*.html" verb="GET,HEAD,POST,DEBUG,TRACE" modules="StaticFileModule" resourceType="File" requireAccess="Read" />

</handlers>

</system.webServer>

[/xml]

12)   Now use JSOM to call host web and REST calls to WebAPI. $q.defer() objects are used to  pass promise to the calling function. This enables clients to complete the DOM structure without waiting for results.( Host web contains a demo contact list with title “DemoCustomer”
<pre>

// ** Step 6: Create Datacontext Service **//

(function(){

'use strict';

&nbsp;

var serviceId ='datacontext';

angular.module('app').factory(serviceId,['$rootScope','$resource','spappcontext','common', datacontext]);

&nbsp;

function datacontext($rootScope,$resource, spappcontext,common){

var $q = common.$q;

&nbsp;

&nbsp;

var service ={

getPeople: getPeople,

getMessageCount: getMessageCount,

getUserName: getUserName,

getCustomers: getCustomers,

getSPContacts: getSPContacts

};

&nbsp;

return service;

&nbsp;

function getCustomers(){

&nbsp;

var dfd = $q.defer();

var webApi = $resource('http://customerwebapi.cloudapp.net/api/Customer/:Id',{ Id:"@Id"},{ update:{ method:'PUT'}});

//   var webApi = $resource('http://localhost/AppWeb/api/Customer/:Id', { Id: "@Id" }, { update: { method: 'PUT' } });

webApi.query(function(data){

var customers = data;

customers.splice(4,847)

dfd.resolve(customers);

},function(error){

console.log(error);

dfd.reject(error);

});

return dfd.promise;

}

&nbsp;

function getUserName(){

&nbsp;

var dfd = $q.defer();

&nbsp;

// Standard function to get hostweb

var ctx =new SP.ClientContext(spappcontext.hostWeb.SPAppWebUrl);

var factory =new SP.ProxyWebRequestExecutorFactory(spappcontext.hostWeb.SPAppWebUrl);

ctx.set_webRequestExecutorFactory(factory);

var hostWebctx =new SP.AppContextSite(ctx, spappcontext.hostWeb.SPHostUrl);

&nbsp;

var appWeb = ctx.get_web();

var hostWeb = hostWebctx.get_web();

var currentAppWebUser = appWeb.get_currentUser();

var currentHostWebUser = hostWeb.get_currentUser();

&nbsp;

ctx.load(appWeb);

ctx.load(hostWeb);

ctx.load(currentAppWebUser);

ctx.load(currentHostWebUser);

&nbsp;

ctx.executeQueryAsync(function(){

var userName = currentAppWebUser.get_title();

dfd.resolve(userName);

},function(sender, args){

console.log(args.get_message()+" "+ args.get_stackTrace());

dfd.reject(args.get_message());

});

&nbsp;

return dfd.promise;

}

&nbsp;

function getSPContacts()

{

var dfd = $q.defer();

// Standard function to get hostweb

var ctx =new SP.ClientContext(spappcontext.hostWeb.SPAppWebUrl);

var factory =new SP.ProxyWebRequestExecutorFactory(spappcontext.hostWeb.SPAppWebUrl);

ctx.set_webRequestExecutorFactory(factory);

var hostWebctx =new SP.AppContextSite(ctx, spappcontext.hostWeb.SPHostUrl);

&nbsp;

var appWeb = ctx.get_web();

var hostWeb = hostWebctx.get_web();

&nbsp;

var ContactList = hostWeb.get_lists().getByTitle('DemoCustomer');

var contactListItems = ContactList.getItems(SP.CamlQuery.createAllItemsQuery());

ctx.load(ContactList);

ctx.load(contactListItems);

ctx.executeQueryAsync(function(){

var enumerator = contactListItems.getEnumerator();

var SPContacts =[];

while(enumerator.moveNext()){

var currentItem = enumerator.get_current();

SPContacts.push({"FirstName": currentItem.get_item('FirstName'),"LastName": currentItem.get_item('Title')});

}

var SPContactsObj = SPContacts;

&nbsp;

dfd.resolve(SPContactsObj);

},function(sender, args){

console.log(args.get_message()+" "+ args.get_stackTrace());

dfd.reject(args.get_message());

});

return dfd.promise;

}

}

})();

13)   Now modify dashboard.js to call data service functions defined above and add spinner while waiting for results.


(function(){

'use strict';

var controllerId ='dashboard';

angular.module('app').controller(controllerId,['common','datacontext', dashboard]);

&nbsp;

function dashboard(common, datacontext){

var getLogFn = common.logger.getLogFn;

var log = getLogFn(controllerId);

&nbsp;

var vm =this;

vm.news ={

title:'Hot Towel Angular',

description:'Hot Towel Angular is a SPA template for Angular developers.'

};

vm.messageCount =0;

vm.people =[];

vm.SPContacts =[];

vm.Customers =[];

vm.title ='Dashboard';

vm.busyMessage ='Please wait ...';

vm.isBusy =true;

vm.spinnerOptions ={

radius:40,

lines:7,

length:0,

width:30,

speed:1.7,

corners:1.0,

trail:100,

color:'#F58A00'

};

&nbsp;

activate();

&nbsp;

function activate(){

var promises =[getMessageCount(), getPeople(), getSPContacts(), getCustomers()];

common.activateController(promises, controllerId)

.then(function(){ log('Activated Dashboard View');});

}

&nbsp;

function getCustomers(){

toggleSpinner(true);

return datacontext.getCustomers().then(function(data){

toggleSpinner(false);

return vm.Customers = data;

});

}

&nbsp;

function getMessageCount(){

return datacontext.getMessageCount().then(function(data){

return vm.messageCount = data;

});

}

&nbsp;

function getPeople(){

return datacontext.getPeople().then(function(data){

return vm.people = data;

});

&nbsp;

}

&nbsp;

function getSPContacts(){

&nbsp;

return datacontext.getSPContacts().then(function(data){

return vm.SPContacts = data;

});

&nbsp;

}

&nbsp;

function toggleSpinner(on){ vm.isBusy = on;}

}

})();

14)   Now add remote endpoint http://customerwebapi.cloudapp.net to the appmanifest.xml

Step36_AppXML_Remoteendpoint

15)   Now navigate to /_layouts/15/appregnew.aspx to generate client Id and client secret. Note down these values.

Step28_AppregNew

16)   Now right click the web project and select publish to select a new publishing profile

Step29_PublishingProfile

17)   Seed the values of the ClientID and Client Secret noted earlier.

Step30_PublishingProfile

18)   Now select the created profile and deploy to Azure Website.

Step31_PublishingProfile

19)   Add a site name and click create

Step32_PublishingProfile

20)   Keep the default settings and click publish.

Step33_PublishingProfile

21)    Now package the app and upload the app to the Office 365 site.

Step34_PublishingProfile

22)   Now clicking the app will navigate to the provider hosted app with displays data from both Office 365 and WebAPI.

Step2_Responsive

This project is hosted in codeplex for download: https://spangular.codeplex.com/ 

Azure hosted SharePoint apps using AngularJS and WebAPI – Part 2


This is second post of a three part series on exploring Azure hosted SharePoint apps using AngularJS and WebAPI.

Part 1: Why Cloud and AppModel?

Part 2: SQLAzure data via WebAPI:

Part 3: Azure hosted apps using AngularJS and WebAPI

SQLAzure data via WebAPI:

This is the continuation of this post on creating Azure hosted enterprise apps using AngularJS and WebAPI.

WebAPI is a framework for building RESTful applications which enables client browsers to tap in to database using HTTP  GET, POST, DELETE requests.  This involves following steps:

Note: Following steps require valid Azure subscription. Microsoft offers free credits for MSDN subscribers. http://azure.microsoft.com/en-us/pricing/member-offers/msdn-benefits-details/

1)       Create a SQL server instance in Azure management portal https://manage.windowsazure.com -> SQL Databases -> New

Step4_SQLAzure1

 

2)      Create SQL Database. I am using AdventureWorksLT2012 database for this demoapp. This is downloaded from http://msftdbprodsamples.codeplex.com/releases/view/55330 . After restoring this app on local instance, create a database in SQLAzure with the same name.

Step5_SQLAzure2

3)      Once the database is created, the properties can be accessed by clicking through it.

Step6_SQLAzure3

4)      Now migrate the SQL DB from local instance to SQL azure using SQLAzure Migration Wizard. http://sqlazuremw.codeplex.com/

Step7_SQLAzure4

5)      SQLAzure supports only a subset of SQL features. Some of the limitations are listed here

http://msdn.microsoft.com/en-us/library/azure/ff394115.aspx . XML Schema collections are not supported so uncheck it.

Step8_SQLAzure5

6)      Now click next to generate SQL script to create database. Script displays non supported items in RED.

Fix these items and re-generate the script until all these errors are cleared. Follow this article to resolve some of the most common errors http://oakleafblog.blogspot.com/2010/01/using-sql-azure-migration-wizard-v313.html

Step9_SQLAzure6

7)      Now run this script on SQLAzure instance by keying the connection details of Azure DB created earlier

Step10_SQLAzure7

8)      Now connect to SQLAzure using SSMS

Step3_FinalDatabase

9)      Now to expose the database via WebAPI.

To do this install Azure SDK by following this link

http://msdn.microsoft.com/en-us/library/ff687127.aspx

Install Entity Framework Powertools to convert SQL objects to .net entities

http://visualstudiogallery.msdn.microsoft.com/72a60b14-1581-4b9b-89f2-846072eff19d

10)   Create new “Windows Azure Cloud Service” project. This creates a project with web role. Refer “Cloud Services” section in the article http://azure.microsoft.com/en-us/documentation/articles/fundamentals-introduction-to-azure/ to understand more on this. Basically this project creates a VM and IIS in Azure to host WebAPI web services on deployment.

Step11_WebApi1

 

11)    Now Right Click WebRole project -> Entity framework -> Reverse Engineer Code First and connect to SQLAzure DB created earlier. This will generate all the required entities.

Step12_WebApi2

12)   This generates table, table mapping and database context classes.

Step13_WebApi3

13)   Now create a controller of customer class using Scaffolding. Scaffold templates are used to generate code for basic CRUD operations for WebApi project against database using DB entity objects generated earlier.

 

Step14_WebApi4

Step15_WebApi5

 

Step16_WebApi6

 

14)   Add  navigate to App Start -> WebAPIConfig.cs and add the following code highlighted under step 2 to enable JSON


public static void Register(HttpConfiguration config)

{

// ** Step: 2: Enable JSON **//

// Web API configuration and services

var json = config.Formatters.JsonFormatter;

json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;

config.Formatters.Remove(config.Formatters.XmlFormatter);

// Web API routes

config.MapHttpAttributeRoutes();

&nbsp;

config.Routes.MapHttpRoute(

name: "DefaultApi",

routeTemplate: "api/{controller}/{id}",

defaults: new { id =RouteParameter.Optional }

);

// ** Step: 3: Enable CORS **//

&nbsp;

var cors =newEnableCorsAttribute("*", "*", "GET, POST, OPTIONS");

config.EnableCors(cors);

}

}

15)   Now Right Click WebRole Project -> Manage Nuget Packages ->Type Cors and install the WebAPI 2.1 cross origin support. This API is used to enable cross origin services for the WebAPI.

Step23_App1

Nugets_Cors

16)   Now add the code to highlighted under “step 3” enable CORS

17)   This will generate required WebApi controller to service Read, Delete and update operations. Right click WebApi project and publish.

Step17_WebApi7
Step18_WebApi8

18)   This will take few minutes to spin up a VM and publish these resources in Azure. Once the deployment is complete, data can be accessed from WebApi service.

Step19_WebApi9

19)   Now click the new cloud service created via Azure management portal

Step20_WebApi10

20)   Navigate to “Instances” tag and click “Connect” button to remote into VM

Step21_WebApi11

21)   Install “IP and Domain restrictions” role on the VM

Step26_IPRestriction

22)   Now open IIS and Navigate to WebAPI service web and click “IP Address and Domain restrictions” and add all the client IP’s which requires access to WebAPI.

 

Step27_IpFiltering

This project is hosted in codeplex for download: https://spangular.codeplex.com/