App Settings sample updated to v1.7.1, devops pipelines added (#803)

* App Settings sample updated to v1.7.1, devops pipelines definitions added

* added build, release and config files for AzureDevops
This commit is contained in:
Velin Georgiev 2019-03-09 10:33:33 +00:00 committed by Vesa Juvonen
parent df46abc363
commit 45e81c8a6c
65 changed files with 7004 additions and 7113 deletions

3
package-lock.json generated
View File

@ -1,3 +0,0 @@
{
"lockfileVersion": 1
}

View File

@ -1,8 +1,12 @@
{
"@microsoft/generator-sharepoint": {
"version": "1.1.1",
"version": "1.7.1",
"libraryName": "react-app-settings",
"libraryId": "9573efb7-06d1-4134-aa8d-f6b4803d6096",
"environment": "spo"
"environment": "spo",
"isDomainIsolated": false,
"isCreatingSolution": false,
"packageManager": "npm",
"componentType": "webpart"
}
}

View File

@ -2,25 +2,17 @@
## Summary
This sample shows how appSettings.json file can be added and used within SharePoint Framewrok webparts similar to the Web.config / App.config key value app settings in .NET Framework projects.
That allows better DevOps and Continious Integration automation. Typescript module appSettings.d.ts is also added so it allows the json app settings to be imported to any webpart or react component with intellisense support.
This sample shows how AppSettings.ts file can be added and used within SharePoint Framewrok webparts similar to the Web.config / App.config key value app settings in .NET Framework projects.
That allows better DevOps and continuous integration (CI/CD) automation. The AppSettings.ts is transpiled/compiled with your SPFx solution which differs from the way the web.config. With .Net web.config file we would be able to update independently without the need of compiling DLLs. If that behavior is required, you can store your app settings in a SharePoint list and change them from there. However, that will have performance degradation over if the setting was part of the SPFx code where the logic can get a setting value in milliseconds.
![SPFx React app settings webpart](./assets/spfx-appSettings-json.PNG)
![SPFx React app settings webpart](./assets/app-settings-class.PNG)
### Easy to replace values in appSettings.json if DEV, QA, PROD environments.
### Replace values in AppSettings.ts if DEV, QA, PROD environments with Azure DevOps pipeline.
Since the appSettings.json is a known format, a DevOps guy can easily open it and add values according the environment then start `gulp build` process in an CI tool like VSTS, Jenkins.
### Gulp task added to verity that the appSettings.json and appSettings.d.ts match.
I have added appSettingsGulp.js with one gulp task in it. The task starts just before solution build or on watch to verify that all the app settings match in both appSettings.json and appSettings.d.ts. If they not match, then error is thrown so the CI tool is aware that the build failed.
### Keep the appSettings.json and appSettings.d.ts format as is.
Since the gulp task I created contains checks based on string operations, it is required that the appSettings.json and appSettings.d.ts are in format as provided and just key-pairs are added to the json file and respective just new properties are added to the IAppSettings interface in the appSettings.d.ts.
Azure DevOps pipelines configurations are included to demonstrate how the AppSettings.ts values can be changed per different environments. Please refer to the `devops/configurations` folder to see how this can be setup for your pipeline.
## Used SharePoint Framework Version
![drop](https://img.shields.io/badge/drop-1.4.1-green.svg)
![drop](https://img.shields.io/badge/drop-1.7.1-green.svg)
## Applies to
@ -44,6 +36,7 @@ Version|Date|Comments
-------|----|--------
0.0.1|August 03, 2017 | Initial commit
0.0.2|March 08, 2018 | Update to SPFx 1.4.1
0.0.3|March 03, 2019 | Update to SPFx 1.7.1
## Disclaimer
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -1,18 +1,27 @@
{
"$schema": "https://dev.office.com/json-schemas/spfx-build/config.2.0.schema.json",
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
"version": "2.0",
"bundles": {
"react-app-settings-bundle": {
"ice-cream-shop-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/reactAppSettings/ReactAppSettingsWebPart.js",
"manifest": "./src/webparts/reactAppSettings/ReactAppSettingsWebPart.manifest.json"
"entrypoint": "./lib/webparts/iceCreamShop/IceCreamShopWebPart.js",
"manifest": "./src/webparts/iceCreamShop/IceCreamShopWebPart.manifest.json"
}
]
},
"ice-cream-lorry-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/iceCreamLorry/IceCreamLorryWebPart.js",
"manifest": "./src/webparts/iceCreamLorry/IceCreamLorryWebPart.manifest.json"
}
]
}
},
"localizedResources": {
"reactAppSettingsStrings": "lib/webparts/reactAppSettings/loc/{locale}.js"
"IceCreamShopWebPartStrings": "lib/webparts/iceCreamShop/loc/{locale}.js",
"IceCreamLorryWebPartStrings": "lib/webparts/iceCreamLorry/loc/{locale}.js"
},
"externals": {}
}

View File

@ -1,3 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
"deployCdnPath": "temp/deploy"
}

View File

@ -1,4 +1,5 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
"workingDir": "./temp/deploy/",
"account": "<!-- STORAGE ACCOUNT NAME -->",
"container": "react-app-settings",

View File

@ -1,9 +1,11 @@
{
"$schema": "https://dev.office.com/json-schemas/spfx-build/package-solution.schema.json",
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": "react-app-settings-client-side-solution",
"id": "9573efb7-06d1-4134-aa8d-f6b4803d6096",
"version": "1.0.0.0"
"version": "1.0.0.0",
"includeClientSideAssets": true,
"skipFeatureDeployment": true
},
"paths": {
"zippedPackage": "solution/react-app-settings.sppkg"

View File

@ -1,4 +1,5 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
"port": 4321,
"initialPage": "https://localhost:5432/workbench",
"https": true,

View File

@ -1,45 +0,0 @@
{
"$schema": "https://dev.office.com/json-schemas/core-build/tslint.schema.json",
// Display errors as warnings
"displayAsWarning": true,
// The TSLint task may have been configured with several custom lint rules
// before this config file is read (for example lint rules from the tslint-microsoft-contrib
// project). If true, this flag will deactivate any of these rules.
"removeExistingRules": true,
// When true, the TSLint task is configured with some default TSLint "rules.":
"useDefaultConfigAsBase": false,
// Since removeExistingRules=true and useDefaultConfigAsBase=false, there will be no lint rules
// which are active, other than the list of rules below.
"lintConfig": {
// Opt-in to Lint rules which help to eliminate bugs in JavaScript
"rules": {
"class-name": false,
"export-name": false,
"forin": false,
"label-position": false,
"member-access": true,
"no-arg": false,
"no-console": false,
"no-construct": false,
"no-duplicate-case": true,
"no-duplicate-variable": true,
"no-eval": false,
"no-function-expression": true,
"no-internal-module": true,
"no-shadowed-variable": true,
"no-switch-case-fall-through": true,
"no-unnecessary-semicolons": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-with-statement": true,
"semicolon": true,
"trailing-comma": false,
"typedef": false,
"typedef-whitespace": false,
"use-named-parameter": true,
"valid-typeof": true,
"variable-name": false,
"whitespace": false
}
}
}

View File

@ -1,3 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
"cdnBasePath": "<!-- PATH TO CDN -->"
}

View File

@ -0,0 +1,19 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
"version": "2.0",
"bundles": {
"ice-cream-shop-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/iceCreamShop/IceCreamShopWebPart.js",
"manifest": "./src/webparts/iceCreamShop/IceCreamShopWebPart.manifest.json"
}
]
}
},
"localizedResources": {
"IceCreamShopWebPartStrings": "lib/webparts/iceCreamShop/loc/{locale}.js",
"IceCreamLorryWebPartStrings": "lib/webparts/iceCreamLorry/loc/{locale}.js"
},
"externals": {}
}

View File

@ -0,0 +1,5 @@
export class AppSettings {
public static readonly tenantUrl: string = "https://contoso-prod.sharepoint.com/";
public static readonly assetsUrl: string = "https://static2.sharepointonline.com/files/fabric/office-ui-fabric-core/9.6.1/css/fabric.min.css";
}

View File

@ -0,0 +1,20 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "4e6bdbca-212c-40b2-8de8-7d877b6c6db9",
"alias": "IceCreamShopWebPart",
"componentType": "WebPart",
"version": "*",
"manifestVersion": 2,
"requiresCustomScript": false,
"supportsFullBleed": true,
"preconfiguredEntries": [{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70",
"group": { "default": "Other" },
"title": { "default": "IceCreamShop" },
"description": { "default": "IceCreamShop description" },
"officeFabricIconFontName": "Page",
"properties": {
"description": "IceCreamShop"
}
}]
}

View File

@ -0,0 +1,67 @@
# Node.js
# Build a general Node.js project with npm.
# Add steps that analyze code, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript
trigger:
- master
jobs:
- job: DEV
pool:
vmImage: 'Ubuntu-16.04'
steps:
- task: NodeTool@0
inputs:
versionSpec: '8.x'
displayName: 'Install Node.js'
- script: |
npm i
gulp bundle --ship
gulp package-solution --ship
displayName: 'Build DEV package'
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: '$(Build.SourcesDirectory)/sharepoint/solution/react-app-settings.sppkg'
artifactName: 'DEV-sppkg'
displayName: 'Publish DEV env SPFx artifacts'
- job: PROD
pool:
vmImage: 'Ubuntu-16.04'
steps:
- task: NodeTool@0
inputs:
versionSpec: '8.x'
displayName: 'Install Node.js'
- task: CopyFiles@2
inputs:
sourceFolder: '$(Build.SourcesDirectory)/devops/configurations/PROD'
contents: '**/*'
targetFolder: '$(Build.SourcesDirectory)'
overWrite: true
- script: |
npm i
gulp bundle --ship
gulp package-solution --ship
displayName: 'Build PROD package'
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: '$(Build.SourcesDirectory)/sharepoint/solution/react-app-settings.sppkg'
artifactName: 'PROD-sppkg'
displayName: 'Publish PROD env SPFx artifacts'
- job: Release_Scripts
pool:
vmImage: 'Ubuntu-16.04'
steps:
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: '$(Build.SourcesDirectory)/devops/release/'
artifactName: 'Release-scripts'
displayName: 'Publish release scripts'

View File

@ -0,0 +1,72 @@
# Node.js
# Build a general Node.js project with npm.
# Add steps that analyze code, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript
trigger:
- master
jobs:
- job: DEV
pool:
vmImage: 'windows-2019'
steps:
- task: NodeTool@0
inputs:
versionSpec: '8.x'
displayName: 'Install Node.js'
- script: |
npm i
displayName: 'Npm install'
- script: |
gulp bundle --ship && gulp package-solution --ship
displayName: 'Build DEV package'
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: '$(Build.SourcesDirectory)\sharepoint\solution\react-app-settings.sppkg'
artifactName: 'DEV-sppkg'
displayName: 'Publish DEV env SPFx artifacts'
- job: PROD
pool:
vmImage: 'Ubuntu-16.04'
steps:
- task: NodeTool@0
inputs:
versionSpec: '8.x'
displayName: 'Install Node.js'
- task: CopyFiles@2
inputs:
sourceFolder: '$(Build.SourcesDirectory)/devops/configurations/PROD'
contents: '**/*'
targetFolder: '$(Build.SourcesDirectory)'
overWrite: true
- script: |
npm i
displayName: 'Npm install'
- script: |
gulp bundle --ship
gulp package-solution --ship
displayName: 'Build PROD package'
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: '$(Build.SourcesDirectory)/sharepoint/solution/react-app-settings.sppkg'
artifactName: 'PROD-sppkg'
displayName: 'Publish PROD env SPFx artifacts'
- job: Release_Scripts
pool:
vmImage: 'Ubuntu-16.04'
steps:
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: '$(Build.SourcesDirectory)/devops/release/'
artifactName: 'Release-scripts'
displayName: 'Publish release scripts'

View File

@ -0,0 +1,71 @@
# Node.js
# Build a general Node.js project with npm.
# Add steps that analyze code, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript
trigger:
- master
pool:
vmImage: 'Ubuntu-16.04'
steps:
- task: NodeTool@0
inputs:
versionSpec: '8.x'
displayName: 'Install Node.js'
- task: CopyFiles@2
inputs:
sourceFolder: '$(Build.SourcesDirectory)'
contents: '**/*'
targetFolder: '$(Build.ArtifactStagingDirectory)/dev'
displayName: 'CopyFiles to folder staging/dev'
- script: |
cd '$(Build.ArtifactStagingDirectory)/dev'
npm i
gulp bundle --ship
gulp package-solution --ship
displayName: 'Build DEV package'
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: '$(Build.ArtifactStagingDirectory)/dev/sharepoint/solution/react-app-settings.sppkg'
artifactName: 'DEV-sppkg'
displayName: 'Publish DEV env SPFx artifacts'
- task: CopyFiles@2
inputs:
sourceFolder: '$(Build.SourcesDirectory)'
contents: '**/*'
targetFolder: '$(Build.ArtifactStagingDirectory)/prod'
displayName: 'CopyFiles to folder staging/prod'
- task: CopyFiles@2
inputs:
sourceFolder: '$(Build.SourcesDirectory)/devops/configurations/PROD'
contents: '**/*'
targetFolder: '$(Build.ArtifactStagingDirectory)/prod'
overWrite: true
displayName: 'Apply PROD configuration (overrite dev env files)'
- script: |
cd '$(Build.ArtifactStagingDirectory)/prod'
rm package-lock.json
npm i
gulp bundle --ship
gulp package-solution --ship
displayName: 'Build PROD package'
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: '$(Build.ArtifactStagingDirectory)/prod/sharepoint/solution/react-app-settings.sppkg'
artifactName: 'PROD-sppkg'
displayName: 'Publish PROD env SPFx artifacts'
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: '$(Build.SourcesDirectory)/devops/release/'
artifactName: 'Release-scripts'
displayName: 'Publish release scripts'

View File

@ -0,0 +1,19 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
"version": "2.0",
"bundles": {
"ice-cream-shop-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/iceCreamShop/IceCreamShopWebPart.js",
"manifest": "./src/webparts/iceCreamShop/IceCreamShopWebPart.manifest.json"
}
]
}
},
"localizedResources": {
"IceCreamShopWebPartStrings": "lib/webparts/iceCreamShop/loc/{locale}.js",
"IceCreamLorryWebPartStrings": "lib/webparts/iceCreamLorry/loc/{locale}.js"
},
"externals": {}
}

View File

@ -0,0 +1,5 @@
export class AppSettings {
public static readonly tenantUrl: string = "https://contoso-prod.sharepoint.com/1";
public static readonly assetsUrl: string = "https://static2.sharepointonline.com/files/fabric/office-ui-fabric-core/9.6.1/css/fabric.min.css";
}

View File

