Tech tutorials Developing for Multiple Environments in SharePoint Online Test Environment
By Insight Editor / 24 May 2017 , Updated on 16 May 2019 / Topics: Modern infrastructure Application development
By Insight Editor / 24 May 2017 , Updated on 16 May 2019 / Topics: Modern infrastructure Application development
The transition from developing for SharePoint on premises to SharePoint Online contains many hurdles that need to be overcome: switching to client-side object model, implementing custom branding without custom master pages and writing web parts in JavaScript with the removal of managed code, just to name a few. However, one of the major hurdles is writing code that can isolate a SharePoint Online site collection as its own environment due to architectural differences with the on-premise version.
The normal practice for a SharePoint On-Premises farm is to have different servers for the various environments: development, Quality Assurance (QA) and production. Each of these environments resides on a different server and is independent. This means each has its own set of service applications developers can leverage (search, user profiles, managed metadata, etc.).
Based on this architecture, developers can make more assumptions about where a site collection will reside, which normally is at the root of a web application. Each environment will have at least one web application with a unique host URL to distinguish it from another. With this isolation, the information on one environment will not appear in another environment. The image below illustrates the environment separation.
Figure B: SharePoint Online test environments
Sometimes, a solution needs to know what environment it’s currently running in so it can adjust functionality, such as turning off caching in non-roduction environments. Since the host name is no longer unique between environments, there needs to be another way to determine where the code is running.
A way to achieve this is to dynamically change the contents of the code before it’s provisioned to a site. Using remote provisioning allows for this file manipulation. The example below shows how to use a .NET website with a web.config transforms on an AppSetting to perform this action.
Visual Studio has a built-in web.config transformer that allows for manipulation of the web.config file when it’s published under the different configurations. The default configurations of Debug and Release will be used for this example. The current configuration can be changed via the drop-down in the Visual Studio toolbar.
Figure C: Visual Studio Configuration drop-down
Below are the XML entries that will reside in their respective config file. In this case, the “value” attribute of the appSettings with the key of “Environment” will be adjusted based on the Visual Studio configuration selected.
Figure D: Visual Studio web config with transformations
Web.config
<configuration>
<appSettings>
<add key=“Environment” value=“DEV” />
</appSettings>
</configuration>
Web.Debug.config
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<appSettings>
<add key="Environment" value="QA" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
</appSettings>
</configuration>
Web.Release.Config
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<appSettings>
<add key="Environment" value="PROD" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
</appSettings>
</configuration>
Once these web.config manipulations are in place, the code that provisions files to the SharePoint site can be used to modify the contents of a JavaScript file to create an environment JSON object (CSGEnvironment) that can be used by code written for the site.
JavaScript file before modification
var CSGEnvironment = {
Environment: "{ENVIRONMENT}",
};
JavaScript file after modification for production deployment
var CSGEnvironment = {
Environment: "PROD",
};
C# code to modify file
private static System.IO.Stream ReplaceFileTokens(string filePath)
{
string fileText = System.IO.File.ReadAllText(filePath);
string currentEnvironment = WebConfigurationManager.AppSettings["Environment"];
fileText = fileText.Replace("{ENVIRONMENT}", "PROD");
byte[] bytes = System.Text.Encoding.ASCII.GetBytes(fileText);
System.IO.Stream ms = new System.IO.MemoryStream(bytes);
return ms;
As previously called out, all environments will share the SharePoint services on a SharePoint Online tenant. This section will discuss approaches that can be taken to keep your environments separated inside those shared services.
Often, a developer will use the Term Store and User Profile services as part of custom functionality. Common use cases would be cross-site collection navigation using a term set in the Term Store or user personalization on the site using a custom user profile property to store configurations.
When the development is done on premises, the same name can be used across environments for these items due to the isolation of the service applications between environments (see Figure A). In SharePoint Online, this is not the case since these service applications are shared (see Figure A), which means term sets and user profile properties should be created for each environment with different names.
To make sure the code is accessing the proper items in the services, the same environment identifier approach with web.config transforms, as outlined above, can be used to specify the correct name at deployment time.
JavaScript file Before Modification
var CSGEnvironment = {
Environment: "{ENVIRONMENT}",
TermSetGlobalNav: "{TERM_SET_GLOBAL_NAV}",
UserProfilePersonalize : "{USER_PROFILE_PERSONAL}"
};
JavaScript file after modification for production deployment
var CSGEnvironment = {
Environment: "PROD",
TermSetGlobalNav: "CSGGlobalNav-Prod",
UserProfilePersonalize : "CSGPersonalize-Prod"
};
When retrieving data from lists and search, developers need to actively specify where they would like the data to be pulled from. This section will provide some recommendations on how this can be accomplished.
Due to each environment being a separate site collection, it’s important to make sure list querying being done is at the correct scope. When using the REST API to get list data, the request URL can dynamically be adjusted prior to the REST call being made. The JavaScript code snippets below show how this can be done to generate a call at the host, site collection and web levels.
var restURLHost = getHostUrl() + "/_api/web/lists/getByTitle('ListTitle')/items?$select=ID,Title”
//restURLHost: https://company.sharepoint.com
var restURLSiteCollection = getSiteCollectionUrl() + "/_api/web/lists/getByTitle('ListTitle')/items?$select=ID,Title"
//restURLSiteCollection: https://company.sharepoint.com/sites/SiteCollectionName
var restURLWeb = getWebUrl() + "/_api/web/lists/getByTitle('ListTitle')/items?$select=ID,Title"
//restURLSiteCollection: https://company.sharepoint.com/sites/SiteCollection/Subsite
function getHostUrl() {
return window.location.protocol + "//" + window.location.host;
};
function getSiteCollectionUrl() {
return getHostUrl() + _spPageContextInfo.siteServerRelativeUrl;
};
function getWebUrl() {
return getHostUrl() + _spPageContextInfo.webServerRelativeUrl;
};
Since SharePoint Online only contains one search service, the data for all environments will reside in a single search index. Search is a powerful tool inside SharePoint to get data across site collections and sites. To make sure the proper data is being retrieved, the “Path” managed property can be used in the query to guarantee search returns the data from the desired sites. The syntax being used for the example is Keyword Query Language (KQL). The following query condition will only return data from the development site collection (/sites/dev).
Path:https://company.sharepoint.com/sites/dev
For custom code using the search API to get the current environment’s URL, the same functions in the query lists section can be used to dynamically change the query.
When using out-of-the-box search web parts, such as Search Results or Content Search, SharePoint provides a “Site” token that can be placed into the query to get the URL of the current site. The web part will replace that token with the correct information prior to running the query.
Path:{Site.URL}
The screenshot below is the edit query window for the out-of-the-box Content Search web part. The query has been modified to return any document or list item on the current site that contains the word “Benefits” in the title.
Figure E: Content query web part screenshot
There are many other tokens that can be used as well. See the links in the References section at the end of this article.
Understanding the architecture of your SharePoint online development environment at the start of development will eliminate some of the headaches encountered the first time code gets promoted to a QA or production environment. Here are the key items to remember: