From e49e747e818cb03cd8a5dfaa4e392e45aac5726e Mon Sep 17 00:00:00 2001 From: Dynamics 365 Commerce Date: Mon, 24 May 2021 22:07:53 +0000 Subject: [PATCH] release/9.30 - Commerce Sample Update --- .gitignore | 433 +++++------------- CODE_OF_CONDUCT.md | 9 - Pipeline/PowerShellScripts/CodeSign.ps1 | 11 + .../CopyFileToDestination.ps1 | 13 + Pipeline/PowerShellScripts/Pre-Build.ps1 | 6 + Pipeline/YAML_Files/build-pipeline.yml | 40 ++ README.md | 10 + SECURITY.md | 41 -- nuget.config | 11 + repo.props | 14 + .../ChannelDatabase/ChannelDatabase.csproj | 10 + .../Contoso.ExampleTable.ChannelDatabase.sql | 138 ++++++ .../CommerceRuntime/CommerceRuntime.csproj | 10 + .../Controllers/BoundController.cs | 63 +++ .../Controllers/UnboundController.cs | 43 ++ .../CommerceRuntime/Entities/ExampleEntity.cs | 73 +++ .../CreateExampleEntityDataRequest.cs | 37 ++ .../CreateExampleEntityDataResponse.cs | 35 ++ .../DeleteExampleEntityDataRequest.cs | 35 ++ .../DeleteExampleEntityDataResponse.cs | 35 ++ .../Messages/ExampleEntityDataRequest.cs | 21 + .../Messages/ExampleEntityDataResponse.cs | 38 ++ .../UpdateExampleEntityDataRequest.cs | 44 ++ .../UpdateExampleEntityDataResponse.cs | 35 ++ .../RequestHandlers/ExampleDataService.cs | 174 +++++++ .../Triggers/DefinePosExtensionTrigger.cs | 61 +++ .../CustomizationPackage.props | 12 + .../Installer/Extension.config | 6 + .../Installer/ScaleUnit.Installer.csproj | 28 ++ .../Dialogs/Create/ExampleCreateDialog.html | 25 + .../Create/ExampleCreateDialogModule.ts | 88 ++++ .../Dialogs/Display/PingResultDialog.html | 25 + .../Dialogs/Display/PingResultDialogModule.ts | 63 +++ .../Dialogs/Edit/ExampleEditDialog.html | 25 + .../Dialogs/Edit/ExampleEditDialogModule.ts | 93 ++++ src/ScaleUnitSample/POS/POS.csproj | 16 + .../Resources/Strings/en-US/resources.resjson | 38 ++ .../Search/NavigateToExampleViewCommand.ts | 42 ++ .../POS/Views/ExampleView.html | 13 + src/ScaleUnitSample/POS/Views/ExampleView.ts | 152 ++++++ .../POS/Views/ExampleViewModel.ts | 139 ++++++ src/ScaleUnitSample/POS/manifest.json | 50 ++ src/ScaleUnitSample/POS/tsconfig.json | 3 + src/ScaleUnitSample/ScaleUnit.sln | 49 ++ .../ScaleUnit/ScaleUnit.csproj | 23 + 45 files changed, 1950 insertions(+), 380 deletions(-) delete mode 100644 CODE_OF_CONDUCT.md create mode 100644 Pipeline/PowerShellScripts/CodeSign.ps1 create mode 100644 Pipeline/PowerShellScripts/CopyFileToDestination.ps1 create mode 100644 Pipeline/PowerShellScripts/Pre-Build.ps1 create mode 100644 Pipeline/YAML_Files/build-pipeline.yml delete mode 100644 SECURITY.md create mode 100644 nuget.config create mode 100644 repo.props create mode 100644 src/ScaleUnitSample/ChannelDatabase/ChannelDatabase.csproj create mode 100644 src/ScaleUnitSample/ChannelDatabase/Contoso.ExampleTable.ChannelDatabase.sql create mode 100644 src/ScaleUnitSample/CommerceRuntime/CommerceRuntime.csproj create mode 100644 src/ScaleUnitSample/CommerceRuntime/Controllers/BoundController.cs create mode 100644 src/ScaleUnitSample/CommerceRuntime/Controllers/UnboundController.cs create mode 100644 src/ScaleUnitSample/CommerceRuntime/Entities/ExampleEntity.cs create mode 100644 src/ScaleUnitSample/CommerceRuntime/Messages/CreateExampleEntityDataRequest.cs create mode 100644 src/ScaleUnitSample/CommerceRuntime/Messages/CreateExampleEntityDataResponse.cs create mode 100644 src/ScaleUnitSample/CommerceRuntime/Messages/DeleteExampleEntityDataRequest.cs create mode 100644 src/ScaleUnitSample/CommerceRuntime/Messages/DeleteExampleEntityDataResponse.cs create mode 100644 src/ScaleUnitSample/CommerceRuntime/Messages/ExampleEntityDataRequest.cs create mode 100644 src/ScaleUnitSample/CommerceRuntime/Messages/ExampleEntityDataResponse.cs create mode 100644 src/ScaleUnitSample/CommerceRuntime/Messages/UpdateExampleEntityDataRequest.cs create mode 100644 src/ScaleUnitSample/CommerceRuntime/Messages/UpdateExampleEntityDataResponse.cs create mode 100644 src/ScaleUnitSample/CommerceRuntime/RequestHandlers/ExampleDataService.cs create mode 100644 src/ScaleUnitSample/CommerceRuntime/Triggers/DefinePosExtensionTrigger.cs create mode 100644 src/ScaleUnitSample/CustomizationPackage.props create mode 100644 src/ScaleUnitSample/Installer/Extension.config create mode 100644 src/ScaleUnitSample/Installer/ScaleUnit.Installer.csproj create mode 100644 src/ScaleUnitSample/POS/Controls/Dialogs/Create/ExampleCreateDialog.html create mode 100644 src/ScaleUnitSample/POS/Controls/Dialogs/Create/ExampleCreateDialogModule.ts create mode 100644 src/ScaleUnitSample/POS/Controls/Dialogs/Display/PingResultDialog.html create mode 100644 src/ScaleUnitSample/POS/Controls/Dialogs/Display/PingResultDialogModule.ts create mode 100644 src/ScaleUnitSample/POS/Controls/Dialogs/Edit/ExampleEditDialog.html create mode 100644 src/ScaleUnitSample/POS/Controls/Dialogs/Edit/ExampleEditDialogModule.ts create mode 100644 src/ScaleUnitSample/POS/POS.csproj create mode 100644 src/ScaleUnitSample/POS/Resources/Strings/en-US/resources.resjson create mode 100644 src/ScaleUnitSample/POS/ViewExtensions/Search/NavigateToExampleViewCommand.ts create mode 100644 src/ScaleUnitSample/POS/Views/ExampleView.html create mode 100644 src/ScaleUnitSample/POS/Views/ExampleView.ts create mode 100644 src/ScaleUnitSample/POS/Views/ExampleViewModel.ts create mode 100644 src/ScaleUnitSample/POS/manifest.json create mode 100644 src/ScaleUnitSample/POS/tsconfig.json create mode 100644 src/ScaleUnitSample/ScaleUnit.sln create mode 100644 src/ScaleUnitSample/ScaleUnit/ScaleUnit.csproj diff --git a/.gitignore b/.gitignore index dfcfd56f..102bb454 100644 --- a/.gitignore +++ b/.gitignore @@ -1,350 +1,123 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore - -# User-specific files -*.rsuser -*.suo +_ReSharper* +[Bb]in +obj +objd +bin +bld +pkg +/schemas/ +out/ +target/ +tmp/ +.vs +App_Data *.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Mono auto generated files -mono_crash.* - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -[Aa][Rr][Mm]/ -[Aa][Rr][Mm]64/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ -[Ll]ogs/ - -# Visual Studio 2015/2017 cache/options directory +*.sln.cache +*.suo +TestResults +[Tt]humbs.db +buildd.* +build.(err|log|wrn|trc|prf|evt|dbb) +*.log +*.bak +OACRTemp/ +build_logs/ +lock +/public/inc/bldver.* +/public/inc/sources.ver .vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ +/Symbols +/drop +/.corext/.pkg/ +/.corext/gen/ +/.corext/cache +versionsmk.inc +Logs -# Visual Studio 2017 auto generated files -Generated\ Files/ +# Weird MSBuild output folders +[0-9] -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* +# msbuild logs +msbuild.log +msbuild.err +msbuild.wrn -# NUnit -*.VisualState.xml -TestResult.xml -nunit-*.xml +# bad tlb/chm generators in nmake tree +*.tlb +*.chm +# The following file is a part of the sdk and must be an exception to the above rule, meaning it will be included. +!CommerceRuntimeMessages.chm -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c +# dumb silverlight +ClientBin/ -# Benchmark Results -BenchmarkDotNet.Artifacts/ +# dumb azure +*.build.csdef +csx/ -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ +# Generated C# files +*.g.cs +!src/SampleExtensions/ShoppingApp/**/*.g.cs +*.projhash +*.projhash.userData +*.projhash.userData.assemblies +*.slnhash +*.slnhash.userData +*.slnhash.userData.assemblies + +# Ignore other generated file types, too +*.g.ts +*.g.xml # StyleCop -StyleCopReport.xml +StyleCop.Cache -# Files built by Visual Studio -*_i.c -*_p.c -*_h.h -*.ilk -*.meta -*.obj -*.iobj -*.pch -*.pdb -*.ipdb -*.pgc -*.pgd +# Configs and transient build assets +.config/.inc/bldver.cpp +.config/.inc/bldver.cs +.config/.inc/bldver.h +.config/.inc/bldver.vb +.config/.inc/sources.ver +.config/.inc/version.htm +.config/.inc/version.sql +.config/.inc/version.json +.config/.inc/version.txt +.gen/ +build/.gen_oacr/ +build/oacr_exclude.ini +*.tslint +*.d.ts +*.map +*.generated *.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*_wpftmp.csproj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc +*.js +/src/OnlineStore/Ecommerce.Sdk.Controls/Common/Scripts.js -# Chutzpah Test files -_Chutzpah* +# Build and development artifiacts +/VersionGeneration +tslint.out +TypeScripttslintFileListArgs.txt +/.config/dev.packages.props +/src/Provisioning/ProvisioningDatabase/ProvisioningDatabase.refactorlog +.packages +phantomjs-license.txt -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# Visual Studio Trace Files -*.e2e - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# NuGet Symbol Packages -*.snupkg -# The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* -# except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files +# POS Build Artifacts AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx -*.appxbundle -*.appxupload +bld/ +schemas/ +dist/ +devDependencies/ -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!?*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm +# Certificate files *.pfx -*.publishsettings -orleans.codegen.cs -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk +# Nuget package cache +/src/References +/ComponentScenarios/Pos/Dependencies/ -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ +# Nuget package creation temps +/nu/t -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm -ServiceFabricBackup/ -*.rptproj.bak - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings -*.rptproj.rsuser -*- [Bb]ackup.rdl -*- [Bb]ackup ([0-9]).rdl -*- [Bb]ackup ([0-9][0-9]).rdl - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# CodeRush personal settings -.cr/personal - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Tabs Studio -*.tss - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs - -# OpenCover UI analysis results -OpenCover/ - -# Azure Stream Analytics local run output -ASALocalRun/ - -# MSBuild Binary and Structured Log -*.binlog - -# NVidia Nsight GPU debugger configuration file -*.nvuser - -# MFractors (Xamarin productivity tool) working folder -.mfractor/ - -# Local History for Visual Studio -.localhistory/ - -# BeatPulse healthcheck temp database -healthchecksdb - -# Backup folder for Package Reference Convert tool in Visual Studio 2017 -MigrationBackup/ - -# Ionide (cross platform F# VS Code tools) working folder -.ionide/ +# Ignore generated nuget packages +*.nupkg \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index f9ba8cf6..00000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,9 +0,0 @@ -# Microsoft Open Source Code of Conduct - -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). - -Resources: - -- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) -- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) -- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns diff --git a/Pipeline/PowerShellScripts/CodeSign.ps1 b/Pipeline/PowerShellScripts/CodeSign.ps1 new file mode 100644 index 00000000..bf144ff0 --- /dev/null +++ b/Pipeline/PowerShellScripts/CodeSign.ps1 @@ -0,0 +1,11 @@ +param( + [string]$AzureKeyVaultURI, + [string]$ApplicationId, + [string]$ApplicationSecretValue, + [string]$CertificateName, + [string]$Timestamp, + [string]$Files, + [string]$TimestampDigest = "sha256" +) + +AzureSignTool.exe sign -kvu "$AzureKeyVaultURI" -kvi "$ApplicationId" -kvs "$ApplicationSecretValue" -kvc "$CertificateName" -tr "$Timestamp" -td "$TimestampDigest" (-split $Files) \ No newline at end of file diff --git a/Pipeline/PowerShellScripts/CopyFileToDestination.ps1 b/Pipeline/PowerShellScripts/CopyFileToDestination.ps1 new file mode 100644 index 00000000..0ad56d10 --- /dev/null +++ b/Pipeline/PowerShellScripts/CopyFileToDestination.ps1 @@ -0,0 +1,13 @@ +param( + [string]$RelativePath, + [string]$File, + [string]$DestinationFullName +) + +$searchFile = Get-ChildItem -Path $RelativePath -Filter $File -Recurse +if (-NOT $searchFile) { + throw "$File file was not found." +} +else { + Copy-Item $searchFile.FullName -Destination "$DestinationFullName" +} \ No newline at end of file diff --git a/Pipeline/PowerShellScripts/Pre-Build.ps1 b/Pipeline/PowerShellScripts/Pre-Build.ps1 new file mode 100644 index 00000000..3b494a67 --- /dev/null +++ b/Pipeline/PowerShellScripts/Pre-Build.ps1 @@ -0,0 +1,6 @@ +write-host "Executing pre-build scripts" + +# Any additional scripts to be executed during pipeline should be referenced in this file +# Link for reference: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-expression?view=powershell-7.1 + +# Invoke-Expression -Command "C:\ps-test\testscript.ps1" \ No newline at end of file diff --git a/Pipeline/YAML_Files/build-pipeline.yml b/Pipeline/YAML_Files/build-pipeline.yml new file mode 100644 index 00000000..fe851ec0 --- /dev/null +++ b/Pipeline/YAML_Files/build-pipeline.yml @@ -0,0 +1,40 @@ +pr: none +trigger: none + +pool: + vmImage: 'windows-latest' + +steps: + - task: PowerShell@2 + displayName: 'Pre-Build steps' + inputs: + targetType: filePath + filePath: ./Pipeline/PowershellScripts/Pre-Build.ps1 + failOnStderr: true + + - task: NuGetToolInstaller@1 + displayName: 'Use NuGet 5.4.0' + inputs: + versionSpec: '5.4.0' + + - task: NuGetCommand@2 + displayName: 'NuGet restore' + inputs: + feedsToUse: config + nugetConfigPath: nuget.config + + - task: VSBuild@1 + displayName: 'Build solution **\*.sln' + inputs: + solution: '**\*.sln' + + - task: PowerShell@2 + displayName: 'Copy CloudScaleUnitExtensionPackage package to artifact directory' + inputs: + targetType: filePath + filePath: ./Pipeline/PowershellScripts/CopyFileToDestination.ps1 + arguments: '-RelativePath "$(Build.SourcesDirectory)\src\ScaleUnitSample" -File "CloudScaleUnitExtensionPackage.zip" -DestinationFullName "$(Build.ArtifactStagingDirectory)\ScaleUnitPackage_$(Build.BuildNumber).zip"' + failOnStderr: true + + - task: PublishBuildArtifacts@1 + displayName: 'Publish Artifact: drop' diff --git a/README.md b/README.md index e406c7ed..a6579203 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,13 @@ +--- +page_type: sample +languages: + - csharp +products: + - Dynamics 365 Commerce +name: Extend Headless commerce engine (CRT/RS) +description: This repo contains the sample code on how to extend the Dynamics 365 Headless Commerce engine - Retail Server, Commerce runtime and Database. +--- + **Download Retail SDK samples and Reference packages from GitHub and NuGet feed:** This document explains to how to get Retail SDK samples from GitHub and reference package from the public NuGet feed. diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index f7b89984..00000000 --- a/SECURITY.md +++ /dev/null @@ -1,41 +0,0 @@ - - -## Security - -Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). - -If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. - -## Reporting Security Issues - -**Please do not report security vulnerabilities through public GitHub issues.** - -Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). - -If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). - -You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). - -Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: - - * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) - * Full paths of source file(s) related to the manifestation of the issue - * The location of the affected source code (tag/branch/commit or direct URL) - * Any special configuration required to reproduce the issue - * Step-by-step instructions to reproduce the issue - * Proof-of-concept or exploit code (if possible) - * Impact of the issue, including how an attacker might exploit the issue - -This information will help us triage your report more quickly. - -If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. - -## Preferred Languages - -We prefer all communications to be in English. - -## Policy - -Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). - - \ No newline at end of file diff --git a/nuget.config b/nuget.config new file mode 100644 index 00000000..ae30fa4e --- /dev/null +++ b/nuget.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/repo.props b/repo.props new file mode 100644 index 00000000..01ce005a --- /dev/null +++ b/repo.props @@ -0,0 +1,14 @@ + + + 0.0 + 9.30 + $(MajorVersion).$(BuildNumber) + + + CN=Contoso Ltd. + Contoso Ltd. + + + [9.30.21144.7-preview,9.31) + + \ No newline at end of file diff --git a/src/ScaleUnitSample/ChannelDatabase/ChannelDatabase.csproj b/src/ScaleUnitSample/ChannelDatabase/ChannelDatabase.csproj new file mode 100644 index 00000000..16e3b5d6 --- /dev/null +++ b/src/ScaleUnitSample/ChannelDatabase/ChannelDatabase.csproj @@ -0,0 +1,10 @@ + + + + + netstandard2.0 + + + + + \ No newline at end of file diff --git a/src/ScaleUnitSample/ChannelDatabase/Contoso.ExampleTable.ChannelDatabase.sql b/src/ScaleUnitSample/ChannelDatabase/Contoso.ExampleTable.ChannelDatabase.sql new file mode 100644 index 00000000..1198d3c0 --- /dev/null +++ b/src/ScaleUnitSample/ChannelDatabase/Contoso.ExampleTable.ChannelDatabase.sql @@ -0,0 +1,138 @@ +/** + * SAMPLE CODE NOTICE + * + * THIS SAMPLE CODE IS MADE AVAILABLE AS IS. MICROSOFT MAKES NO WARRANTIES, WHETHER EXPRESS OR IMPLIED, + * OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OR CONDITIONS OF MERCHANTABILITY. + * THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS SAMPLE CODE REMAINS WITH THE USER. + * NO TECHNICAL SUPPORT IS PROVIDED. YOU MAY NOT DISTRIBUTE THIS CODE UNLESS YOU HAVE A LICENSE AGREEMENT WITH MICROSOFT THAT ALLOWS YOU TO DO SO. + */ + + -- Create the extension table to store the custom fields. + +IF (SELECT OBJECT_ID('[ext].[CONTOSO_EXAMPLETABLE]')) IS NULL +BEGIN + CREATE TABLE + [ext].[CONTOSO_EXAMPLETABLE] + ( + [EXAMPLEID] BIGINT IDENTITY(1,1) NOT NULL, + [EXAMPLEINT] INT NOT NULL DEFAULT ((0)), + [EXAMPLESTRING] NVARCHAR(64) NOT NULL DEFAULT (('')), + CONSTRAINT [I_EXAMPLETABLE_EXAMPLEID] PRIMARY KEY CLUSTERED + ( + [EXAMPLEID] ASC + ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] + ) ON [PRIMARY] + + ALTER TABLE [ext].[CONTOSO_EXAMPLETABLE] WITH CHECK ADD CHECK (([EXAMPLEID]<>(0))) +END +GO + +GRANT SELECT, INSERT, UPDATE, DELETE ON OBJECT::[ext].[CONTOSO_EXAMPLETABLE] TO [DataSyncUsersRole] +GO + +-- Create a stored procedure CRT can use to add entries to the custom table. + +IF OBJECT_ID(N'[ext].[CONTOSO_INSERTEXAMPLE]', N'P') IS NOT NULL + DROP PROCEDURE [ext].[CONTOSO_INSERTEXAMPLE] +GO + +CREATE PROCEDURE [ext].[CONTOSO_INSERTEXAMPLE] + @i_ExampleInt INT, + @s_ExampleString NVARCHAR(64) +AS +BEGIN + SET NOCOUNT ON + + INSERT INTO + ext.CONTOSO_EXAMPLETABLE + (EXAMPLEINT, EXAMPLESTRING) + OUTPUT + INSERTED.EXAMPLEID + VALUES + (@i_ExampleInt, @s_ExampleString) +END; +GO + +GRANT EXECUTE ON [ext].[CONTOSO_INSERTEXAMPLE] TO [UsersRole]; +GO + +GRANT EXECUTE ON [ext].[CONTOSO_INSERTEXAMPLE] TO [DeployExtensibilityRole]; +GO + +-- Create the custom view that can query a complete Example Entity. + +IF (SELECT OBJECT_ID('[ext].[CONTOSO_EXAMPLEVIEW]')) IS NOT NULL + DROP VIEW [ext].[CONTOSO_EXAMPLEVIEW] +GO + +CREATE VIEW [ext].[CONTOSO_EXAMPLEVIEW] AS +( + SELECT + et.EXAMPLEINT, + et.EXAMPLESTRING, + et.EXAMPLEID + FROM + [ext].[CONTOSO_EXAMPLETABLE] et +) +GO + +GRANT SELECT ON OBJECT::[ext].[CONTOSO_EXAMPLEVIEW] TO [UsersRole]; +GO + +GRANT SELECT ON OBJECT::[ext].[CONTOSO_EXAMPLEVIEW] TO [DeployExtensibilityRole]; +GO + +-- Create a stored procedure CRT can use to perform updates. + +IF OBJECT_ID(N'[ext].[CONTOSO_UPDATEEXAMPLE]', N'P') IS NOT NULL + DROP PROCEDURE [ext].[CONTOSO_UPDATEEXAMPLE] +GO + +CREATE PROCEDURE [ext].[CONTOSO_UPDATEEXAMPLE] + @bi_Id BIGINT, + @i_ExampleInt INT, + @s_ExampleString NVARCHAR(64) +AS +BEGIN + SET NOCOUNT ON + + UPDATE + ext.CONTOSO_EXAMPLETABLE + SET + EXAMPLEINT = @i_ExampleInt, + EXAMPLESTRING = @s_ExampleString + WHERE + EXAMPLEID = @bi_Id +END; +GO + +GRANT EXECUTE ON [ext].[CONTOSO_UPDATEEXAMPLE] TO [UsersRole]; +GO + +GRANT EXECUTE ON [ext].[CONTOSO_UPDATEEXAMPLE] TO [DeployExtensibilityRole]; +GO + +-- Create a stored procedure CRT can use to delete Example Entities. + +IF OBJECT_ID(N'[ext].[CONTOSO_DELETEEXAMPLE]', N'P') IS NOT NULL + DROP PROCEDURE [ext].CONTOSO_DELETEEXAMPLE +GO + +CREATE PROCEDURE [ext].CONTOSO_DELETEEXAMPLE + @bi_Id BIGINT +AS +BEGIN + SET NOCOUNT ON + + DELETE FROM + ext.CONTOSO_EXAMPLETABLE + WHERE + EXAMPLEID = @bi_Id +END; +GO + +GRANT EXECUTE ON [ext].CONTOSO_DELETEEXAMPLE TO [UsersRole]; +GO + +GRANT EXECUTE ON [ext].CONTOSO_DELETEEXAMPLE TO [DeployExtensibilityRole]; +GO diff --git a/src/ScaleUnitSample/CommerceRuntime/CommerceRuntime.csproj b/src/ScaleUnitSample/CommerceRuntime/CommerceRuntime.csproj new file mode 100644 index 00000000..a7bec452 --- /dev/null +++ b/src/ScaleUnitSample/CommerceRuntime/CommerceRuntime.csproj @@ -0,0 +1,10 @@ + + + + + netstandard2.0 + + + + + \ No newline at end of file diff --git a/src/ScaleUnitSample/CommerceRuntime/Controllers/BoundController.cs b/src/ScaleUnitSample/CommerceRuntime/Controllers/BoundController.cs new file mode 100644 index 00000000..c6a6da54 --- /dev/null +++ b/src/ScaleUnitSample/CommerceRuntime/Controllers/BoundController.cs @@ -0,0 +1,63 @@ +/** + * SAMPLE CODE NOTICE + * + * THIS SAMPLE CODE IS MADE AVAILABLE AS IS. MICROSOFT MAKES NO WARRANTIES, WHETHER EXPRESS OR IMPLIED, + * OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OR CONDITIONS OF MERCHANTABILITY. + * THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS SAMPLE CODE REMAINS WITH THE USER. + * NO TECHNICAL SUPPORT IS PROVIDED. YOU MAY NOT DISTRIBUTE THIS CODE UNLESS YOU HAVE A LICENSE AGREEMENT WITH MICROSOFT THAT ALLOWS YOU TO DO SO. + */ + +namespace Contoso.CommerceRuntime.Controllers +{ + using System.Threading.Tasks; + using Microsoft.Dynamics.Commerce.Runtime; + using Microsoft.Dynamics.Commerce.Runtime.DataModel; + using Microsoft.Dynamics.Commerce.Runtime.Hosting.Contracts; + + /// + /// An extension controller to handle requests to the StoreHours entity set. + /// + [RoutePrefix("BoundController")] + [BindEntity(typeof(Entities.DataModel.ExampleEntity))] + public class BoundController : IController + { + [HttpGet] + [Authorization(CommerceRoles.Anonymous, CommerceRoles.Application, CommerceRoles.Customer, CommerceRoles.Device, CommerceRoles.Employee, CommerceRoles.Storefront)] + public async Task> GetAllExampleEntities(IEndpointContext context) + { + var queryResultSettings = QueryResultSettings.SingleRecord; + queryResultSettings.Paging = new PagingInfo(10); + + var request = new Messages.ExampleEntityDataRequest() { QueryResultSettings = queryResultSettings }; + var response = await context.ExecuteAsync(request).ConfigureAwait(false); + return response.ExampleEntities; + } + + [HttpPost] + [Authorization(CommerceRoles.Customer, CommerceRoles.Device, CommerceRoles.Employee)] + public async Task CreateExampleEntity(IEndpointContext context, CommerceRuntime.Entities.DataModel.ExampleEntity entityData) + { + var request = new Messages.CreateExampleEntityDataRequest(entityData); + var response = await context.ExecuteAsync(request).ConfigureAwait(false); + return response.CreatedId; + } + + [HttpPost] + [Authorization(CommerceRoles.Customer, CommerceRoles.Device, CommerceRoles.Employee)] + public async Task UpdateExampleEntity(IEndpointContext context, [EntityKey] long key, CommerceRuntime.Entities.DataModel.ExampleEntity updatedEntity) + { + var request = new Messages.UpdateExampleEntityDataRequest(key, updatedEntity); + var response = await context.ExecuteAsync(request).ConfigureAwait(false); + return response.Success; + } + + [HttpPost] + [Authorization(CommerceRoles.Customer, CommerceRoles.Device, CommerceRoles.Employee)] + public async Task DeleteExampleEntity(IEndpointContext context, [EntityKey] long key) + { + var request = new Messages.DeleteExampleEntityDataRequest(key); + var response = await context.ExecuteAsync(request).ConfigureAwait(false); + return response.Success; + } + } +} diff --git a/src/ScaleUnitSample/CommerceRuntime/Controllers/UnboundController.cs b/src/ScaleUnitSample/CommerceRuntime/Controllers/UnboundController.cs new file mode 100644 index 00000000..59ec72c9 --- /dev/null +++ b/src/ScaleUnitSample/CommerceRuntime/Controllers/UnboundController.cs @@ -0,0 +1,43 @@ +/** + * SAMPLE CODE NOTICE + * + * THIS SAMPLE CODE IS MADE AVAILABLE AS IS. MICROSOFT MAKES NO WARRANTIES, WHETHER EXPRESS OR IMPLIED, + * OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OR CONDITIONS OF MERCHANTABILITY. + * THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS SAMPLE CODE REMAINS WITH THE USER. + * NO TECHNICAL SUPPORT IS PROVIDED. YOU MAY NOT DISTRIBUTE THIS CODE UNLESS YOU HAVE A LICENSE AGREEMENT WITH MICROSOFT THAT ALLOWS YOU TO DO SO. + */ + +namespace Contoso.CommerceRuntime.Controllers +{ + using System.Threading.Tasks; + using Microsoft.Dynamics.Commerce.Runtime.DataModel; + using Microsoft.Dynamics.Commerce.Runtime.Hosting.Contracts; + + /// + /// An extension controller to handle requests to the StoreHours entity set. + /// + public class UnboundController : IController + { + /// + /// A simple GET endpoint to demonstrate GET endpoints on an unbound controller. + /// + /// A simple true value to indicate the endpoint was reached. + [HttpGet] + [Authorization(CommerceRoles.Anonymous, CommerceRoles.Application, CommerceRoles.Customer, CommerceRoles.Device, CommerceRoles.Employee, CommerceRoles.Storefront)] + public Task SimplePingGet() + { + return Task.FromResult(true); + } + + /// + /// A simple POST endpoint to demonstrate POST endpoints on an unbound controller. + /// + /// A simple true value to indicate the endpoint was reached. + [HttpPost] + [Authorization(CommerceRoles.Customer, CommerceRoles.Device, CommerceRoles.Employee)] + public Task SimplePingPost() + { + return Task.FromResult(true); + } + } +} diff --git a/src/ScaleUnitSample/CommerceRuntime/Entities/ExampleEntity.cs b/src/ScaleUnitSample/CommerceRuntime/Entities/ExampleEntity.cs new file mode 100644 index 00000000..e0e796fc --- /dev/null +++ b/src/ScaleUnitSample/CommerceRuntime/Entities/ExampleEntity.cs @@ -0,0 +1,73 @@ +/** + * SAMPLE CODE NOTICE + * + * THIS SAMPLE CODE IS MADE AVAILABLE AS IS. MICROSOFT MAKES NO WARRANTIES, WHETHER EXPRESS OR IMPLIED, + * OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OR CONDITIONS OF MERCHANTABILITY. + * THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS SAMPLE CODE REMAINS WITH THE USER. + * NO TECHNICAL SUPPORT IS PROVIDED. YOU MAY NOT DISTRIBUTE THIS CODE UNLESS YOU HAVE A LICENSE AGREEMENT WITH MICROSOFT THAT ALLOWS YOU TO DO SO. + */ + +namespace Contoso.CommerceRuntime.Entities.DataModel +{ + using System.Runtime.Serialization; + using Microsoft.Dynamics.Commerce.Runtime.ComponentModel.DataAnnotations; + using Microsoft.Dynamics.Commerce.Runtime.DataModel; + using SystemAnnotations = System.ComponentModel.DataAnnotations; + + /// + /// Defines a simple class that holds information about opening and closing times for a particular day. + /// + public class ExampleEntity : CommerceEntity + { + private const string ExampleIntColumn = "EXAMPLEINT"; + private const string ExampleStringColumn = "EXAMPLESTRING"; + private const string IdColumn = "EXAMPLEID"; + + /// + /// Initializes a new instance of the class. + /// + public ExampleEntity() + : base("Example") + { + } + + /// + /// Gets or sets a property containing an int value. + /// + [DataMember] + [Column(ExampleIntColumn)] + public int IntData + { + get { return (int)this[ExampleIntColumn]; } + set { this[ExampleIntColumn] = value; } + } + + /// + /// Gets or sets a property containing a string value. + /// + [DataMember] + [Column(ExampleStringColumn)] + public string StringData + { + get { return (string)this[ExampleStringColumn]; } + set { this[ExampleStringColumn] = value; } + } + + /// + /// Gets or sets the id. + /// + /// + /// Fields named "Id" are automatically treated as the entity key. + /// If a name other than Id is preferred, + /// can be used like it is here to annotate a given field as the entity key. + /// + [SystemAnnotations.Key] + [DataMember] + [Column(IdColumn)] + public long UnusualEntityId + { + get { return (long)this[IdColumn]; } + set { this[IdColumn] = value; } + } + } +} \ No newline at end of file diff --git a/src/ScaleUnitSample/CommerceRuntime/Messages/CreateExampleEntityDataRequest.cs b/src/ScaleUnitSample/CommerceRuntime/Messages/CreateExampleEntityDataRequest.cs new file mode 100644 index 00000000..6648d0c5 --- /dev/null +++ b/src/ScaleUnitSample/CommerceRuntime/Messages/CreateExampleEntityDataRequest.cs @@ -0,0 +1,37 @@ +/** + * SAMPLE CODE NOTICE + * + * THIS SAMPLE CODE IS MADE AVAILABLE AS IS. MICROSOFT MAKES NO WARRANTIES, WHETHER EXPRESS OR IMPLIED, + * OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OR CONDITIONS OF MERCHANTABILITY. + * THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS SAMPLE CODE REMAINS WITH THE USER. + * NO TECHNICAL SUPPORT IS PROVIDED. YOU MAY NOT DISTRIBUTE THIS CODE UNLESS YOU HAVE A LICENSE AGREEMENT WITH MICROSOFT THAT ALLOWS YOU TO DO SO. + */ + +namespace Contoso.CommerceRuntime.Messages +{ + using System.Runtime.Serialization; + using Contoso.CommerceRuntime.Entities.DataModel; + using Microsoft.Dynamics.Commerce.Runtime.Messages; + + /// + /// A simple request class to create an Example Entity in the database. + /// + [DataContract] + public sealed class CreateExampleEntityDataRequest : Request + { + /// + /// Initializes a new instance of the class. + /// + /// An example entity with its fields populated with the values to be stored. + public CreateExampleEntityDataRequest(ExampleEntity entityData) + { + this.EntityData = entityData; + } + + /// + /// Gets an Example Entity instance with its fields set with the values to be stored. + /// + [DataMember] + public ExampleEntity EntityData { get; private set; } + } +} \ No newline at end of file diff --git a/src/ScaleUnitSample/CommerceRuntime/Messages/CreateExampleEntityDataResponse.cs b/src/ScaleUnitSample/CommerceRuntime/Messages/CreateExampleEntityDataResponse.cs new file mode 100644 index 00000000..95082ec3 --- /dev/null +++ b/src/ScaleUnitSample/CommerceRuntime/Messages/CreateExampleEntityDataResponse.cs @@ -0,0 +1,35 @@ +/** + * SAMPLE CODE NOTICE + * + * THIS SAMPLE CODE IS MADE AVAILABLE AS IS. MICROSOFT MAKES NO WARRANTIES, WHETHER EXPRESS OR IMPLIED, + * OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OR CONDITIONS OF MERCHANTABILITY. + * THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS SAMPLE CODE REMAINS WITH THE USER. + * NO TECHNICAL SUPPORT IS PROVIDED. YOU MAY NOT DISTRIBUTE THIS CODE UNLESS YOU HAVE A LICENSE AGREEMENT WITH MICROSOFT THAT ALLOWS YOU TO DO SO. + */ + +namespace Contoso.CommerceRuntime.Messages +{ + using System.Runtime.Serialization; + using Microsoft.Dynamics.Commerce.Runtime.Messages; + + /// + /// A simple response class to indicate whether creating a new entity succeeded or not. + /// + [DataContract] + public sealed class CreateExampleEntityDataResponse : Response + { + /// + /// Initializes a new instance of the class. + /// + /// The ID of the newly saved entity instance, 0 in the event of failure. + public CreateExampleEntityDataResponse(long createdId) + { + this.CreatedId = createdId; + } + + /// + /// Gets the ID of the newly saved entity instance, or 0 in the event of failure. + /// + public long CreatedId { get; private set; } + } +} \ No newline at end of file diff --git a/src/ScaleUnitSample/CommerceRuntime/Messages/DeleteExampleEntityDataRequest.cs b/src/ScaleUnitSample/CommerceRuntime/Messages/DeleteExampleEntityDataRequest.cs new file mode 100644 index 00000000..67ca61db --- /dev/null +++ b/src/ScaleUnitSample/CommerceRuntime/Messages/DeleteExampleEntityDataRequest.cs @@ -0,0 +1,35 @@ +/** + * SAMPLE CODE NOTICE + * + * THIS SAMPLE CODE IS MADE AVAILABLE AS IS. MICROSOFT MAKES NO WARRANTIES, WHETHER EXPRESS OR IMPLIED, + * OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OR CONDITIONS OF MERCHANTABILITY. + * THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS SAMPLE CODE REMAINS WITH THE USER. + * NO TECHNICAL SUPPORT IS PROVIDED. YOU MAY NOT DISTRIBUTE THIS CODE UNLESS YOU HAVE A LICENSE AGREEMENT WITH MICROSOFT THAT ALLOWS YOU TO DO SO. + */ + +namespace Contoso.CommerceRuntime.Messages +{ + using System.Runtime.Serialization; + using Microsoft.Dynamics.Commerce.Runtime.Messages; + + /// + /// A simple request used to delete an example entity from the database. + /// + [DataContract] + public sealed class DeleteExampleEntityDataRequest : Request + { + /// + /// Initializes a new instance of the class. + /// + /// A unique key identifying an Example Entity record to delete. + public DeleteExampleEntityDataRequest(long entityKey) + { + this.ExampleEntityKey = entityKey; + } + + /// + /// Gets the unique ID specifying the Example Entity record to delete. + /// + public long ExampleEntityKey { get; private set; } + } +} \ No newline at end of file diff --git a/src/ScaleUnitSample/CommerceRuntime/Messages/DeleteExampleEntityDataResponse.cs b/src/ScaleUnitSample/CommerceRuntime/Messages/DeleteExampleEntityDataResponse.cs new file mode 100644 index 00000000..0b8a96eb --- /dev/null +++ b/src/ScaleUnitSample/CommerceRuntime/Messages/DeleteExampleEntityDataResponse.cs @@ -0,0 +1,35 @@ +/** + * SAMPLE CODE NOTICE + * + * THIS SAMPLE CODE IS MADE AVAILABLE AS IS. MICROSOFT MAKES NO WARRANTIES, WHETHER EXPRESS OR IMPLIED, + * OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OR CONDITIONS OF MERCHANTABILITY. + * THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS SAMPLE CODE REMAINS WITH THE USER. + * NO TECHNICAL SUPPORT IS PROVIDED. YOU MAY NOT DISTRIBUTE THIS CODE UNLESS YOU HAVE A LICENSE AGREEMENT WITH MICROSOFT THAT ALLOWS YOU TO DO SO. + */ + +namespace Contoso.CommerceRuntime.Messages +{ + using System.Runtime.Serialization; + using Microsoft.Dynamics.Commerce.Runtime.Messages; + + /// + /// A simple response class to indicate whether a delete succeeded or not. + /// + [DataContract] + public sealed class DeleteExampleEntityDataResponse : Response + { + /// + /// Creates a new instance of the class. + /// + /// Whether the delete succeeded. + public DeleteExampleEntityDataResponse(bool success) + { + this.Success = success; + } + + /// + /// Gets a value indicating whether the delete succeeded. + /// + public bool Success { get; private set; } + } +} \ No newline at end of file diff --git a/src/ScaleUnitSample/CommerceRuntime/Messages/ExampleEntityDataRequest.cs b/src/ScaleUnitSample/CommerceRuntime/Messages/ExampleEntityDataRequest.cs new file mode 100644 index 00000000..1c9c57d7 --- /dev/null +++ b/src/ScaleUnitSample/CommerceRuntime/Messages/ExampleEntityDataRequest.cs @@ -0,0 +1,21 @@ +/** + * SAMPLE CODE NOTICE + * + * THIS SAMPLE CODE IS MADE AVAILABLE AS IS. MICROSOFT MAKES NO WARRANTIES, WHETHER EXPRESS OR IMPLIED, + * OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OR CONDITIONS OF MERCHANTABILITY. + * THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS SAMPLE CODE REMAINS WITH THE USER. + * NO TECHNICAL SUPPORT IS PROVIDED. YOU MAY NOT DISTRIBUTE THIS CODE UNLESS YOU HAVE A LICENSE AGREEMENT WITH MICROSOFT THAT ALLOWS YOU TO DO SO. + */ +namespace Contoso.CommerceRuntime.Messages +{ + using System.Runtime.Serialization; + using Microsoft.Dynamics.Commerce.Runtime.Messages; + + /// + /// A simple request class to get all Example Entities. + /// + [DataContract] + public sealed class ExampleEntityDataRequest : Request + { + } +} \ No newline at end of file diff --git a/src/ScaleUnitSample/CommerceRuntime/Messages/ExampleEntityDataResponse.cs b/src/ScaleUnitSample/CommerceRuntime/Messages/ExampleEntityDataResponse.cs new file mode 100644 index 00000000..45002ebd --- /dev/null +++ b/src/ScaleUnitSample/CommerceRuntime/Messages/ExampleEntityDataResponse.cs @@ -0,0 +1,38 @@ +/** + * SAMPLE CODE NOTICE + * + * THIS SAMPLE CODE IS MADE AVAILABLE AS IS. MICROSOFT MAKES NO WARRANTIES, WHETHER EXPRESS OR IMPLIED, + * OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OR CONDITIONS OF MERCHANTABILITY. + * THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS SAMPLE CODE REMAINS WITH THE USER. + * NO TECHNICAL SUPPORT IS PROVIDED. YOU MAY NOT DISTRIBUTE THIS CODE UNLESS YOU HAVE A LICENSE AGREEMENT WITH MICROSOFT THAT ALLOWS YOU TO DO SO. + */ + +namespace Contoso.CommerceRuntime.Messages +{ + using System.Runtime.Serialization; + using Contoso.CommerceRuntime.Entities.DataModel; + using Microsoft.Dynamics.Commerce.Runtime; + using Microsoft.Dynamics.Commerce.Runtime.Messages; + + /// + /// Defines a simple response class that holds a collection of Example Entities. + /// + [DataContract] + public sealed class ExampleEntityDataResponse : Response + { + /// + /// Initializes a new instance of the class. + /// + /// The collection of Example Entities. + public ExampleEntityDataResponse(PagedResult exampleEntities) + { + this.ExampleEntities = exampleEntities; + } + + /// + /// Gets the retrieved Example Entities as a paged result. + /// + [DataMember] + public PagedResult ExampleEntities { get; private set; } + } +} \ No newline at end of file diff --git a/src/ScaleUnitSample/CommerceRuntime/Messages/UpdateExampleEntityDataRequest.cs b/src/ScaleUnitSample/CommerceRuntime/Messages/UpdateExampleEntityDataRequest.cs new file mode 100644 index 00000000..05d101be --- /dev/null +++ b/src/ScaleUnitSample/CommerceRuntime/Messages/UpdateExampleEntityDataRequest.cs @@ -0,0 +1,44 @@ +/** + * SAMPLE CODE NOTICE + * + * THIS SAMPLE CODE IS MADE AVAILABLE AS IS. MICROSOFT MAKES NO WARRANTIES, WHETHER EXPRESS OR IMPLIED, + * OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OR CONDITIONS OF MERCHANTABILITY. + * THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS SAMPLE CODE REMAINS WITH THE USER. + * NO TECHNICAL SUPPORT IS PROVIDED. YOU MAY NOT DISTRIBUTE THIS CODE UNLESS YOU HAVE A LICENSE AGREEMENT WITH MICROSOFT THAT ALLOWS YOU TO DO SO. + */ + +namespace Contoso.CommerceRuntime.Messages +{ + using System.Runtime.Serialization; + using Contoso.CommerceRuntime.Entities.DataModel; + using Microsoft.Dynamics.Commerce.Runtime.Messages; + + /// + /// A simple request class to update the values on an example entity. + /// + [DataContract] + public sealed class UpdateExampleEntityDataRequest : Request + { + /// + /// Initializes a new instance of the class. + /// + /// A unique key identifying an Example Entity record to update. + /// An example entity with update fields. + public UpdateExampleEntityDataRequest(long entityKey, ExampleEntity updatedEntity) + { + this.ExampleEntityKey = entityKey; + this.UpdatedExampleEntity = updatedEntity; + } + + /// + /// Gets the unique ID specifying the Example Entity record to update. + /// + public long ExampleEntityKey { get; private set; } + + /// + /// Gets an Example Entity instance with any updates applied to it. + /// + [DataMember] + public ExampleEntity UpdatedExampleEntity { get; private set; } + } +} \ No newline at end of file diff --git a/src/ScaleUnitSample/CommerceRuntime/Messages/UpdateExampleEntityDataResponse.cs b/src/ScaleUnitSample/CommerceRuntime/Messages/UpdateExampleEntityDataResponse.cs new file mode 100644 index 00000000..25ad9d48 --- /dev/null +++ b/src/ScaleUnitSample/CommerceRuntime/Messages/UpdateExampleEntityDataResponse.cs @@ -0,0 +1,35 @@ +/** + * SAMPLE CODE NOTICE + * + * THIS SAMPLE CODE IS MADE AVAILABLE AS IS. MICROSOFT MAKES NO WARRANTIES, WHETHER EXPRESS OR IMPLIED, + * OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OR CONDITIONS OF MERCHANTABILITY. + * THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS SAMPLE CODE REMAINS WITH THE USER. + * NO TECHNICAL SUPPORT IS PROVIDED. YOU MAY NOT DISTRIBUTE THIS CODE UNLESS YOU HAVE A LICENSE AGREEMENT WITH MICROSOFT THAT ALLOWS YOU TO DO SO. + */ + +namespace Contoso.CommerceRuntime.Messages +{ + using System.Runtime.Serialization; + using Microsoft.Dynamics.Commerce.Runtime.Messages; + + /// + /// A simple response class to indicate whether an update succeeded or not. + /// + [DataContract] + public sealed class UpdateExampleEntityDataResponse : Response + { + /// + /// Initializes a new instance of the class. + /// + /// Whether the update succeeded. + public UpdateExampleEntityDataResponse(bool success) + { + this.Success = success; + } + + /// + /// Gets a value indicating whether the update succeeded. + /// + public bool Success { get; private set; } + } +} \ No newline at end of file diff --git a/src/ScaleUnitSample/CommerceRuntime/RequestHandlers/ExampleDataService.cs b/src/ScaleUnitSample/CommerceRuntime/RequestHandlers/ExampleDataService.cs new file mode 100644 index 00000000..0036f795 --- /dev/null +++ b/src/ScaleUnitSample/CommerceRuntime/RequestHandlers/ExampleDataService.cs @@ -0,0 +1,174 @@ +/** + * SAMPLE CODE NOTICE + * + * THIS SAMPLE CODE IS MADE AVAILABLE AS IS. MICROSOFT MAKES NO WARRANTIES, WHETHER EXPRESS OR IMPLIED, + * OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OR CONDITIONS OF MERCHANTABILITY. + * THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS SAMPLE CODE REMAINS WITH THE USER. + * NO TECHNICAL SUPPORT IS PROVIDED. YOU MAY NOT DISTRIBUTE THIS CODE UNLESS YOU HAVE A LICENSE AGREEMENT WITH MICROSOFT THAT ALLOWS YOU TO DO SO. + */ + +namespace Contoso.CommerceRuntime.RequestHandlers +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Linq; + using System.Threading.Tasks; + using Microsoft.Dynamics.Commerce.Runtime; + using Microsoft.Dynamics.Commerce.Runtime.Data; + using Microsoft.Dynamics.Commerce.Runtime.Messages; + using Contoso.CommerceRuntime.Entities.DataModel; + using Contoso.CommerceRuntime.Messages; + + /// + /// Sample service to demonstrate managing a collection of entities. + /// + public class ExampleDataService : IRequestHandlerAsync + { + /// + /// Gets the collection of supported request types by this handler. + /// + public IEnumerable SupportedRequestTypes + { + get + { + return new[] + { + typeof(CreateExampleEntityDataRequest), + typeof(ExampleEntityDataRequest), + typeof(UpdateExampleEntityDataRequest), + typeof(DeleteExampleEntityDataRequest), + }; + } + } + + /// + /// Entry point to StoreHoursDataService service. + /// + /// The request to execute. + /// Result of executing request, or null object for void operations. + public Task Execute(Request request) + { + if (request == null) + { + throw new ArgumentNullException("request"); + } + + Type reqType = request.GetType(); + if (reqType == typeof(CreateExampleEntityDataRequest)) + { + return this.CreateExampleEntity((CreateExampleEntityDataRequest)request); + } + else if (reqType == typeof(ExampleEntityDataRequest)) + { + return this.GetExampleEntities((ExampleEntityDataRequest)request); + } + else if (reqType == typeof(UpdateExampleEntityDataRequest)) + { + return this.UpdateExampleEntity((UpdateExampleEntityDataRequest)request); + } + else if (reqType == typeof(DeleteExampleEntityDataRequest)) + { + return this.DeleteExampleEntity((DeleteExampleEntityDataRequest)request); + } + else + { + string message = string.Format(CultureInfo.InvariantCulture, "Request '{0}' is not supported.", reqType); + throw new NotSupportedException(message); + } + } + + private async Task CreateExampleEntity(CreateExampleEntityDataRequest request) + { + ThrowIf.Null(request, nameof(request)); + ThrowIf.Null(request.EntityData, nameof(request.EntityData)); + + long insertedId = 0; + using (var databaseContext = new DatabaseContext(request.RequestContext)) + { + ParameterSet parameters = new ParameterSet(); + parameters["@i_ExampleInt"] = request.EntityData.IntData; + parameters["@s_ExampleString"] = request.EntityData.StringData; + var result = await databaseContext + .ExecuteStoredProcedureAsync("[ext].CONTOSO_INSERTEXAMPLE", parameters, request.QueryResultSettings) + .ConfigureAwait(continueOnCapturedContext: false); + insertedId = result.Item2.Single().UnusualEntityId; + } + + return new CreateExampleEntityDataResponse(insertedId); + } + + private async Task GetExampleEntities(ExampleEntityDataRequest request) + { + ThrowIf.Null(request, "request"); + + using (DatabaseContext databaseContext = new DatabaseContext(request.RequestContext)) + { + var query = new SqlPagedQuery(request.QueryResultSettings) + { + DatabaseSchema = "ext", + Select = new ColumnSet("EXAMPLEINT", "EXAMPLESTRING", "EXAMPLEID"), + From = "CONTOSO_EXAMPLEVIEW", + OrderBy = "EXAMPLEID", + }; + + var queryResults = + await databaseContext + .ReadEntityAsync(query) + .ConfigureAwait(continueOnCapturedContext: false); + return new ExampleEntityDataResponse(queryResults); + } + } + + private async Task UpdateExampleEntity(UpdateExampleEntityDataRequest request) + { + ThrowIf.Null(request, nameof(request)); + ThrowIf.Null(request.UpdatedExampleEntity, nameof(request.UpdatedExampleEntity)); + + if (request.ExampleEntityKey == 0) + { + throw new DataValidationException(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_ValueOutOfRange, $"{nameof(request.ExampleEntityKey)} cannot be 0"); + } + + bool updateSuccess = false; + using (var databaseContext = new DatabaseContext(request.RequestContext)) + { + ParameterSet parameters = new ParameterSet(); + parameters["@bi_Id"] = request.ExampleEntityKey; + parameters["@i_ExampleInt"] = request.UpdatedExampleEntity.IntData; + parameters["@s_ExampleString"] = request.UpdatedExampleEntity.StringData; + int sprocErrorCode = + await databaseContext + .ExecuteStoredProcedureNonQueryAsync("[ext].CONTOSO_UPDATEEXAMPLE", parameters, request.QueryResultSettings) + .ConfigureAwait(continueOnCapturedContext: false); + updateSuccess = (sprocErrorCode == 0); + } + + return new UpdateExampleEntityDataResponse(updateSuccess); + } + + private async Task DeleteExampleEntity(DeleteExampleEntityDataRequest request) + { + ThrowIf.Null(request, nameof(request)); + + if (request.ExampleEntityKey == 0) + { + throw new DataValidationException(DataValidationErrors.Microsoft_Dynamics_Commerce_Runtime_ValueOutOfRange, $"{nameof(request.ExampleEntityKey)} cannot be 0"); + } + + bool deleteSuccess = false; + using (var databaseContext = new DatabaseContext(request.RequestContext)) + { + ParameterSet parameters = new ParameterSet(); + parameters["@bi_Id"] = request.ExampleEntityKey; + int sprocErrorCode = + await databaseContext + .ExecuteStoredProcedureNonQueryAsync("[ext].CONTOSO_DELETEEXAMPLE", parameters, request.QueryResultSettings) + .ConfigureAwait(continueOnCapturedContext: false); + deleteSuccess = sprocErrorCode == 0; + } + + return new DeleteExampleEntityDataResponse(deleteSuccess); + } + } +} \ No newline at end of file diff --git a/src/ScaleUnitSample/CommerceRuntime/Triggers/DefinePosExtensionTrigger.cs b/src/ScaleUnitSample/CommerceRuntime/Triggers/DefinePosExtensionTrigger.cs new file mode 100644 index 00000000..5e132dba --- /dev/null +++ b/src/ScaleUnitSample/CommerceRuntime/Triggers/DefinePosExtensionTrigger.cs @@ -0,0 +1,61 @@ +/** + * SAMPLE CODE NOTICE + * + * THIS SAMPLE CODE IS MADE AVAILABLE AS IS. MICROSOFT MAKES NO WARRANTIES, WHETHER EXPRESS OR IMPLIED, + * OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OR CONDITIONS OF MERCHANTABILITY. + * THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS SAMPLE CODE REMAINS WITH THE USER. + * NO TECHNICAL SUPPORT IS PROVIDED. YOU MAY NOT DISTRIBUTE THIS CODE UNLESS YOU HAVE A LICENSE AGREEMENT WITH MICROSOFT THAT ALLOWS YOU TO DO SO. + */ + +namespace Contoso.CommerceRuntime.Triggers +{ + using System; + using System.Collections.Generic; + using Microsoft.Dynamics.Commerce.Runtime; + using Microsoft.Dynamics.Commerce.Runtime.DataModel; + using Microsoft.Dynamics.Commerce.Runtime.Messages; + + /// + /// Class that implements a post trigger for the GetCustomerDataRequest request type. + /// + public class DefinePosExtensionTrigger : IRequestTrigger + { + /// + /// Gets the supported requests for this trigger. + /// + public IEnumerable SupportedRequestTypes + { + get + { + return new[] { typeof(GetExtensionPackageDefinitionsRequest) }; + } + } + + /// + /// Post trigger code to retrieve extension properties. + /// + /// The request. + /// The response. + public void OnExecuted(Request request, Response response) + { + ThrowIf.Null(request, "request"); + ThrowIf.Null(response, "response"); + + var getExtensionsResponse = (GetExtensionPackageDefinitionsResponse)response; + var extensionPackageDefinition = new ExtensionPackageDefinition(); + extensionPackageDefinition.Name = "POS"; + extensionPackageDefinition.Publisher = "Contoso"; + extensionPackageDefinition.IsEnabled = true; + + getExtensionsResponse.ExtensionPackageDefinitions.Add(extensionPackageDefinition); + } + + /// + /// Pre trigger code. + /// + /// The request. + public void OnExecuting(Request request) + { + } + } +} \ No newline at end of file diff --git a/src/ScaleUnitSample/CustomizationPackage.props b/src/ScaleUnitSample/CustomizationPackage.props new file mode 100644 index 00000000..fc2d496b --- /dev/null +++ b/src/ScaleUnitSample/CustomizationPackage.props @@ -0,0 +1,12 @@ + + + + + $(Publisher) + $(PublisherDisplayName) + $(Version) + Contoso.Commerce.ScaleUnit + Contoso Scale Unit Example Commerce Customization + Contoso Scale Unit Example Commerce Customization + + \ No newline at end of file diff --git a/src/ScaleUnitSample/Installer/Extension.config b/src/ScaleUnitSample/Installer/Extension.config new file mode 100644 index 00000000..ba81fc58 --- /dev/null +++ b/src/ScaleUnitSample/Installer/Extension.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/ScaleUnitSample/Installer/ScaleUnit.Installer.csproj b/src/ScaleUnitSample/Installer/ScaleUnit.Installer.csproj new file mode 100644 index 00000000..0bc657cd --- /dev/null +++ b/src/ScaleUnitSample/Installer/ScaleUnit.Installer.csproj @@ -0,0 +1,28 @@ + + + + + Exe + net461 + + + + + + + + + + + + + false + + + false + + + false + + + \ No newline at end of file diff --git a/src/ScaleUnitSample/POS/Controls/Dialogs/Create/ExampleCreateDialog.html b/src/ScaleUnitSample/POS/Controls/Dialogs/Create/ExampleCreateDialog.html new file mode 100644 index 00000000..c847fb7f --- /dev/null +++ b/src/ScaleUnitSample/POS/Controls/Dialogs/Create/ExampleCreateDialog.html @@ -0,0 +1,25 @@ + + + + + Example Create Dialog + + +
+
+
Int data:
+ +
+
+
String data:
+ +
+
+
Extension property string data:
+ +
+
+
+
+ + diff --git a/src/ScaleUnitSample/POS/Controls/Dialogs/Create/ExampleCreateDialogModule.ts b/src/ScaleUnitSample/POS/Controls/Dialogs/Create/ExampleCreateDialogModule.ts new file mode 100644 index 00000000..6b81a542 --- /dev/null +++ b/src/ScaleUnitSample/POS/Controls/Dialogs/Create/ExampleCreateDialogModule.ts @@ -0,0 +1,88 @@ +/** + * SAMPLE CODE NOTICE + * + * THIS SAMPLE CODE IS MADE AVAILABLE AS IS. MICROSOFT MAKES NO WARRANTIES, WHETHER EXPRESS OR IMPLIED, + * OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OR CONDITIONS OF MERCHANTABILITY. + * THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS SAMPLE CODE REMAINS WITH THE USER. + * NO TECHNICAL SUPPORT IS PROVIDED. YOU MAY NOT DISTRIBUTE THIS CODE UNLESS YOU HAVE A LICENSE AGREEMENT WITH MICROSOFT THAT ALLOWS YOU TO DO SO. + */ + +import * as Dialogs from "PosApi/Create/Dialogs"; +import { Entities } from "../../../DataService/DataServiceEntities.g"; +import { ObjectExtensions } from "PosApi/TypeExtensions"; + +type DialogResolve = (updatedEntity: Entities.ExampleEntity) => void; +type DialogReject = (reason: any) => void; + +export default class ExampleCreateDialog extends Dialogs.ExtensionTemplatedDialogBase { + private _resolve: DialogResolve; + private _data: Entities.ExampleEntity; + + constructor() { + super(); + this._data = { + UnusualEntityId: -1, + IntData: 0, + StringData: "", + ExtensionProperties: [{ + Key: "customExtensionProp", + Value: {} + }] + }; + } + + public onReady(element: HTMLElement): void { + let intDataInput: HTMLInputElement = element.querySelector("#intData") as HTMLInputElement; + intDataInput.onchange = () => { this._data.IntData = intDataInput.valueAsNumber; }; + + let stringDataInput: HTMLInputElement = element.querySelector("#stringData") as HTMLInputElement; + stringDataInput.onchange = () => { this._data.StringData = stringDataInput.value; }; + + let extensionPropertyStringDataInput: HTMLInputElement = element.querySelector("#extensionPropertyStringData") as HTMLInputElement; + extensionPropertyStringDataInput.onchange = () => { + this._data.ExtensionProperties[0].Value.StringValue = extensionPropertyStringDataInput.value; + }; + } + + public open(): Promise { + let promise: Promise = new Promise((resolve: DialogResolve, reject: DialogReject) => { + this._resolve = resolve; + let option: Dialogs.ITemplatedDialogOptions = { + title: "Create Example Entity", + button1: { + id: "btnCreate", + label: this.context.resources.getString("string_2001"), + isPrimary: true, + onClick: this.btnUpdateClickHandler.bind(this) + }, + button2: { + id: "btnCancel", + label: this.context.resources.getString("string_2004"), + onClick: this.btnCancelClickHandler.bind(this) + }, + onCloseX: () => this.btnCancelClickHandler() + }; + + this.openDialog(option); + }); + + return promise; + } + + private btnUpdateClickHandler(): boolean { + this.resolvePromise(this._data); + return true; + } + + private btnCancelClickHandler(): boolean { + this.resolvePromise(null); + return true; + } + + private resolvePromise(editResult: Entities.ExampleEntity): void { + if (ObjectExtensions.isFunction(this._resolve)) { + this._resolve(editResult); + this._resolve = null; + } + } +} diff --git a/src/ScaleUnitSample/POS/Controls/Dialogs/Display/PingResultDialog.html b/src/ScaleUnitSample/POS/Controls/Dialogs/Display/PingResultDialog.html new file mode 100644 index 00000000..5fb37fb1 --- /dev/null +++ b/src/ScaleUnitSample/POS/Controls/Dialogs/Display/PingResultDialog.html @@ -0,0 +1,25 @@ + + + + + Ping Result Dialog + + +
+

+

Unbound GET endpoint ping result:
+
+
+ +

+

+

Unbound POST endpoint ping result:
+
+
+ +

+
+
+
+ + diff --git a/src/ScaleUnitSample/POS/Controls/Dialogs/Display/PingResultDialogModule.ts b/src/ScaleUnitSample/POS/Controls/Dialogs/Display/PingResultDialogModule.ts new file mode 100644 index 00000000..ae3160b5 --- /dev/null +++ b/src/ScaleUnitSample/POS/Controls/Dialogs/Display/PingResultDialogModule.ts @@ -0,0 +1,63 @@ +/** + * SAMPLE CODE NOTICE + * + * THIS SAMPLE CODE IS MADE AVAILABLE AS IS. MICROSOFT MAKES NO WARRANTIES, WHETHER EXPRESS OR IMPLIED, + * OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OR CONDITIONS OF MERCHANTABILITY. + * THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS SAMPLE CODE REMAINS WITH THE USER. + * NO TECHNICAL SUPPORT IS PROVIDED. YOU MAY NOT DISTRIBUTE THIS CODE UNLESS YOU HAVE A LICENSE AGREEMENT WITH MICROSOFT THAT ALLOWS YOU TO DO SO. + */ + +import * as Dialogs from "PosApi/Create/Dialogs"; +import { ObjectExtensions } from "PosApi/TypeExtensions"; + +type DialogResolve = (result: any) => void; +type DialogReject = (reason: any) => void; + +export default class PingResultDialog extends Dialogs.ExtensionTemplatedDialogBase { + private _resolve: DialogResolve; + private _pingUnboundGetResult: boolean; + private _pingUnboundPostResult: boolean; + + constructor() { + super(); + } + + public onReady(element: HTMLElement): void { + let getPingResult = element.querySelector("#UnboundGetResult") as HTMLSpanElement; + getPingResult.textContent = this._pingUnboundGetResult ? "Success!" : "Failed."; + let postPingResult = element.querySelector("#UnboundPostResult") as HTMLSpanElement; + postPingResult.textContent = this._pingUnboundPostResult ? "Success!" : "Failed."; + } + + public open(pingUnboundGetResult: boolean, pingUnboundPostResult: boolean): Promise { + let promise: Promise = new Promise((resolve: DialogResolve, reject: DialogReject) => { + this._resolve = resolve; + this._pingUnboundGetResult = pingUnboundGetResult; + this._pingUnboundPostResult = pingUnboundPostResult; + this.openDialog({ + title: "Ping Test Results", + button1: { + id: "btnOk", + label: this.context.resources.getString("string_2005"), + isPrimary: true, + onClick: this.closeDialogHandler.bind(this) + }, + onCloseX: () => this.closeDialogHandler() + }); + }); + + return promise; + } + + private closeDialogHandler(): boolean { + this.resolvePromise(); + return true; + } + + private resolvePromise(): void { + if (ObjectExtensions.isFunction(this._resolve)) { + this._resolve(null); + this._resolve = null; + } + } +} diff --git a/src/ScaleUnitSample/POS/Controls/Dialogs/Edit/ExampleEditDialog.html b/src/ScaleUnitSample/POS/Controls/Dialogs/Edit/ExampleEditDialog.html new file mode 100644 index 00000000..3a4a45a5 --- /dev/null +++ b/src/ScaleUnitSample/POS/Controls/Dialogs/Edit/ExampleEditDialog.html @@ -0,0 +1,25 @@ + + + + + Example Edit Dialog + + +
+
+
Int data:
+ +
+
+
String data:
+ +
+
+
Extension property string data:
+ +
+
+
+
+ + diff --git a/src/ScaleUnitSample/POS/Controls/Dialogs/Edit/ExampleEditDialogModule.ts b/src/ScaleUnitSample/POS/Controls/Dialogs/Edit/ExampleEditDialogModule.ts new file mode 100644 index 00000000..eb4f61da --- /dev/null +++ b/src/ScaleUnitSample/POS/Controls/Dialogs/Edit/ExampleEditDialogModule.ts @@ -0,0 +1,93 @@ +/** + * SAMPLE CODE NOTICE + * + * THIS SAMPLE CODE IS MADE AVAILABLE AS IS. MICROSOFT MAKES NO WARRANTIES, WHETHER EXPRESS OR IMPLIED, + * OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OR CONDITIONS OF MERCHANTABILITY. + * THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS SAMPLE CODE REMAINS WITH THE USER. + * NO TECHNICAL SUPPORT IS PROVIDED. YOU MAY NOT DISTRIBUTE THIS CODE UNLESS YOU HAVE A LICENSE AGREEMENT WITH MICROSOFT THAT ALLOWS YOU TO DO SO. + */ + +import * as Dialogs from "PosApi/Create/Dialogs"; +import { Entities } from "../../../DataService/DataServiceEntities.g"; +import { ObjectExtensions } from "PosApi/TypeExtensions"; + +type DialogResolve = (updatedEntity: Entities.ExampleEntity) => void; +type DialogReject = (reason: any) => void; + +export default class ExampleEditDialog extends Dialogs.ExtensionTemplatedDialogBase { + private _resolve: DialogResolve; + private _data: Entities.ExampleEntity; + + constructor() { + super(); + } + + public onReady(element: HTMLElement): void { + let intDataInput: HTMLInputElement = element.querySelector("#intData") as HTMLInputElement; + intDataInput.value = this._data.IntData.toString(); + intDataInput.onchange = () => { this._data.IntData = intDataInput.valueAsNumber; }; + + let stringDataInput: HTMLInputElement = element.querySelector("#stringData") as HTMLInputElement; + stringDataInput.value = this._data.StringData; + stringDataInput.onchange = () => { this._data.StringData = stringDataInput.value; }; + + let extensionPropertyStringDataInput: HTMLInputElement = element.querySelector("#extensionPropertyStringData") as HTMLInputElement; + extensionPropertyStringDataInput.value = this._data.ExtensionProperties.filter(prop => prop.Key == "customExtensionProp").map(prop => prop.Value.StringValue)[0]; + extensionPropertyStringDataInput.onchange = () => { + if (this._data.ExtensionProperties.length > 0) { + this._data.ExtensionProperties.filter(prop => prop.Key == "customExtensionProp")[0].Value.StringValue = extensionPropertyStringDataInput.value; + } else { + this._data.ExtensionProperties = [{ + Key: "customExtensionProp", + Value: { + StringValue: extensionPropertyStringDataInput.value + } + }]; + } + }; + } + + public open(dataToEdit: Entities.ExampleEntity): Promise { + this._data = dataToEdit; + + let promise: Promise = new Promise((resolve: DialogResolve, reject: DialogReject) => { + this._resolve = resolve; + let option: Dialogs.ITemplatedDialogOptions = { + title: "Update Example Entity", + button1: { + id: "btnUpdate", + label: this.context.resources.getString("string_2002"), + isPrimary: true, + onClick: this.btnUpdateClickHandler.bind(this) + }, + button2: { + id: "btnCancel", + label: this.context.resources.getString("string_2004"), + onClick: this.btnCancelClickHandler.bind(this) + }, + onCloseX: () => this.btnCancelClickHandler() + }; + + this.openDialog(option); + }); + + return promise; + } + + private btnUpdateClickHandler(): boolean { + this.resolvePromise(this._data); + return true; + } + + private btnCancelClickHandler(): boolean { + this.resolvePromise(null); + return true; + } + + private resolvePromise(editResult: Entities.ExampleEntity): void { + if (ObjectExtensions.isFunction(this._resolve)) { + this._resolve(editResult); + this._resolve = null; + } + } +} diff --git a/src/ScaleUnitSample/POS/POS.csproj b/src/ScaleUnitSample/POS/POS.csproj new file mode 100644 index 00000000..36529e25 --- /dev/null +++ b/src/ScaleUnitSample/POS/POS.csproj @@ -0,0 +1,16 @@ + + + + + netstandard2.0 + + + + + + + + + + + \ No newline at end of file diff --git a/src/ScaleUnitSample/POS/Resources/Strings/en-US/resources.resjson b/src/ScaleUnitSample/POS/Resources/Strings/en-US/resources.resjson new file mode 100644 index 00000000..66534f1e --- /dev/null +++ b/src/ScaleUnitSample/POS/Resources/Strings/en-US/resources.resjson @@ -0,0 +1,38 @@ +//====================================================================================================== +//======================================= Store Hour Sample comment. =================================== +//====================================================================================================== +{ + //======================== View extension titles. ======================== + "string_0001" : "EXAMPLE VIEW", + "_string_0001.comment" : "The Example View's title.", + + //======================== Column names. ======================== + "string_1001" : "Int data", + "_string_1001.comment" : "A column title for the int data on the Example Entity.", + + "string_1002" : "String data", + "_string_2.comment" : "A column title for the string data on the Example Entity.", + + "string_1003" : "Extension property string data", + "_string_3.comment" : "A column title for the extension property data on the Example Entity.", + + //======================== Dialog button labels. ======================== + "string_2001" : "Create", + "_string_4.comment" : "Create button used on a dialog for making a new entity.", + + "string_2002" : "Update", + "_string_5.comment" : "Update button used to commit changes made to an entity in an update dialog.", + + "string_1006" : "Delete", + "_string_6.comment" : "Delete button used to delete an entity instance in the list view.", + + "string_2004" : "Cancel", + "_string_7.comment" : "Cancel button shared across dialogs.", + + "string_2005" : "OK", + "_string_8.comment" : "OK button used to dismiss a dialog.", + + //======================== Command labels. ======================== + "string_3001" : "Ping Test", + "_string_3001.comment" : "A button label used in the app bar, when pressed two unbound endpoints are pinged." +} diff --git a/src/ScaleUnitSample/POS/ViewExtensions/Search/NavigateToExampleViewCommand.ts b/src/ScaleUnitSample/POS/ViewExtensions/Search/NavigateToExampleViewCommand.ts new file mode 100644 index 00000000..25d677d4 --- /dev/null +++ b/src/ScaleUnitSample/POS/ViewExtensions/Search/NavigateToExampleViewCommand.ts @@ -0,0 +1,42 @@ +/** + * SAMPLE CODE NOTICE + * + * THIS SAMPLE CODE IS MADE AVAILABLE AS IS. MICROSOFT MAKES NO WARRANTIES, WHETHER EXPRESS OR IMPLIED, + * OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OR CONDITIONS OF MERCHANTABILITY. + * THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS SAMPLE CODE REMAINS WITH THE USER. + * NO TECHNICAL SUPPORT IS PROVIDED. YOU MAY NOT DISTRIBUTE THIS CODE UNLESS YOU HAVE A LICENSE AGREEMENT WITH MICROSOFT THAT ALLOWS YOU TO DO SO. + */ + +import { IExtensionCommandContext } from "PosApi/Extend/Views/AppBarCommands"; +import * as SearchView from "PosApi/Extend/Views/SearchView"; + +export default class NavigateToExampleViewCommand extends SearchView.ProductSearchExtensionCommandBase { + /** + * Creates a new instance of the NavigateToExampleViewCommand class. + * @param {IExtensionCommandContext} context The command context. + * @remarks The command context contains APIs through which a command can communicate with POS. + */ + constructor(context: IExtensionCommandContext) { + super(context); + + this.id = "navigateToExampleViewCommand"; + this.label = "Navigate to Full System Example View"; + this.extraClass = "iconGo"; + } + + /** + * Initializes the command. + * @param {ProductDetailsView.IProductDetailsExtensionCommandState} state The state used to initialize the command. + */ + protected init(state: SearchView.IProductSearchExtensionCommandState): void { + this.canExecute = true; + this.isVisible = true; + } + + /** + * Executes the command. + */ + protected execute(): void { + this.context.navigator.navigate("ExampleView"); + } +} \ No newline at end of file diff --git a/src/ScaleUnitSample/POS/Views/ExampleView.html b/src/ScaleUnitSample/POS/Views/ExampleView.html new file mode 100644 index 00000000..f7b74479 --- /dev/null +++ b/src/ScaleUnitSample/POS/Views/ExampleView.html @@ -0,0 +1,13 @@ + + + + + Example View + + +
+
+
+
+ + diff --git a/src/ScaleUnitSample/POS/Views/ExampleView.ts b/src/ScaleUnitSample/POS/Views/ExampleView.ts new file mode 100644 index 00000000..48a74b64 --- /dev/null +++ b/src/ScaleUnitSample/POS/Views/ExampleView.ts @@ -0,0 +1,152 @@ +/** + * SAMPLE CODE NOTICE + * + * THIS SAMPLE CODE IS MADE AVAILABLE AS IS. MICROSOFT MAKES NO WARRANTIES, WHETHER EXPRESS OR IMPLIED, + * OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OR CONDITIONS OF MERCHANTABILITY. + * THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS SAMPLE CODE REMAINS WITH THE USER. + * NO TECHNICAL SUPPORT IS PROVIDED. YOU MAY NOT DISTRIBUTE THIS CODE UNLESS YOU HAVE A LICENSE AGREEMENT WITH MICROSOFT THAT ALLOWS YOU TO DO SO. + */ + +import * as Views from "PosApi/Create/Views"; +import { Entities } from "../DataService/DataServiceEntities.g"; +import ExampleViewModel from "./ExampleViewModel"; +import { IDataList, IDataListOptions, DataListInteractionMode } from "PosApi/Consume/Controls"; +import { ObjectExtensions, ArrayExtensions } from "PosApi/TypeExtensions"; + +/** + * The controller for ExampleView. + */ +export default class StoreHoursView extends Views.CustomViewControllerBase { + public readonly viewModel: ExampleViewModel; + public dataList: IDataList; + + constructor(context: Views.ICustomViewControllerContext) { + let config: Views.ICustomViewControllerConfiguration = { + title: context.resources.getString("string_0001"), + commandBar: { + commands: [ + { + name: "Create", + label: context.resources.getString("string_2001"), + icon: Views.Icons.Add, + isVisible: true, + canExecute: true, + execute: (args: Views.CustomViewControllerExecuteCommandArgs): void => { + this.viewModel.createExampleEntity().then((entityCreated) => { + if (entityCreated) { + // Re-load the list, since the underlying data was amended + this.dataList.data = this.viewModel.loadedData; + } + }); + } + }, + { + name: "Edit", + label: context.resources.getString("string_2002"), + icon: Views.Icons.Edit, + isVisible: true, + canExecute: false, + execute: (args: Views.CustomViewControllerExecuteCommandArgs): void => { + this.state.isProcessing = true; + this.viewModel.editExampleEntity().then((editsMade) => { + if (editsMade) { + // Re-load the list since the underlying data changed + this.dataList.data = this.viewModel.loadedData; + } + this.state.isProcessing = false; + }); + } + }, + { + name: "Delete", + label: context.resources.getString("string_1006"), + icon: Views.Icons.Delete, + isVisible: true, + canExecute: false, + execute: (args: Views.CustomViewControllerExecuteCommandArgs): void => { + this.state.isProcessing = true; + this.viewModel.deleteExampleEntity().then(() => { + // Re-load the list, since the data has changed + this.dataList.data = this.viewModel.loadedData; + this.state.isProcessing = false; + }); + } + }, + { + name: "PingTest", + label: context.resources.getString("string_3001"), + icon: Views.Icons.LightningBolt, + isVisible: true, + canExecute: true, + execute: (args: Views.CustomViewControllerExecuteCommandArgs): void => { + this.state.isProcessing = true; + this.viewModel.runPingTest().then(() => { + this.state.isProcessing = false; + }); + } + } + ] + } + }; + + super(context, config); + + // Initialize the view model. + this.viewModel = new ExampleViewModel(context); + } + + public dispose(): void { + ObjectExtensions.disposeAllProperties(this); + } + + public onReady(element: HTMLElement): void { + // DataList + let dataListOptions: IDataListOptions = { + interactionMode: DataListInteractionMode.SingleSelect, + data: this.viewModel.loadedData, + columns: [ + { + title: this.context.resources.getString("string_1001"), // Int data + ratio: 40, collapseOrder: 1, minWidth: 100, + computeValue: (data: Entities.ExampleEntity): string => data.IntData.toString() + }, + { + title: this.context.resources.getString("string_1002"), // String data + ratio: 30, collapseOrder: 2, minWidth: 100, + computeValue: (data: Entities.ExampleEntity): string => data.StringData + }, + { + title: this.context.resources.getString("string_1003"), // Extension property string data + ratio: 30, collapseOrder: 3, minWidth: 100, + computeValue: (data: Entities.ExampleEntity): string => { + return ArrayExtensions.firstOrUndefined( + data.ExtensionProperties.filter(prop => prop.Key == "customExtensionProp").map(prop => prop.Value.StringValue) + ); + } + } + ] + }; + + let dataListRootElem: HTMLDivElement = element.querySelector("#exampleListView") as HTMLDivElement; + this.dataList = this.context.controlFactory.create(this.context.logger.getNewCorrelationId(), "DataList", dataListOptions, dataListRootElem); + + this.dataList.addEventListener("SelectionChanged", (eventData: { items: Entities.ExampleEntity[] }) => { + this.viewModel.seletionChanged(eventData.items); + + // Update the command states to reflect the current selection state. + this.state.commandBar.commands.forEach( + command => command.canExecute = ( + ["Create", "PingTest"].some(name => name == command.name) || + this.viewModel.isItemSelected() + ) + ); + }); + + this.state.isProcessing = true; + this.viewModel.load().then((): void => { + // Initialize the data list with what the view model loaded + this.dataList.data = this.viewModel.loadedData; + this.state.isProcessing = false; + }); + } +} diff --git a/src/ScaleUnitSample/POS/Views/ExampleViewModel.ts b/src/ScaleUnitSample/POS/Views/ExampleViewModel.ts new file mode 100644 index 00000000..5aaa9791 --- /dev/null +++ b/src/ScaleUnitSample/POS/Views/ExampleViewModel.ts @@ -0,0 +1,139 @@ +/** + * SAMPLE CODE NOTICE + * + * THIS SAMPLE CODE IS MADE AVAILABLE AS IS. MICROSOFT MAKES NO WARRANTIES, WHETHER EXPRESS OR IMPLIED, + * OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OR CONDITIONS OF MERCHANTABILITY. + * THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS SAMPLE CODE REMAINS WITH THE USER. + * NO TECHNICAL SUPPORT IS PROVIDED. YOU MAY NOT DISTRIBUTE THIS CODE UNLESS YOU HAVE A LICENSE AGREEMENT WITH MICROSOFT THAT ALLOWS YOU TO DO SO. + */ + +import { IExtensionViewControllerContext } from "PosApi/Create/Views"; +import { Entities } from "../DataService/DataServiceEntities.g"; +import * as Messages from "../DataService/DataServiceRequests.g"; +import ExampleCreateDialog from "../Controls/Dialogs/Create/ExampleCreateDialogModule"; +import ExampleEditDialog from "../Controls/Dialogs/Edit/ExampleEditDialogModule"; +import PingResultDialog from "../Controls/Dialogs/Display/PingResultDialogModule"; +import { ObjectExtensions, ArrayExtensions } from "PosApi/TypeExtensions"; + +/** + * The ViewModel for ExampleView. + */ +export default class ExampleViewModel { + public title: string; + public loadedData: Entities.ExampleEntity[]; + public isItemSelected: () => boolean; + private _selectedItem: Entities.ExampleEntity; + private _context: IExtensionViewControllerContext; + + constructor(context: IExtensionViewControllerContext) { + this._context = context; + this.title = context.resources.getString("string_0001"); + this.loadedData = []; + this.isItemSelected = () => !ObjectExtensions.isNullOrUndefined(this._selectedItem); + } + + public load(): Promise { + return this._context.runtime + .executeAsync(new Messages.BoundController.GetAllExampleEntitiesRequest()) + .then(response => { + if (!response.canceled) { + this.loadedData = response.data.result; + } + }); + } + + /** + * Handler for list item selection. + * @param {Entities.ExampleEntity[]} items + */ + public seletionChanged(items: Entities.ExampleEntity[]): Promise { + this._context.logger.logInformational("Item selected:" + JSON.stringify(items)); + this._selectedItem = ArrayExtensions.firstOrUndefined(items); + return Promise.resolve(); + } + + public createExampleEntity(): Promise { + let dialog: ExampleCreateDialog = new ExampleCreateDialog(); + return dialog + .open() + .then(newItem => { + // No action if the dialog was canceled + if (ObjectExtensions.isNullOrUndefined(newItem)) { + this._context.logger.logInformational("Create canceled."); + return Promise.resolve(false);; + } + + this._context.logger.logInformational("Item created with data: " + JSON.stringify(newItem)); + // Create the entity and reload the loaded data to reflect the change: + return this._context.runtime + .executeAsync(new Messages.BoundController.CreateExampleEntityRequest(newItem)) + .then(response => { + if (!response.canceled && response.data.result != 0) { + this._context.logger.logInformational("Create success for id: " + response.data.result); + return this.load().then((): boolean => true); // Load the updated data + } + this._context.logger.logInformational("Create failed for entity: " + JSON.stringify(newItem)); + return Promise.resolve(false); + }); + }).catch(reason => { + this._context.logger.logError("Error occurred in the create dialog: " + JSON.stringify(reason)); + return Promise.resolve(false);; + }); + } + + public editExampleEntity(): Promise { + let dialog: ExampleEditDialog = new ExampleEditDialog(); + return dialog + .open(this._selectedItem) + .then(updatedItem => { + // No action if the dialog was canceled + if (ObjectExtensions.isNullOrUndefined(updatedItem)) { + this._context.logger.logInformational("Update canceled for data: " + JSON.stringify(updatedItem)); + return Promise.resolve(false); + } + + this._context.logger.logInformational("Updated data is: " + JSON.stringify(updatedItem)); + // Perform the update and reload the loaded data to reflect the change: + return this._context.runtime + .executeAsync(new Messages.BoundController.UpdateExampleEntityRequest(updatedItem.UnusualEntityId, updatedItem)) + .then(response => { + if (!response.canceled && response.data.result) { + this._context.logger.logInformational("Update success for id: " + updatedItem.UnusualEntityId); + return this.load().then((): boolean => true); // Load the updated data + } + this._context.logger.logInformational("Update failed for id: " + updatedItem.UnusualEntityId); + return Promise.resolve(false); + }); + }).catch(reason => { + this._context.logger.logError("Error occurred in the edit dialog: " + JSON.stringify(reason)); + return Promise.resolve(false); + }); + } + + public deleteExampleEntity(): Promise { + // Delete the selected entity and reload the loaded data to reflect the change: + return this._context.runtime + .executeAsync(new Messages.BoundController.DeleteExampleEntityRequest(this._selectedItem.UnusualEntityId)) + .then(response => { + if (!response.canceled && response.data.result) { + this._context.logger.logInformational("Delete success for id: " + this._selectedItem.UnusualEntityId); + return this.load(); // Load the updated data + } + this._context.logger.logInformational("Delete failed for id " + this._selectedItem.UnusualEntityId); + return Promise.resolve(); + }); + } + + public runPingTest(): Promise { + return this._context.runtime + .executeAsync(new Messages.StoreOperations.SimplePingGetRequest()) + .then(pingGetResponse => { + return this._context.runtime + .executeAsync(new Messages.StoreOperations.SimplePingPostRequest()) + .then(pingPostResponse => { + let pingResultDialog: PingResultDialog = new PingResultDialog(); + return pingResultDialog.open(pingGetResponse.data.result, pingPostResponse.data.result); + }); + }); + } +} diff --git a/src/ScaleUnitSample/POS/manifest.json b/src/ScaleUnitSample/POS/manifest.json new file mode 100644 index 00000000..dd369c0b --- /dev/null +++ b/src/ScaleUnitSample/POS/manifest.json @@ -0,0 +1,50 @@ +{ + "$schema": "./devDependencies/schemas/manifestSchema.json", + "name": "Contoso.Pos.FullSystemExample.Sample", + "publisher": "Contoso", + "version": "1.0.0", + "minimumPosVersion": "9.29.0.0", + "description": "An extension package that makes use of a Retail Server extension. Also works off-line.", + "components": { + "resources": { + "supportedUICultures": [ "en-US" ], + "fallbackUICulture": "en-US", + "culturesDirectoryPath": "Resources/Strings", + "stringResourcesFileName": "resources.resjson" + }, + "extend": { + "views": { + "SearchView": { + "productAppBarCommands": [ + { "modulePath": "ViewExtensions/Search/NavigateToExampleViewCommand" }, + ] + } + } + }, + "create": { + "templatedDialogs": [ + { + "htmlPath": "Controls/Dialogs/Create/ExampleCreateDialog.html", + "modulePath": "Controls/Dialogs/Create/ExampleCreateDialogModule" + }, + { + "htmlPath": "Controls/Dialogs/Edit/ExampleEditDialog.html", + "modulePath": "Controls/Dialogs/Edit/ExampleEditDialogModule" + }, + { + "htmlPath": "Controls/Dialogs/Display/PingResultDialog.html", + "modulePath": "Controls/Dialogs/Display/PingResultDialogModule" + } + ], + "views": [ + { + "title": "Sample Extension View", + "pageName": "ExampleView", + "phonePageName": "ExampleView", + "viewDirectory": "Views/", + "viewControllerPath": "Views/ExampleView" + } + ] + } + } +} diff --git a/src/ScaleUnitSample/POS/tsconfig.json b/src/ScaleUnitSample/POS/tsconfig.json new file mode 100644 index 00000000..37d9122c --- /dev/null +++ b/src/ScaleUnitSample/POS/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./devDependencies/pos-tsconfig-base.json" +} \ No newline at end of file diff --git a/src/ScaleUnitSample/ScaleUnit.sln b/src/ScaleUnitSample/ScaleUnit.sln new file mode 100644 index 00000000..ad7d4ec3 --- /dev/null +++ b/src/ScaleUnitSample/ScaleUnit.sln @@ -0,0 +1,49 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.1267 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChannelDatabase", "ChannelDatabase\ChannelDatabase.csproj", "{A8166D6A-9868-43C8-8732-0D2DD6C9826C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommerceRuntime", "CommerceRuntime\CommerceRuntime.csproj", "{59AA9A4E-084B-4DF4-83D2-151759F6944F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScaleUnit", "ScaleUnit\ScaleUnit.csproj", "{2888FAB4-68EF-4670-ABFF-20E1DBAA32F4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "POS", "POS\POS.csproj", "{F69DE011-47C5-46FC-A9E0-88A62650B082}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScaleUnit.Installer", "Installer\ScaleUnit.Installer.csproj", "{E937C87D-E159-44F0-AD36-DEAA6D72E678}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A8166D6A-9868-43C8-8732-0D2DD6C9826C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A8166D6A-9868-43C8-8732-0D2DD6C9826C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8166D6A-9868-43C8-8732-0D2DD6C9826C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A8166D6A-9868-43C8-8732-0D2DD6C9826C}.Release|Any CPU.Build.0 = Release|Any CPU + {59AA9A4E-084B-4DF4-83D2-151759F6944F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {59AA9A4E-084B-4DF4-83D2-151759F6944F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {59AA9A4E-084B-4DF4-83D2-151759F6944F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {59AA9A4E-084B-4DF4-83D2-151759F6944F}.Release|Any CPU.Build.0 = Release|Any CPU + {2888FAB4-68EF-4670-ABFF-20E1DBAA32F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2888FAB4-68EF-4670-ABFF-20E1DBAA32F4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2888FAB4-68EF-4670-ABFF-20E1DBAA32F4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2888FAB4-68EF-4670-ABFF-20E1DBAA32F4}.Release|Any CPU.Build.0 = Release|Any CPU + {F69DE011-47C5-46FC-A9E0-88A62650B082}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F69DE011-47C5-46FC-A9E0-88A62650B082}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F69DE011-47C5-46FC-A9E0-88A62650B082}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F69DE011-47C5-46FC-A9E0-88A62650B082}.Release|Any CPU.Build.0 = Release|Any CPU + {E937C87D-E159-44F0-AD36-DEAA6D72E678}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E937C87D-E159-44F0-AD36-DEAA6D72E678}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E937C87D-E159-44F0-AD36-DEAA6D72E678}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E937C87D-E159-44F0-AD36-DEAA6D72E678}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D8D07A3D-D311-441B-A70E-A959154EE41B} + EndGlobalSection +EndGlobal diff --git a/src/ScaleUnitSample/ScaleUnit/ScaleUnit.csproj b/src/ScaleUnitSample/ScaleUnit/ScaleUnit.csproj new file mode 100644 index 00000000..aad57798 --- /dev/null +++ b/src/ScaleUnitSample/ScaleUnit/ScaleUnit.csproj @@ -0,0 +1,23 @@ + + + + + netstandard2.0 + + + + + false + + + false + + + false + + + + + + + \ No newline at end of file