@ -0,0 +1,20 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "4e6bdbca-212c-40b2-8de8-7d877b6c6db9",
"alias": "IceCreamShopWebPart",
"componentType": "WebPart",
"version": "*",
"manifestVersion": 2,
"requiresCustomScript": false,
"supportsFullBleed": true,
"preconfiguredEntries": [{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70",
"group": { "default": "Other" },
"title": { "default": "IceCreamShop" },
"description": { "default": "IceCreamShop description" },
"officeFabricIconFontName": "Page",
"properties": {
"description": "IceCreamShop"
}
}]
}

View File

@ -0,0 +1,25 @@
#!/bin/bash
SITE=$1
EMAIL=$2
PASS=$3
SCOPE=$4
ISDEV=$5
npm i -g @pnp/office365-cli
o365 version
# You have to run "o365 spo login https://contoso.sharepoint.com"
# to agree with the consent first time
sppkg_path="PROD-sppkg"
if [ ${ISDEV,,} = "true" ]
then
sppkg_path="DEV-sppkg"
fi
o365 spo login $SITE --authType password --userName $EMAIL --password $PASS
o365 spo app add --filePath "./_SPFx build/$sppkg_path/react-app-settings.sppkg" --appCatalogUrl $SITE --scope $SCOPE --overwrite
o365 spo app deploy --name react-app-settings.sppkg --appCatalogUrl $SITE --scope $SCOPE --skipFeatureDeployment

View File

@ -0,0 +1,19 @@
param(
$site,
$accessToken,
[ValidateSet("Site", "Tenant")]
$scope,
[switch]$isDev
)
# Install pnp powershell module on your release agent/vm
$sppkgPath = "PROD-sppkg"
if ($isDev.ToString) {
$sppkgPath = "DEV-sppkg"
}
Connect-PnPOnline $site -AccessToken $accessToken
Add-PnPApp -Path "./_SPFx build/$sppkgPath/react-app-settings.sppkg" -Scope $scope -Publish

View File

@ -3,10 +3,5 @@
const gulp = require('gulp');
const build = require('@microsoft/sp-build-web');
/**
* Checks if the app settings match in both the appSettings.json and appSettings.d.ts.
*/
const verifyAppSettings = require('./src/appSettingsGulp.js');
build.rig.addBuildTasks(verifyAppSettings);
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
build.initialize(gulp);

File diff suppressed because it is too large Load Diff

View File

@ -1,29 +1,31 @@
{
"name": "react-app-settings",
"version": "0.0.2",
"version": "0.0.3",
"private": true,
"engines": {
"node": ">=0.10.0"
},
"dependencies": {
"@microsoft/sp-core-library": "~1.4.1",
"@microsoft/sp-webpart-base": "~1.4.1",
"@types/webpack-env": ">=1.12.1 <1.14.0",
"react": "15.6.2",
"react-dom": "15.6.2",
"@types/react": "15.6.6",
"@types/react-dom": "15.5.6",
"@microsoft/sp-core-library": "1.7.1",
"@microsoft/sp-webpart-base": "1.7.1",
"@microsoft/sp-lodash-subset": "1.7.1",
"@microsoft/sp-office-ui-fabric-core": "1.7.1",
"@types/es6-promise": "0.0.33",
"@types/react": "16.4.2",
"@types/react-addons-shallow-compare": "0.14.17",
"@types/react-addons-test-utils": "0.14.15",
"@types/react-addons-update": "0.14.14",
"@types/react-addons-test-utils": "0.14.15"
"@types/react-dom": "16.0.5",
"@types/webpack-env": "1.13.1",
"react": "16.3.2",
"react-dom": "16.3.2"
},
"devDependencies": {
"@microsoft/sp-build-web": "~1.4.1",
"@microsoft/sp-module-interfaces": "~1.4.1",
"@microsoft/sp-webpart-workbench": "~1.4.1",
"@microsoft/sp-build-web": "1.7.1",
"@microsoft/sp-module-interfaces": "1.7.1",
"@microsoft/sp-webpart-workbench": "1.7.1",
"gulp": "~3.9.1",
"@types/chai": ">=3.4.34 <3.6.0",
"@types/mocha": ">=2.2.33 <2.6.0"
"ajv": "5.2.2"
},
"scripts": {
"build": "gulp bundle",

View File

@ -0,0 +1,5 @@
export class AppSettings {
public static readonly tenantUrl: string = "https://contoso-dev.sharepoint.com/";
public static readonly assetsUrl: string = "https://static2.sharepointonline.com/files/fabric/office-ui-fabric-core/4.1.0/css/fabric.min.css";
}

View File

@ -1,10 +0,0 @@
declare interface IAppSettings {
tenantUrl: string;
assetsUrl: string;
webSearchUrl: string;
}
declare module 'appSettings' {
const appSettings: IAppSettings;
export = appSettings;
}

View File

@ -1,5 +0,0 @@
{
"tenantUrl": "https://contoso.sharepoint.com/",
"assetsUrl": "https://static2.sharepointonline.com/files/fabric/office-ui-fabric-core/4.1.0/css/fabric.min.css",
"webSearchUrl": "https://www.bing.com/cr?IG=C027D8AA145E4C698CBAC863891F7ADF&CID=35725CD571236AE21C6D560670856B67&rd=1&h=Uonuek4ZZjPKBTXFL2TqY-P-mH_lVRUJpYqKN5gF-Qk&v=1&r=https%3a%2f%2fwww.bing.com%2fsearch%3fq%3dvelin%2bgeorgiev%2bblog&p=DevEx,5294.1"
}

View File

@ -1,98 +0,0 @@
'use strict';
var fs = require('fs'),
build = require('@microsoft/sp-build-web');
/**
* Verifies if the appSettings.json and appSettings.d.ts have the same appSetting keys.
*/
var verifyAppSettings = build.subTask('verify-app-settings', function(gulp, buildConfig, done) {
// will hold the keys from the appSettings.json file.
var appSettingsJsKeys = [];
// will hold the keys from the appSettings.d.ts file.
var appSettingsTsKeys = [];
/**
* Get all appSettings keys from the appSettings.d.ts text in javascript/nodejs array.
* Pure string operations.
*/
var getappSettingsTsKeys = function(appSettingsTsSettingsAsText, appSettingsTsKeysArray) {
var keyEndPos = appSettingsTsSettingsAsText.indexOf(":");
// end the recursion if no more `:`.
if(keyEndPos === -1) return appSettingsTsKeysArray;
// substring the appSetting key from the text.
var key = appSettingsTsSettingsAsText.substring(0, keyEndPos);
// add the appSetting key to the result array.
appSettingsTsKeysArray.push(key);
// exclude the key for the next call.
appSettingsTsSettingsAsText = appSettingsTsSettingsAsText.substring(appSettingsTsSettingsAsText.indexOf(";") + 1);
// call again for the next key.
getappSettingsTsKeys(appSettingsTsSettingsAsText, appSettingsTsKeysArray);
}
return new Promise(function(resolve, reject) {
/**
* Opens the appSettings.json file and pulls the appSetting keys in javascript array.
* Then calls operations on appSettings.d.ts.
*/
fs.readFile('./src/appSettings.json', 'utf8', function (err,data) {
if (err) { return reject(err); }
// remove some strings so we can parse to JSON, prue string manipulation.
var jsonAsString = data.replace(/(?:\r\n|\r|\n)/g, "").trim();
// appSettings.json keys to array.
appSettingsJsKeys = Object.keys(JSON.parse(jsonAsString));
/**
* Opens the appSettings.d.ts file and pulls the appSetting keys in javascript array.
* Then compares the appSettings.d.ts and the appSettings.json keys.
*/
return fs.readFile('./src/appSettings.d.ts', 'utf8', function (err,data) {
if (err) { return reject(err); }
// remove some strings, prue string manipulation.
var text = data.substring(data.indexOf("{") + 1, data.indexOf("}")).replace(/ /g,"").replace(/(?:\r\n|\r|\n)/g, "").trim();
// fill the appSettingsTsKeys array with the appSettings.d.ts keys.
getappSettingsTsKeys(text, appSettingsTsKeys);
// now we have two arrays with keys to compare.
// checks the appSettings.json for missing keys.
var l = appSettingsTsKeys.length;
while(l--) {
if(appSettingsJsKeys.indexOf(appSettingsTsKeys[l]) === -1)
{
build.error(`Key \"${appSettingsTsKeys[l]}\" not found in appSettings.json, but exists in appSettings.d.ts. Please fix your appSettings.`);
return reject();
}
}
// checks the appSettings.d.ts for missing keys.
l = appSettingsJsKeys.length;
while(l--) {
if(appSettingsTsKeys.indexOf(appSettingsJsKeys[l]) === -1)
{
build.error(`Key \"${appSettingsJsKeys[l]}\" not found in appSettings.d.ts, but exists in appSettings.json. Please fix your appSettings.`);
return reject();
}
}
return resolve();
});
});
});
});
exports.default = verifyAppSettings;

View File

@ -0,0 +1,3 @@
// A file is required to be in the root of the /src directory by the TypeScript compiler

View File

@ -0,0 +1,19 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "6a8e85b0-fb77-4206-aced-cdfc53a9b6c1",
"alias": "IceCreamLorryWebPart",
"componentType": "WebPart",
"version": "*",
"manifestVersion": 2,
"requiresCustomScript": false,
"preconfiguredEntries": [{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70",
"group": { "default": "Other" },
"title": { "default": "IceCreamLorry" },
"description": { "default": "IceCreamLorry description" },
"officeFabricIconFontName": "Page",
"properties": {
"description": "IceCreamLorry"
}
}]
}

View File

@ -0,0 +1,60 @@
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneTextField
} from '@microsoft/sp-webpart-base';
import * as strings from 'IceCreamLorryWebPartStrings';
import IceCreamLorry from './components/IceCreamLorry';
import { IIceCreamLorryProps } from './components/IIceCreamLorryProps';
export interface IIceCreamLorryWebPartProps {
description: string;
}
export default class IceCreamLorryWebPart extends BaseClientSideWebPart<IIceCreamLorryWebPartProps> {
public render(): void {
const element: React.ReactElement<IIceCreamLorryProps > = React.createElement(
IceCreamLorry,
{
description: this.properties.description
}
);
ReactDom.render(element, this.domElement);
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField('description', {
label: strings.DescriptionFieldLabel
})
]
}
]
}
]
};
}
}

View File

@ -0,0 +1,3 @@
export interface IIceCreamLorryProps {
description: string;
}

View File

@ -0,0 +1,74 @@
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
.iceCreamLorry {
.container {
max-width: 700px;
margin: 0px auto;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.row {
@include ms-Grid-row;
@include ms-fontColor-white;
background-color: $ms-color-themeDark;
padding: 20px;
}
.column {
@include ms-Grid-col;
@include ms-lg10;
@include ms-xl8;
@include ms-xlPush2;
@include ms-lgPush1;
}
.title {
@include ms-font-xl;
@include ms-fontColor-white;
}
.subTitle {
@include ms-font-l;
@include ms-fontColor-white;
}
.description {
@include ms-font-l;
@include ms-fontColor-white;
}
.button {
// Our button
text-decoration: none;
height: 32px;
// Primary Button
min-width: 80px;
background-color: $ms-color-themePrimary;
border-color: $ms-color-themePrimary;
color: $ms-color-white;
// Basic Button
outline: transparent;
position: relative;
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
-webkit-font-smoothing: antialiased;
font-size: $ms-font-size-m;
font-weight: $ms-font-weight-regular;
border-width: 0;
text-align: center;
cursor: pointer;
display: inline-block;
padding: 0 16px;
.label {
font-weight: $ms-font-weight-semibold;
font-size: $ms-font-size-m;
height: 32px;
line-height: 32px;
margin: 0 4px;
vertical-align: top;
display: inline-block;
}
}
}

View File

@ -0,0 +1,32 @@
import * as React from 'react';
import styles from './IceCreamLorry.module.scss';
import { IIceCreamLorryProps } from './IIceCreamLorryProps';
/**
* Import the AppSettings and use them to call apis.
*/
import { AppSettings } from '../../../AppSettings';
export default class IceCreamLorry extends React.Component<IIceCreamLorryProps, {}> {
public render(): React.ReactElement<IIceCreamLorryProps> {
return (
<div className={ styles.iceCreamLorry }>
<div className={ styles.container }>
<div className={ styles.row }>
<div className={ styles.column }>
<span className={ styles.title }>Welcome to the PnP ice cream lorry!</span>
<code>
<pre>
appSettings.tenantUrl: {AppSettings.tenantUrl}
</pre>
<pre>
appSettings.assetsUrl: {AppSettings.assetsUrl}
</pre>
</code>
</div>
</div>
</div>
</div>
);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 645 KiB

View File

@ -0,0 +1,10 @@
declare interface IIceCreamLorryWebPartStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
DescriptionFieldLabel: string;
}
declare module 'IceCreamLorryWebPartStrings' {
const strings: IIceCreamLorryWebPartStrings;
export = strings;
}

View File

@ -0,0 +1,19 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "4e6bdbca-212c-40b2-8de8-7d877b6c6db9",
"alias": "IceCreamShopWebPart",
"componentType": "WebPart",
"version": "*",
"manifestVersion": 2,
"requiresCustomScript": false,
"preconfiguredEntries": [{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70",
"group": { "default": "Other" },
"title": { "default": "IceCreamShop" },
"description": { "default": "IceCreamShop description" },
"officeFabricIconFontName": "Page",
"properties": {
"description": "IceCreamShop"
}
}]
}

View File

@ -7,16 +7,19 @@ import {
PropertyPaneTextField
} from '@microsoft/sp-webpart-base';
import * as strings from 'reactAppSettingsStrings';
import ReactAppSettings from './components/ReactAppSettings';
import { IReactAppSettingsProps } from './components/IReactAppSettingsProps';
import { IReactAppSettingsWebPartProps } from './IReactAppSettingsWebPartProps';
import * as strings from 'IceCreamShopWebPartStrings';
import IceCreamShop from './components/IceCreamShop';
import { IIceCreamShopProps } from './components/IIceCreamShopProps';
export default class ReactAppSettingsWebPart extends BaseClientSideWebPart<IReactAppSettingsWebPartProps> {
export interface IIceCreamShopWebPartProps {
description: string;
}
export default class IceCreamShopWebPart extends BaseClientSideWebPart<IIceCreamShopWebPartProps> {
public render(): void {
const element: React.ReactElement<IReactAppSettingsProps> = React.createElement(
ReactAppSettings,
const element: React.ReactElement<IIceCreamShopProps > = React.createElement(
IceCreamShop,
{
description: this.properties.description
}
@ -25,6 +28,10 @@ export default class ReactAppSettingsWebPart extends BaseClientSideWebPart<IReac
ReactDom.render(element, this.domElement);
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}

View File

@ -0,0 +1,3 @@
export interface IIceCreamShopProps {
description: string;
}

View File

@ -0,0 +1,74 @@
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
.iceCreamShop {
.container {
max-width: 700px;
margin: 0px auto;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.row {
@include ms-Grid-row;
@include ms-fontColor-white;
background-color: $ms-color-themeDark;
padding: 20px;
}
.column {
@include ms-Grid-col;
@include ms-lg10;
@include ms-xl8;
@include ms-xlPush2;
@include ms-lgPush1;
}
.title {
@include ms-font-xl;
@include ms-fontColor-white;
}
.subTitle {
@include ms-font-l;
@include ms-fontColor-white;
}
.description {
@include ms-font-l;
@include ms-fontColor-white;
}
.button {
// Our button
text-decoration: none;
height: 32px;
// Primary Button
min-width: 80px;
background-color: $ms-color-themePrimary;
border-color: $ms-color-themePrimary;
color: $ms-color-white;
// Basic Button
outline: transparent;
position: relative;
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
-webkit-font-smoothing: antialiased;
font-size: $ms-font-size-m;
font-weight: $ms-font-weight-regular;
border-width: 0;
text-align: center;
cursor: pointer;
display: inline-block;
padding: 0 16px;
.label {
font-weight: $ms-font-weight-semibold;
font-size: $ms-font-size-m;
height: 32px;
line-height: 32px;
margin: 0 4px;
vertical-align: top;
display: inline-block;
}
}
}

View File

@ -0,0 +1,32 @@
import * as React from 'react';
import styles from './IceCreamShop.module.scss';
import { IIceCreamShopProps } from './IIceCreamShopProps';
/**
* Import the AppSettings and use them to call apis.
*/
import { AppSettings } from '../../../AppSettings';
export default class IceCreamShop extends React.Component<IIceCreamShopProps, {}> {
public render(): React.ReactElement<IIceCreamShopProps> {
return (
<div className={ styles.iceCreamShop }>
<div className={ styles.container }>
<div className={ styles.row }>
<div className={ styles.column }>
<span className={ styles.title }>Welcome to PnP ice cream shop!</span>
<code>
<pre>
appSettings.tenantUrl: {AppSettings.tenantUrl}
</pre>
<pre>
appSettings.assetsUrl: {AppSettings.assetsUrl}
</pre>
</code>
</div>
</div>
</div>
</div>
);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

View File

@ -0,0 +1,7 @@
define([], function() {
return {
"PropertyPaneDescription": "Description",
"BasicGroupName": "Group Name",
"DescriptionFieldLabel": "Description Field"
}
});

View File

@ -0,0 +1,10 @@
declare interface IIceCreamShopWebPartStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
DescriptionFieldLabel: string;
}
declare module 'IceCreamShopWebPartStrings' {
const strings: IIceCreamShopWebPartStrings;
export = strings;
}

View File

@ -1,3 +0,0 @@
export interface IReactAppSettingsWebPartProps {
description: string;
}

View File

@ -1,26 +0,0 @@
{
"$schema": "../../../node_modules/@microsoft/sp-module-interfaces/lib/manifestSchemas/jsonSchemas/clientSideComponentManifestSchema.json",
"id": "4c2bfa08-5735-4af9-ae2f-de0b1a9dfe7d",
"alias": "ReactAppSettingsWebPart",
"componentType": "WebPart",
"version": "*", // The "*" signifies that the version should be taken from the package.json
"manifestVersion": 2,
/**
* This property should only be set to true if it is certain that the webpart does not
* allow arbitrary scripts to be called
*/
"safeWithCustomScriptDisabled": false,
"preconfiguredEntries": [{
"groupId": "4c2bfa08-5735-4af9-ae2f-de0b1a9dfe7d",
"group": { "default": "Under Development" },
"title": { "default": "ReactAppSettings" },
"description": { "default": "Shows how settings can be stored in appSettings.json file so they can easly be maintained for different envoirements like QEV, QA, STAGE, PROD" },
"officeFabricIconFontName": "Page",
"properties": {
"description": "ReactAppSettings"
}
}]
}

View File

@ -1,3 +0,0 @@
export interface IReactAppSettingsProps {
description: string;
}

View File

@ -1,52 +0,0 @@
.reactAppSettings {
.container {
max-width: 700px;
margin: 0px auto;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.row {
padding: 20px;
}
.listItem {
max-width: 715px;
margin: 5px auto 5px auto;
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.button {
// Our button
text-decoration: none;
height: 32px;
// Primary Button
min-width: 80px;
background-color: #0078d7;
border-color: #0078d7;
color: #ffffff;
// Basic Button
outline: transparent;
position: relative;
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
-webkit-font-smoothing: antialiased;
font-size: 14px;
font-weight: 400;
border-width: 0;
text-align: center;
cursor: pointer;
display: inline-block;
padding: 0 16px;
.label {
font-weight: 600;
font-size: 14px;
height: 32px;
line-height: 32px;
margin: 0 4px;
vertical-align: top;
display: inline-block;
}
}
}

View File

@ -1,38 +0,0 @@
import * as React from 'react';
import styles from './ReactAppSettings.module.scss';
import { IReactAppSettingsProps } from './IReactAppSettingsProps';
import { escape } from '@microsoft/sp-lodash-subset';
/**
* Import the appSettings and use them to call apis.
*/
import * as appSettings from 'appSettings';
export default class ReactAppSettings extends React.Component<IReactAppSettingsProps, {}> {
public render(): React.ReactElement<IReactAppSettingsProps> {
return (
<div className={styles.reactAppSettings}>
<div className={styles.container}>
<div className={`ms-Grid-row ms-bgColor-themeDark ms-fontColor-white ${styles.row}`}>
<div className="ms-Grid-col ms-u-lg10 ms-u-xl8 ms-u-xlPush2 ms-u-lgPush1">
<span className="ms-font-xl ms-fontColor-white">Welcome to SharePoint!</span>
<pre>
appSettings.tenantUrl: {appSettings.tenantUrl}
</pre>
<pre>
appSettings.assetsUrl: {appSettings.assetsUrl}
</pre>
<pre>
appSettings.webSearchUrl: {appSettings.webSearchUrl}
</pre>
<p className="ms-font-l ms-fontColor-white">{escape(this.props.description)}</p>
<a href="https://aka.ms/spfx" className={styles.button}>
<span className={styles.label}>Learn more</span>
</a>
</div>
</div>
</div>
</div>
);
}
}

View File

@ -1,10 +0,0 @@
declare interface IReactAppSettingsStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
DescriptionFieldLabel: string;
}
declare module 'reactAppSettingsStrings' {
const strings: IReactAppSettingsStrings;
export = strings;
}

View File

@ -1,9 +0,0 @@
/// <reference types="mocha" />
import { assert } from 'chai';
describe('ReactAppSettingsWebPart', () => {
it('should do something', () => {
assert.ok(true);
});
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 933 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 933 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,48 @@
{
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.2/MicrosoftTeams.schema.json",
"manifestVersion": "1.2",
"packageName": "IceCreamShop",
"id": "4e6bdbca-212c-40b2-8de8-7d877b6c6db9",
"version": "0.1",
"developer": {
"name": "SPFx + Teams Dev",
"websiteUrl": "https://products.office.com/en-us/sharepoint/collaboration",
"privacyUrl": "https://privacy.microsoft.com/en-us/privacystatement",
"termsOfUseUrl": "https://www.microsoft.com/en-us/servicesagreement"
},
"name": {
"short": "IceCreamShop"
},
"description": {
"short": "Shows how settings can be stored in appSettings.json file so they can easly be maintained for different envoirements like QEV, QA, STAGE, PROD",
"full": "Shows how settings can be stored in appSettings.json file so they can easly be maintained for different envoirements like QEV, QA, STAGE, PROD"
},
"icons": {
"outline": "tab20x20.png",
"color": "tab96x96.png"
},
"accentColor": "#004578",
"configurableTabs": [
{
"configurationUrl": "https://{teamSiteDomain}{teamSitePath}/_layouts/15/TeamsLogon.aspx?SPFX=true&dest={teamSitePath}/_layouts/15/teamshostedapp.aspx%3FopenPropertyPane=true%26teams%26componentId=4e6bdbca-212c-40b2-8de8-7d877b6c6db9",
"canUpdateConfiguration": true,
"scopes": [
"team"
]
}
],
"validDomains": [
"*.login.microsoftonline.com",
"*.sharepoint.com",
"*.sharepoint-df.com",
"spoppe-a.akamaihd.net",
"spoprod-a.akamaihd.net",
"resourceseng.blob.core.windows.net",
"msft.spoppe.com"
],
"webApplicationInfo": {
"resource": "https://{teamSiteDomain}",
"id": "00000003-0000-0ff1-ce00-000000000000"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 933 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -2,12 +2,14 @@
"compilerOptions": {
"target": "es5",
"forceConsistentCasingInFileNames": true,
"module": "commonjs",
"module": "esnext",
"moduleResolution": "node",
"jsx": "react",
"declaration": true,
"sourceMap": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"outDir": "lib",
"typeRoots": [
"./node_modules/@types",
"./node_modules/@microsoft"
@ -21,5 +23,12 @@
"dom",
"es2015.collection"
]
}
}
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules",
"lib"
]
}

View File

@ -1,3 +1,31 @@
{
"rulesDirectory": "./config"
"rulesDirectory": [],
"rules": {
"class-name": false,
"export-name": false,
"forin": false,
"label-position": false,
"member-access": true,
"no-arg": false,
"no-console": false,
"no-construct": false,
"no-duplicate-variable": true,
"no-eval": false,
"no-function-expression": true,
"no-internal-module": true,
"no-shadowed-variable": true,
"no-switch-case-fall-through": true,
"no-unnecessary-semicolons": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-with-statement": true,
"semicolon": true,
"trailing-comma": false,
"typedef": false,
"typedef-whitespace": false,
"use-named-parameter": true,
"variable-name": false,
"whitespace": false
},
"extends": "@microsoft/sp-tslint-rules/base-tslint.json"
}

View File

@ -1,11 +0,0 @@
// Type definitions for Microsoft ODSP projects
// Project: ODSP
/* Global definition for UNIT_TEST builds
Code that is wrapped inside an if(UNIT_TEST) {...}
block will not be included in the final bundle when the
--ship flag is specified */
declare const UNIT_TEST: boolean;
/* Global defintion for SPO builds */
declare const DATACENTER: boolean;

View File

@ -1 +0,0 @@
/// <reference path="@ms/odsp.d.ts" />