├── README.md ├── Solution ├── Exercise2 │ └── AppOwnsData │ │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ │ ├── AppOwnsData.csproj │ │ ├── AppOwnsData.sln │ │ ├── Controllers │ │ └── HomeController.cs │ │ ├── Models │ │ └── ErrorViewModel.cs │ │ ├── Program.cs │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── Startup.cs │ │ ├── Views │ │ ├── Home │ │ │ ├── Embed.cshtml │ │ │ └── Index.cshtml │ │ ├── Shared │ │ │ ├── Error.cshtml │ │ │ ├── _Layout.cshtml │ │ │ ├── _LoginPartial.cshtml │ │ │ └── _ValidationScriptsPartial.cshtml │ │ ├── _ViewImports.cshtml │ │ └── _ViewStart.cshtml │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ └── wwwroot │ │ ├── css │ │ └── site.css │ │ ├── favicon.ico │ │ ├── js │ │ └── site.js │ │ └── lib │ │ ├── bootstrap │ │ ├── LICENSE │ │ └── dist │ │ │ ├── css │ │ │ ├── bootstrap-grid.css │ │ │ ├── bootstrap-grid.css.map │ │ │ ├── bootstrap-grid.min.css │ │ │ ├── bootstrap-grid.min.css.map │ │ │ ├── bootstrap-reboot.css │ │ │ ├── bootstrap-reboot.css.map │ │ │ ├── bootstrap-reboot.min.css │ │ │ ├── bootstrap-reboot.min.css.map │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.css.map │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ │ └── js │ │ │ ├── bootstrap.bundle.js │ │ │ ├── bootstrap.bundle.js.map │ │ │ ├── bootstrap.bundle.min.js │ │ │ ├── bootstrap.bundle.min.js.map │ │ │ ├── bootstrap.js │ │ │ ├── bootstrap.js.map │ │ │ ├── bootstrap.min.js │ │ │ └── bootstrap.min.js.map │ │ ├── jquery-validation-unobtrusive │ │ ├── LICENSE.txt │ │ ├── jquery.validate.unobtrusive.js │ │ └── jquery.validate.unobtrusive.min.js │ │ ├── jquery-validation │ │ ├── LICENSE.md │ │ └── dist │ │ │ ├── additional-methods.js │ │ │ ├── additional-methods.min.js │ │ │ ├── jquery.validate.js │ │ │ └── jquery.validate.min.js │ │ └── jquery │ │ ├── LICENSE.txt │ │ └── dist │ │ ├── jquery.js │ │ ├── jquery.min.js │ │ └── jquery.min.map ├── Exercise3 │ └── AppOwnsData │ │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ │ ├── AppOwnsData.csproj │ │ ├── AppOwnsData.sln │ │ ├── Controllers │ │ └── HomeController.cs │ │ ├── Models │ │ └── ErrorViewModel.cs │ │ ├── Program.cs │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── Services │ │ └── PowerBiServiceApi.cs │ │ ├── Startup.cs │ │ ├── Views │ │ ├── Home │ │ │ ├── Embed.cshtml │ │ │ └── Index.cshtml │ │ ├── Shared │ │ │ ├── Error.cshtml │ │ │ ├── _Layout.cshtml │ │ │ ├── _LoginPartial.cshtml │ │ │ └── _ValidationScriptsPartial.cshtml │ │ ├── _ViewImports.cshtml │ │ └── _ViewStart.cshtml │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ └── wwwroot │ │ ├── css │ │ └── site.css │ │ ├── favicon.ico │ │ ├── js │ │ └── site.js │ │ └── lib │ │ ├── bootstrap │ │ ├── LICENSE │ │ └── dist │ │ │ ├── css │ │ │ ├── bootstrap-grid.css │ │ │ ├── bootstrap-grid.css.map │ │ │ ├── bootstrap-grid.min.css │ │ │ ├── bootstrap-grid.min.css.map │ │ │ ├── bootstrap-reboot.css │ │ │ ├── bootstrap-reboot.css.map │ │ │ ├── bootstrap-reboot.min.css │ │ │ ├── bootstrap-reboot.min.css.map │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.css.map │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ │ └── js │ │ │ ├── bootstrap.bundle.js │ │ │ ├── bootstrap.bundle.js.map │ │ │ ├── bootstrap.bundle.min.js │ │ │ ├── bootstrap.bundle.min.js.map │ │ │ ├── bootstrap.js │ │ │ ├── bootstrap.js.map │ │ │ ├── bootstrap.min.js │ │ │ └── bootstrap.min.js.map │ │ ├── jquery-validation-unobtrusive │ │ ├── LICENSE.txt │ │ ├── jquery.validate.unobtrusive.js │ │ └── jquery.validate.unobtrusive.min.js │ │ ├── jquery-validation │ │ ├── LICENSE.md │ │ └── dist │ │ │ ├── additional-methods.js │ │ │ ├── additional-methods.min.js │ │ │ ├── jquery.validate.js │ │ │ └── jquery.validate.min.js │ │ └── jquery │ │ ├── LICENSE.txt │ │ └── dist │ │ ├── jquery.js │ │ ├── jquery.min.js │ │ └── jquery.min.map ├── Exercise4 │ └── AppOwnsData │ │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ │ ├── AppOwnsData.csproj │ │ ├── AppOwnsData.sln │ │ ├── Controllers │ │ └── HomeController.cs │ │ ├── Models │ │ └── ErrorViewModel.cs │ │ ├── Program.cs │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── Services │ │ └── PowerBiServiceApi.cs │ │ ├── Startup.cs │ │ ├── Views │ │ ├── Home │ │ │ ├── Embed.cshtml │ │ │ └── Index.cshtml │ │ ├── Shared │ │ │ ├── Error.cshtml │ │ │ ├── _Layout.cshtml │ │ │ ├── _LoginPartial.cshtml │ │ │ └── _ValidationScriptsPartial.cshtml │ │ ├── _ViewImports.cshtml │ │ └── _ViewStart.cshtml │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ └── wwwroot │ │ ├── css │ │ └── site.css │ │ ├── favicon.ico │ │ ├── js │ │ ├── embed.js │ │ └── site.js │ │ └── lib │ │ ├── bootstrap │ │ ├── LICENSE │ │ └── dist │ │ │ ├── css │ │ │ ├── bootstrap-grid.css │ │ │ ├── bootstrap-grid.css.map │ │ │ ├── bootstrap-grid.min.css │ │ │ ├── bootstrap-grid.min.css.map │ │ │ ├── bootstrap-reboot.css │ │ │ ├── bootstrap-reboot.css.map │ │ │ ├── bootstrap-reboot.min.css │ │ │ ├── bootstrap-reboot.min.css.map │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.css.map │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ │ └── js │ │ │ ├── bootstrap.bundle.js │ │ │ ├── bootstrap.bundle.js.map │ │ │ ├── bootstrap.bundle.min.js │ │ │ ├── bootstrap.bundle.min.js.map │ │ │ ├── bootstrap.js │ │ │ ├── bootstrap.js.map │ │ │ ├── bootstrap.min.js │ │ │ └── bootstrap.min.js.map │ │ ├── jquery-validation-unobtrusive │ │ ├── LICENSE.txt │ │ ├── jquery.validate.unobtrusive.js │ │ └── jquery.validate.unobtrusive.min.js │ │ ├── jquery-validation │ │ ├── LICENSE.md │ │ └── dist │ │ │ ├── additional-methods.js │ │ │ ├── additional-methods.min.js │ │ │ ├── jquery.validate.js │ │ │ └── jquery.validate.min.js │ │ └── jquery │ │ ├── LICENSE.txt │ │ └── dist │ │ ├── jquery.js │ │ ├── jquery.min.js │ │ └── jquery.min.map ├── Exercise5 │ └── AppOwnsData │ │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ │ ├── AppOwnsData.csproj │ │ ├── AppOwnsData.sln │ │ ├── Controllers │ │ └── HomeController.cs │ │ ├── Models │ │ └── ErrorViewModel.cs │ │ ├── Program.cs │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── Scripts │ │ └── embed.ts │ │ ├── Services │ │ └── PowerBiServiceApi.cs │ │ ├── Startup.cs │ │ ├── Views │ │ ├── Home │ │ │ ├── Embed.cshtml │ │ │ └── Index.cshtml │ │ ├── Shared │ │ │ ├── Error.cshtml │ │ │ ├── _Layout.cshtml │ │ │ ├── _LoginPartial.cshtml │ │ │ └── _ValidationScriptsPartial.cshtml │ │ ├── _ViewImports.cshtml │ │ └── _ViewStart.cshtml │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ ├── package.json │ │ ├── tsconfig.json │ │ ├── webpack.config.js │ │ └── wwwroot │ │ ├── css │ │ └── site.css │ │ ├── favicon.ico │ │ ├── js │ │ ├── embed.js │ │ ├── embed.js.map │ │ └── site.js │ │ └── lib │ │ ├── bootstrap │ │ ├── LICENSE │ │ └── dist │ │ │ ├── css │ │ │ ├── bootstrap-grid.css │ │ │ ├── bootstrap-grid.css.map │ │ │ ├── bootstrap-grid.min.css │ │ │ ├── bootstrap-grid.min.css.map │ │ │ ├── bootstrap-reboot.css │ │ │ ├── bootstrap-reboot.css.map │ │ │ ├── bootstrap-reboot.min.css │ │ │ ├── bootstrap-reboot.min.css.map │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.css.map │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ │ └── js │ │ │ ├── bootstrap.bundle.js │ │ │ ├── bootstrap.bundle.js.map │ │ │ ├── bootstrap.bundle.min.js │ │ │ ├── bootstrap.bundle.min.js.map │ │ │ ├── bootstrap.js │ │ │ ├── bootstrap.js.map │ │ │ ├── bootstrap.min.js │ │ │ └── bootstrap.min.js.map │ │ ├── jquery-validation-unobtrusive │ │ ├── LICENSE.txt │ │ ├── jquery.validate.unobtrusive.js │ │ └── jquery.validate.unobtrusive.min.js │ │ ├── jquery-validation │ │ ├── LICENSE.md │ │ └── dist │ │ │ ├── additional-methods.js │ │ │ ├── additional-methods.min.js │ │ │ ├── jquery.validate.js │ │ │ └── jquery.validate.min.js │ │ └── jquery │ │ ├── LICENSE.txt │ │ └── dist │ │ ├── jquery.js │ │ ├── jquery.min.js │ │ └── jquery.min.map └── Final │ └── AppOwnsData │ ├── .vscode │ ├── launch.json │ └── tasks.json │ ├── AppOwnsData.csproj │ ├── AppOwnsData.sln │ ├── Controllers │ └── HomeController.cs │ ├── Models │ └── ErrorViewModel.cs │ ├── Program.cs │ ├── Properties │ └── launchSettings.json │ ├── Scripts │ ├── embed.js │ ├── embed.js.map │ └── embed.ts │ ├── Services │ └── PowerBiServiceApi.cs │ ├── Startup.cs │ ├── Views │ ├── Home │ │ ├── Embed.cshtml │ │ └── Index.cshtml │ ├── Shared │ │ ├── Error.cshtml │ │ ├── _Layout.cshtml │ │ ├── _LoginPartial.cshtml │ │ └── _ValidationScriptsPartial.cshtml │ ├── _ViewImports.cshtml │ └── _ViewStart.cshtml │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── obj │ └── Debug │ │ └── net5.0 │ │ ├── .NETCoreApp,Version=v5.0.AssemblyAttributes.cs │ │ ├── AppOwnsData.AssemblyInfo.cs │ │ ├── AppOwnsData.AssemblyInfoInputs.cache │ │ ├── AppOwnsData.GeneratedMSBuildEditorConfig.editorconfig │ │ ├── AppOwnsData.RazorAssemblyInfo.cache │ │ ├── AppOwnsData.RazorAssemblyInfo.cs │ │ ├── AppOwnsData.csprojAssemblyReference.cache │ │ └── project.razor.json │ ├── package.json │ ├── tsconfig.json │ ├── webpack.config.js │ └── wwwroot │ ├── css │ └── site.css │ ├── favicon.ico │ ├── js │ ├── embed.js │ ├── embed.js.map │ └── site.js │ └── lib │ ├── bootstrap │ ├── LICENSE │ └── dist │ │ ├── css │ │ ├── bootstrap-grid.css │ │ ├── bootstrap-grid.css.map │ │ ├── bootstrap-grid.min.css │ │ ├── bootstrap-grid.min.css.map │ │ ├── bootstrap-reboot.css │ │ ├── bootstrap-reboot.css.map │ │ ├── bootstrap-reboot.min.css │ │ ├── bootstrap-reboot.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ │ └── js │ │ ├── bootstrap.bundle.js │ │ ├── bootstrap.bundle.js.map │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.bundle.min.js.map │ │ ├── bootstrap.js │ │ ├── bootstrap.js.map │ │ ├── bootstrap.min.js │ │ └── bootstrap.min.js.map │ ├── jquery-validation-unobtrusive │ ├── LICENSE.txt │ ├── jquery.validate.unobtrusive.js │ └── jquery.validate.unobtrusive.min.js │ ├── jquery-validation │ ├── LICENSE.md │ └── dist │ │ ├── additional-methods.js │ │ ├── additional-methods.min.js │ │ ├── jquery.validate.js │ │ └── jquery.validate.min.js │ └── jquery │ ├── LICENSE.txt │ └── dist │ ├── jquery.js │ ├── jquery.min.js │ └── jquery.min.map ├── StudentLabFiles ├── Create-AzureAD-Application.ps1 ├── Create-AzureAD-Group.ps1 ├── Exercise 2 - Embed.cshtml.txt ├── Exercise 2 - HomeController.cs.txt ├── Exercise 2 - Startup.cs.txt ├── Exercise 2 - _Layout.cshtml.txt ├── Exercise 2 - _LoginPartial.cshtml.txt ├── Exercise 2 - index.cshtml.txt ├── Exercise 2 - site.css.txt ├── Exercise 3 - Embed.cshtml.txt ├── Exercise 3 - HomeController.cs.txt ├── Exercise 3 - PowerBiServiceApi.cs.txt ├── Exercise 4 - Embed.cshtml.txt ├── Exercise 4 - embed.js.txt ├── Exercise 5 - embed.ts.txt ├── Exercise 6 - Embed.cshtml.txt ├── Exercise 6 - Embed.ts.txt ├── Exercise 6 - HomeController.cs.txt ├── Exercise 6 - PowerBiServiceApi.cs.txt ├── Troubleshooting │ └── .vscode │ │ ├── launch.json │ │ └── tasks.json ├── favicon.ico ├── package.json ├── tsconfig.json └── webpack.config.js ├── Tutorial.docx └── Tutorial.pdf /README.md: -------------------------------------------------------------------------------- 1 | ## App-Owns-Data Embedding Tutorial with .NET 5 2 | This tutorial teaches developers how to create a .NET 5 MVC web applicaiton that implements Power BI embedding using the App-Owns-Data embedding model. 3 | 4 | ### Download the student manual for these hands-on lab exercises 5 | - [In DOCX Format](https://github.com/PowerBiDevCamp/DOTNET5-AppOwnsData-Tutorial/raw/master/Tutorial.docx) 6 | - [In PDF Format](https://github.com/PowerBiDevCamp/DOTNET5-AppOwnsData-Tutorial/raw/master/Tutorial.pdf) 7 | 8 | ### Download the student files for these hands-on lab exercises 9 | - [In ZIP Format](https://github.com/PowerBiDevCamp/DOTNET5-AppOwnsData-Tutorial/archive/master.zip) 10 | 11 | ### Lab exercises in this tutorial 12 | - Exercise 1: Create a New Azure AD Application 13 | - Exercise 2: Create a .NET 5 Project for a Secure Web Application 14 | - Exercise 3: Call the Power BI Service API 15 | - Exercise 4: Embed a Report using powerbi.js 16 | - Exercise 5: Add TypeScript Support to a .NET 5 Project 17 | - Exercise 6: Create a View Model for App Workspaces -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (web)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/net5.0/AppOwnsData.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | "stopAtEntry": false, 17 | // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser 18 | "serverReadyAction": { 19 | "action": "openExternally", 20 | "pattern": "\\bNow listening on:\\s+(https?://\\S+)" 21 | }, 22 | "env": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | }, 25 | "sourceFileMap": { 26 | "/Views": "${workspaceFolder}/Views" 27 | } 28 | }, 29 | { 30 | "name": ".NET Core Attach", 31 | "type": "coreclr", 32 | "request": "attach", 33 | "processId": "${command:pickProcess}" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/AppOwnsData.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/AppOwnsData.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "${workspaceFolder}/AppOwnsData.csproj", 36 | "/property:GenerateFullPaths=true", 37 | "/consoleloggerparameters:NoSummary" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/AppOwnsData.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | aspnet-AppOwnsData-74548193-D24B-4C99-9C55-AF543E13A630 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/AppOwnsData.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30804.86 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppOwnsData", "AppOwnsData.csproj", "{F3A2F8C1-120C-4B0E-9032-80958C1E01C0}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {F3A2F8C1-120C-4B0E-9032-80958C1E01C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {F3A2F8C1-120C-4B0E-9032-80958C1E01C0}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {F3A2F8C1-120C-4B0E-9032-80958C1E01C0}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {F3A2F8C1-120C-4B0E-9032-80958C1E01C0}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {CE4B4376-2FBD-4213-A882-438369972CF3} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Authorization; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.Extensions.Logging; 9 | using AppOwnsData.Models; 10 | 11 | namespace AppOwnsData.Controllers 12 | { 13 | [Authorize] 14 | public class HomeController : Controller 15 | { 16 | private readonly ILogger _logger; 17 | 18 | public HomeController(ILogger logger) 19 | { 20 | _logger = logger; 21 | } 22 | 23 | [AllowAnonymous] 24 | public IActionResult Index() 25 | { 26 | return View(); 27 | } 28 | 29 | public IActionResult Embed() 30 | { 31 | return View(); 32 | } 33 | 34 | [AllowAnonymous] 35 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 36 | public IActionResult Error() 37 | { 38 | return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AppOwnsData.Models 4 | { 5 | public class ErrorViewModel 6 | { 7 | public string RequestId { get; set; } 8 | 9 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace AppOwnsData 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:63983", 7 | "sslPort": 44325 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "AppOwnsData": { 19 | "commandName": "Project", 20 | "dotnetRunMessages": "true", 21 | "launchBrowser": true, 22 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 23 | "environmentVariables": { 24 | "ASPNETCORE_ENVIRONMENT": "Development" 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Authentication; 6 | using Microsoft.AspNetCore.Authentication.OpenIdConnect; 7 | using Microsoft.AspNetCore.Authorization; 8 | using Microsoft.Identity.Web; 9 | using Microsoft.Identity.Web.UI; 10 | using Microsoft.AspNetCore.Builder; 11 | using Microsoft.AspNetCore.Hosting; 12 | using Microsoft.AspNetCore.HttpsPolicy; 13 | using Microsoft.AspNetCore.Mvc.Authorization; 14 | using Microsoft.Extensions.Configuration; 15 | using Microsoft.Extensions.DependencyInjection; 16 | using Microsoft.Extensions.Hosting; 17 | 18 | namespace AppOwnsData 19 | { 20 | public class Startup 21 | { 22 | public Startup(IConfiguration configuration) 23 | { 24 | Configuration = configuration; 25 | } 26 | 27 | public IConfiguration Configuration { get; } 28 | 29 | // This method gets called by the runtime. Use this method to add services to the container. 30 | public void ConfigureServices(IServiceCollection services) 31 | { 32 | services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) 33 | .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd")); 34 | 35 | services.AddControllersWithViews(options => 36 | { 37 | var policy = new AuthorizationPolicyBuilder() 38 | .RequireAuthenticatedUser() 39 | .Build(); 40 | options.Filters.Add(new AuthorizeFilter(policy)); 41 | }); 42 | services.AddRazorPages() 43 | .AddMicrosoftIdentityUI(); 44 | } 45 | 46 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 47 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 48 | { 49 | if (env.IsDevelopment()) 50 | { 51 | app.UseDeveloperExceptionPage(); 52 | } 53 | else 54 | { 55 | app.UseExceptionHandler("/Home/Error"); 56 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 57 | app.UseHsts(); 58 | } 59 | app.UseHttpsRedirection(); 60 | app.UseStaticFiles(); 61 | 62 | app.UseRouting(); 63 | 64 | app.UseAuthentication(); 65 | app.UseAuthorization(); 66 | 67 | app.UseEndpoints(endpoints => 68 | { 69 | endpoints.MapControllerRoute( 70 | name: "default", 71 | pattern: "{controller=Home}/{action=Index}/{id?}"); 72 | endpoints.MapRazorPages(); 73 | }); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/Views/Home/Embed.cshtml: -------------------------------------------------------------------------------- 1 |

TODO: Embed Report Here

-------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Security.Principal 2 | 3 | @if (User.Identity.IsAuthenticated) { 4 |
5 |

Welcome @User.FindFirst("name").Value

6 |

You have now logged into this application.

7 |
8 | } 9 | else { 10 |
11 |

Welcome to the App-Owns-Data Tutorial

12 |

Click the sign in link in the upper right to get started

13 |
14 | } -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model ErrorViewModel 2 | @{ 3 | ViewData["Title"] = "Error"; 4 | } 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (Model.ShowRequestId) 10 | { 11 |

12 | Request ID: @Model.RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |

26 | -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | App-Owns-Data Demo 7 | 8 | 9 | 10 | 11 |
12 | 32 |
33 |
34 |
35 | @RenderBody() 36 |
37 |
38 | 39 | 40 | @RenderSection("Scripts", required: false) 41 | 42 | 43 | -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/Views/Shared/_LoginPartial.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Security.Principal 2 | 3 | 20 | -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/Views/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using AppOwnsData 2 | @using AppOwnsData.Models 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "AzureAd": { 3 | "Instance": "https://login.microsoftonline.com/", 4 | "Domain": "yourtenant.onMicrosoft.com", 5 | "TenantId": "", 6 | "ClientId": "", 7 | "ClientSecret": "", 8 | "CallbackPath": "/signin-oidc", 9 | "SignedOutCallbackPath": "/signout-callback-oidc" 10 | }, 11 | "PowerBi": { 12 | "ServiceRootUrl": "https://api.powerbi.com/" 13 | }, 14 | "Logging": { 15 | "LogLevel": { 16 | "Default": "Information", 17 | "Microsoft": "Warning", 18 | "Microsoft.Hosting.Lifetime": "Information" 19 | } 20 | }, 21 | "AllowedHosts": "*" 22 | } -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | /* App-Owns-Data Lab Styles from Power BI Dev Camp */ 2 | 3 | ul i { padding-right: 4px; } 4 | h1 { font-size: 1.35em; } 5 | h2 { font-size: 1.25em; } 6 | h3 { font-size: 1.1em; } 7 | h4 { font-size: 1.0em; } 8 | 9 | .mb-3, .my-3 { 10 | margin-bottom: 0 !important; 11 | } 12 | 13 | nav { 14 | margin-bottom: 0px; 15 | } 16 | 17 | i { 18 | margin-right: 4px; 19 | } 20 | 21 | 22 | #banner { 23 | padding-left: 0px; 24 | padding-right: 0px; 25 | } 26 | 27 | .jumbotron { 28 | background-color: #EDBE11; 29 | border: 1px solid #AAA; 30 | margin-top: 16px; 31 | } 32 | 33 | /* styles for embed page */ 34 | 35 | #content-box { 36 | padding-left: 0; 37 | padding-right: 0; 38 | border: solid 1px black; 39 | } 40 | 41 | #left-nav { 42 | background-color: black; 43 | color: white; 44 | padding: 0; 45 | border: 1px solid black; 46 | } 47 | 48 | #left-nav .navbar-header { 49 | color: black; 50 | background-color: #EDBE11; 51 | padding: 2px; 52 | padding-left: 4px; 53 | border-bottom: 1px solid #333333; 54 | font-size: 14px; 55 | } 56 | 57 | #workspace-selector { 58 | width: 100%; 59 | padding: 0; 60 | } 61 | 62 | #workspace-selector-dropdown { 63 | padding: 0px; 64 | } 65 | 66 | .dropdown-toggle::after { 67 | position: absolute; 68 | right: 8px; 69 | top: 45%; 70 | } 71 | 72 | #left-nav .navbar-collapse, #left-nav .dropdown { 73 | background-color: white; 74 | color: black; 75 | padding: 8px; 76 | font-size: 12px; 77 | border-bottom: 1px solid #333333; 78 | } 79 | 80 | #left-nav .navbar-collapse { 81 | padding: 8px; 82 | } 83 | 84 | #left-nav .dropdown { 85 | padding: 0px; 86 | } 87 | 88 | #left-nav .dropdown button { 89 | margin: 0px; 90 | padding: 4px; 91 | width: 100%; 92 | text-align: left; 93 | } 94 | 95 | 96 | #embed-container { 97 | min-height: 480px; 98 | height: 600px; 99 | } 100 | 101 | 102 | .no-content { 103 | font-style: italic; 104 | margin-left: 16px; 105 | color: #88AAAA; 106 | } 107 | 108 | #embedding-instructions { 109 | margin: 24px; 110 | color: #BBBBBB; 111 | } 112 | 113 | #embed-toolbar { 114 | margin-top: -1px; 115 | background-color: black; 116 | color: white; 117 | padding: 4px; 118 | font-size: 12px; 119 | } 120 | 121 | #embed-toolbar button { 122 | margin: 0px; 123 | font-size: 9px; 124 | font-weight: bold; 125 | padding: 2px; 126 | color: #222; 127 | background-color: #F2C811; 128 | border-color: #111; 129 | display: inline; 130 | float: right; 131 | margin-left: 8px; 132 | padding-left: 6px; 133 | padding-right: 6px; 134 | } 135 | 136 | .workspaces-toolbar { 137 | background-color: #CCCCCC; 138 | border: 1px solid #666666; 139 | border-top-style: none; 140 | border-bottom-left-radius: 4px; 141 | border-bottom-right-radius: 4px; 142 | padding: 8px; 143 | margin-bottom: 16px; 144 | text-align: left; 145 | } 146 | 147 | #create-workspace-button, #back-to-workspaces-button { 148 | background-color: #F2C811; 149 | color: black; 150 | border: 1px solid black; 151 | } 152 | 153 | table a.nav-link { 154 | margin: 0px; 155 | padding: 4px; 156 | padding-left: 6px; 157 | background-color: #F2C811; 158 | color: black; 159 | border: 1px solid black; 160 | border-radius: 4px; 161 | text-align: center; 162 | } 163 | 164 | .create-workspace-fieldset { 165 | background-color: #EEEEEE; 166 | border: solid 1px #AAAAAA; 167 | padding: 24px; 168 | } 169 | 170 | .create-workspace-fieldset legend { 171 | width: auto; 172 | padding: 8px; 173 | } 174 | -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerBiDevCamp/DOTNET5-AppOwnsData-Tutorial/ad38de0afe91ad41ce5af6f2b61554f8f4ae7040/Solution/Exercise2/AppOwnsData/wwwroot/favicon.ico -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | // for details on configuring this project to bundle and minify static web assets. 3 | 4 | // Write your JavaScript code. 5 | -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2018 Twitter, Inc. 4 | Copyright (c) 2011-2018 The Bootstrap Authors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v4.3.1 (https://getbootstrap.com/) 3 | * Copyright 2011-2019 The Bootstrap Authors 4 | * Copyright 2011-2019 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important} 8 | /*# sourceMappingURL=bootstrap-reboot.min.css.map */ -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Solution/Exercise2/AppOwnsData/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors, https://js.foundation/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (web)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/net5.0/AppOwnsData.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | "stopAtEntry": false, 17 | // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser 18 | "serverReadyAction": { 19 | "action": "openExternally", 20 | "pattern": "\\bNow listening on:\\s+(https?://\\S+)" 21 | }, 22 | "env": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | }, 25 | "sourceFileMap": { 26 | "/Views": "${workspaceFolder}/Views" 27 | } 28 | }, 29 | { 30 | "name": ".NET Core Attach", 31 | "type": "coreclr", 32 | "request": "attach", 33 | "processId": "${command:pickProcess}" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/AppOwnsData.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/AppOwnsData.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "${workspaceFolder}/AppOwnsData.csproj", 36 | "/property:GenerateFullPaths=true", 37 | "/consoleloggerparameters:NoSummary" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/AppOwnsData.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | aspnet-AppOwnsData-74548193-D24B-4C99-9C55-AF543E13A630 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/AppOwnsData.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30804.86 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppOwnsData", "AppOwnsData.csproj", "{F3A2F8C1-120C-4B0E-9032-80958C1E01C0}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {F3A2F8C1-120C-4B0E-9032-80958C1E01C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {F3A2F8C1-120C-4B0E-9032-80958C1E01C0}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {F3A2F8C1-120C-4B0E-9032-80958C1E01C0}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {F3A2F8C1-120C-4B0E-9032-80958C1E01C0}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {CE4B4376-2FBD-4213-A882-438369972CF3} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Authorization; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.Extensions.Logging; 9 | using AppOwnsData.Models; 10 | using AppOwnsData.Services; 11 | 12 | namespace AppOwnsData.Controllers 13 | { 14 | [Authorize] 15 | public class HomeController : Controller 16 | { 17 | private PowerBiServiceApi powerBiServiceApi; 18 | 19 | public HomeController(PowerBiServiceApi powerBiServiceApi) 20 | { 21 | this.powerBiServiceApi = powerBiServiceApi; 22 | } 23 | 24 | [AllowAnonymous] 25 | public IActionResult Index() 26 | { 27 | return View(); 28 | } 29 | 30 | public async Task Embed() { 31 | 32 | // replace these two GUIDs with the workspace ID and report ID you recorded earlier 33 | Guid workspaceId = new Guid("ddab987a-7941-4eb5-a084-370d422dfcac"); 34 | Guid reportId = new Guid("3e75d100-b21a-4129-8e84-6c6e90ccbd7e"); 35 | 36 | var viewModel = await powerBiServiceApi.GetReport(workspaceId, reportId); 37 | 38 | return View(viewModel); 39 | } 40 | 41 | [AllowAnonymous] 42 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 43 | public IActionResult Error() 44 | { 45 | return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AppOwnsData.Models 4 | { 5 | public class ErrorViewModel 6 | { 7 | public string RequestId { get; set; } 8 | 9 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace AppOwnsData 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:63983", 7 | "sslPort": 44325 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "AppOwnsData": { 19 | "commandName": "Project", 20 | "dotnetRunMessages": "true", 21 | "launchBrowser": true, 22 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 23 | "environmentVariables": { 24 | "ASPNETCORE_ENVIRONMENT": "Development" 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/Services/PowerBiServiceApi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Identity.Web; 6 | using Microsoft.Rest; 7 | using Microsoft.PowerBI.Api; 8 | using Microsoft.PowerBI.Api.Models; 9 | using Newtonsoft.Json; 10 | 11 | namespace AppOwnsData.Services { 12 | 13 | public class EmbeddedReportViewModel { 14 | public string Id; 15 | public string Name; 16 | public string EmbedUrl; 17 | public string Token; 18 | } 19 | 20 | public class PowerBiServiceApi { 21 | 22 | private ITokenAcquisition tokenAcquisition { get; } 23 | private string urlPowerBiServiceApiRoot { get; } 24 | 25 | public PowerBiServiceApi(IConfiguration configuration, ITokenAcquisition tokenAcquisition) { 26 | this.urlPowerBiServiceApiRoot = configuration["PowerBi:ServiceRootUrl"]; 27 | this.tokenAcquisition = tokenAcquisition; 28 | } 29 | 30 | public const string powerbiApiDefaultScope = "https://analysis.windows.net/powerbi/api/.default"; 31 | 32 | public string GetAccessToken() { 33 | return this.tokenAcquisition.GetAccessTokenForAppAsync(powerbiApiDefaultScope).Result; 34 | } 35 | 36 | public PowerBIClient GetPowerBiClient() { 37 | var tokenCredentials = new TokenCredentials(GetAccessToken(), "Bearer"); 38 | return new PowerBIClient(new Uri(urlPowerBiServiceApiRoot), tokenCredentials); 39 | } 40 | 41 | public async Task GetReport(Guid WorkspaceId, Guid ReportId) { 42 | 43 | PowerBIClient pbiClient = GetPowerBiClient(); 44 | 45 | // call to Power BI Service API to get embedding data 46 | var report = await pbiClient.Reports.GetReportInGroupAsync(WorkspaceId, ReportId); 47 | 48 | // generate read-only embed token for the report 49 | var datasetId = report.DatasetId; 50 | var tokenRequest = new GenerateTokenRequest(TokenAccessLevel.View, datasetId); 51 | var embedTokenResponse = await pbiClient.Reports.GenerateTokenAsync(WorkspaceId, ReportId, tokenRequest); 52 | var embedToken = embedTokenResponse.Token; 53 | 54 | // return report embedding data to caller 55 | return new EmbeddedReportViewModel { 56 | Id = report.Id.ToString(), 57 | EmbedUrl = report.EmbedUrl, 58 | Name = report.Name, 59 | Token = embedToken 60 | }; 61 | } 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Authentication; 6 | using Microsoft.AspNetCore.Authentication.OpenIdConnect; 7 | using Microsoft.AspNetCore.Authorization; 8 | using Microsoft.Identity.Web; 9 | using Microsoft.Identity.Web.UI; 10 | using Microsoft.AspNetCore.Builder; 11 | using Microsoft.AspNetCore.Hosting; 12 | using Microsoft.AspNetCore.HttpsPolicy; 13 | using Microsoft.AspNetCore.Mvc.Authorization; 14 | using Microsoft.Extensions.Configuration; 15 | using Microsoft.Extensions.DependencyInjection; 16 | using Microsoft.Extensions.Hosting; 17 | using AppOwnsData.Services; 18 | 19 | namespace AppOwnsData 20 | { 21 | public class Startup 22 | { 23 | public Startup(IConfiguration configuration) 24 | { 25 | Configuration = configuration; 26 | } 27 | 28 | public IConfiguration Configuration { get; } 29 | 30 | // This method gets called by the runtime. Use this method to add services to the container. 31 | public void ConfigureServices(IServiceCollection services) { 32 | 33 | services.AddMicrosoftIdentityWebAppAuthentication(Configuration) 34 | .EnableTokenAcquisitionToCallDownstreamApi() 35 | .AddInMemoryTokenCaches(); 36 | 37 | services.AddScoped(typeof(PowerBiServiceApi)); 38 | 39 | services.AddControllersWithViews(options => { 40 | var policy = new AuthorizationPolicyBuilder() 41 | .RequireAuthenticatedUser() 42 | .Build(); 43 | options.Filters.Add(new AuthorizeFilter(policy)); 44 | }); 45 | services.AddRazorPages() 46 | .AddMicrosoftIdentityUI(); 47 | } 48 | 49 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 50 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 51 | { 52 | if (env.IsDevelopment()) 53 | { 54 | app.UseDeveloperExceptionPage(); 55 | } 56 | else 57 | { 58 | app.UseExceptionHandler("/Home/Error"); 59 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 60 | app.UseHsts(); 61 | } 62 | app.UseHttpsRedirection(); 63 | app.UseStaticFiles(); 64 | 65 | app.UseRouting(); 66 | 67 | app.UseAuthentication(); 68 | app.UseAuthorization(); 69 | 70 | app.UseEndpoints(endpoints => 71 | { 72 | endpoints.MapControllerRoute( 73 | name: "default", 74 | pattern: "{controller=Home}/{action=Index}/{id?}"); 75 | endpoints.MapRazorPages(); 76 | }); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/Views/Home/Embed.cshtml: -------------------------------------------------------------------------------- 1 | @model AppOwnsData.Services.EmbeddedReportViewModel; 2 | 3 | 11 | 12 |

Report View Model Data

13 | 14 | 15 | 16 | 17 | 18 | 19 |
Report Name@Model.Name
Report ID@Model.Id
Embed Url@Model.EmbedUrl
Embed Token@Model.Token
20 | 21 | -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Security.Principal 2 | 3 | @if (User.Identity.IsAuthenticated) { 4 |
5 |

Welcome @User.FindFirst("name").Value

6 |

You have now logged into this application.

7 |
8 | } 9 | else { 10 |
11 |

Welcome to the App-Owns-Data Tutorial

12 |

Click the sign in link in the upper right to get started

13 |
14 | } -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model ErrorViewModel 2 | @{ 3 | ViewData["Title"] = "Error"; 4 | } 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (Model.ShowRequestId) 10 | { 11 |

12 | Request ID: @Model.RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |

26 | -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | App-Owns-Data Demo 7 | 8 | 9 | 10 | 11 |
12 | 32 |
33 |
34 |
35 | @RenderBody() 36 |
37 |
38 | 39 | 40 | @RenderSection("Scripts", required: false) 41 | 42 | 43 | -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/Views/Shared/_LoginPartial.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Security.Principal 2 | 3 | 20 | -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/Views/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using AppOwnsData 2 | @using AppOwnsData.Models 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "AzureAd": { 3 | "Instance": "https://login.microsoftonline.com/", 4 | "Domain": "yourtenant.onMicrosoft.com", 5 | "TenantId": "", 6 | "ClientId": "", 7 | "ClientSecret": "", 8 | "CallbackPath": "/signin-oidc", 9 | "SignedOutCallbackPath": "/signout-callback-oidc" 10 | }, 11 | "PowerBi": { 12 | "ServiceRootUrl": "https://api.powerbi.com/" 13 | }, 14 | "Logging": { 15 | "LogLevel": { 16 | "Default": "Information", 17 | "Microsoft": "Warning", 18 | "Microsoft.Hosting.Lifetime": "Information" 19 | } 20 | }, 21 | "AllowedHosts": "*" 22 | } -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | /* App-Owns-Data Lab Styles from Power BI Dev Camp */ 2 | 3 | ul i { padding-right: 4px; } 4 | h1 { font-size: 1.35em; } 5 | h2 { font-size: 1.25em; } 6 | h3 { font-size: 1.1em; } 7 | h4 { font-size: 1.0em; } 8 | 9 | .mb-3, .my-3 { 10 | margin-bottom: 0 !important; 11 | } 12 | 13 | nav { 14 | margin-bottom: 0px; 15 | } 16 | 17 | i { 18 | margin-right: 4px; 19 | } 20 | 21 | 22 | #banner { 23 | padding-left: 0px; 24 | padding-right: 0px; 25 | } 26 | 27 | .jumbotron { 28 | background-color: #EDBE11; 29 | border: 1px solid #AAA; 30 | margin-top: 16px; 31 | } 32 | 33 | /* styles for embed page */ 34 | 35 | #content-box { 36 | padding-left: 0; 37 | padding-right: 0; 38 | border: solid 1px black; 39 | } 40 | 41 | #left-nav { 42 | background-color: black; 43 | color: white; 44 | padding: 0; 45 | border: 1px solid black; 46 | } 47 | 48 | #left-nav .navbar-header { 49 | color: black; 50 | background-color: #EDBE11; 51 | padding: 2px; 52 | padding-left: 4px; 53 | border-bottom: 1px solid #333333; 54 | font-size: 14px; 55 | } 56 | 57 | #workspace-selector { 58 | width: 100%; 59 | padding: 0; 60 | } 61 | 62 | #workspace-selector-dropdown { 63 | padding: 0px; 64 | } 65 | 66 | .dropdown-toggle::after { 67 | position: absolute; 68 | right: 8px; 69 | top: 45%; 70 | } 71 | 72 | #left-nav .navbar-collapse, #left-nav .dropdown { 73 | background-color: white; 74 | color: black; 75 | padding: 8px; 76 | font-size: 12px; 77 | border-bottom: 1px solid #333333; 78 | } 79 | 80 | #left-nav .navbar-collapse { 81 | padding: 8px; 82 | } 83 | 84 | #left-nav .dropdown { 85 | padding: 0px; 86 | } 87 | 88 | #left-nav .dropdown button { 89 | margin: 0px; 90 | padding: 4px; 91 | width: 100%; 92 | text-align: left; 93 | } 94 | 95 | 96 | #embed-container { 97 | min-height: 480px; 98 | height: 600px; 99 | } 100 | 101 | 102 | .no-content { 103 | font-style: italic; 104 | margin-left: 16px; 105 | color: #88AAAA; 106 | } 107 | 108 | #embedding-instructions { 109 | margin: 24px; 110 | color: #BBBBBB; 111 | } 112 | 113 | #embed-toolbar { 114 | margin-top: -1px; 115 | background-color: black; 116 | color: white; 117 | padding: 4px; 118 | font-size: 12px; 119 | } 120 | 121 | #embed-toolbar button { 122 | margin: 0px; 123 | font-size: 9px; 124 | font-weight: bold; 125 | padding: 2px; 126 | color: #222; 127 | background-color: #F2C811; 128 | border-color: #111; 129 | display: inline; 130 | float: right; 131 | margin-left: 8px; 132 | padding-left: 6px; 133 | padding-right: 6px; 134 | } 135 | 136 | .workspaces-toolbar { 137 | background-color: #CCCCCC; 138 | border: 1px solid #666666; 139 | border-top-style: none; 140 | border-bottom-left-radius: 4px; 141 | border-bottom-right-radius: 4px; 142 | padding: 8px; 143 | margin-bottom: 16px; 144 | text-align: left; 145 | } 146 | 147 | #create-workspace-button, #back-to-workspaces-button { 148 | background-color: #F2C811; 149 | color: black; 150 | border: 1px solid black; 151 | } 152 | 153 | table a.nav-link { 154 | margin: 0px; 155 | padding: 4px; 156 | padding-left: 6px; 157 | background-color: #F2C811; 158 | color: black; 159 | border: 1px solid black; 160 | border-radius: 4px; 161 | text-align: center; 162 | } 163 | 164 | .create-workspace-fieldset { 165 | background-color: #EEEEEE; 166 | border: solid 1px #AAAAAA; 167 | padding: 24px; 168 | } 169 | 170 | .create-workspace-fieldset legend { 171 | width: auto; 172 | padding: 8px; 173 | } 174 | -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerBiDevCamp/DOTNET5-AppOwnsData-Tutorial/ad38de0afe91ad41ce5af6f2b61554f8f4ae7040/Solution/Exercise3/AppOwnsData/wwwroot/favicon.ico -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | // for details on configuring this project to bundle and minify static web assets. 3 | 4 | // Write your JavaScript code. 5 | -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2018 Twitter, Inc. 4 | Copyright (c) 2011-2018 The Bootstrap Authors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v4.3.1 (https://getbootstrap.com/) 3 | * Copyright 2011-2019 The Bootstrap Authors 4 | * Copyright 2011-2019 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important} 8 | /*# sourceMappingURL=bootstrap-reboot.min.css.map */ -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Solution/Exercise3/AppOwnsData/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors, https://js.foundation/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (web)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/net5.0/AppOwnsData.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | "stopAtEntry": false, 17 | // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser 18 | "serverReadyAction": { 19 | "action": "openExternally", 20 | "pattern": "\\bNow listening on:\\s+(https?://\\S+)" 21 | }, 22 | "env": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | }, 25 | "sourceFileMap": { 26 | "/Views": "${workspaceFolder}/Views" 27 | } 28 | }, 29 | { 30 | "name": ".NET Core Attach", 31 | "type": "coreclr", 32 | "request": "attach", 33 | "processId": "${command:pickProcess}" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/AppOwnsData.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/AppOwnsData.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "${workspaceFolder}/AppOwnsData.csproj", 36 | "/property:GenerateFullPaths=true", 37 | "/consoleloggerparameters:NoSummary" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/AppOwnsData.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | aspnet-AppOwnsData-74548193-D24B-4C99-9C55-AF543E13A630 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/AppOwnsData.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30804.86 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppOwnsData", "AppOwnsData.csproj", "{F3A2F8C1-120C-4B0E-9032-80958C1E01C0}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {F3A2F8C1-120C-4B0E-9032-80958C1E01C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {F3A2F8C1-120C-4B0E-9032-80958C1E01C0}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {F3A2F8C1-120C-4B0E-9032-80958C1E01C0}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {F3A2F8C1-120C-4B0E-9032-80958C1E01C0}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {CE4B4376-2FBD-4213-A882-438369972CF3} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Authorization; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.Extensions.Logging; 9 | using AppOwnsData.Models; 10 | using AppOwnsData.Services; 11 | 12 | namespace AppOwnsData.Controllers 13 | { 14 | [Authorize] 15 | public class HomeController : Controller 16 | { 17 | private PowerBiServiceApi powerBiServiceApi; 18 | 19 | public HomeController(PowerBiServiceApi powerBiServiceApi) 20 | { 21 | this.powerBiServiceApi = powerBiServiceApi; 22 | } 23 | 24 | [AllowAnonymous] 25 | public IActionResult Index() 26 | { 27 | return View(); 28 | } 29 | 30 | public async Task Embed() { 31 | 32 | // replace these two GUIDs with the workspace ID and report ID you recorded earlier 33 | Guid workspaceId = new Guid("11111111-1111-1111-1111-111111111111"); 34 | Guid reportId = new Guid("22222222-2222-2222-2222-222222222222"); 35 | 36 | var viewModel = await powerBiServiceApi.GetReport(workspaceId, reportId); 37 | 38 | return View(viewModel); 39 | } 40 | 41 | [AllowAnonymous] 42 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 43 | public IActionResult Error() 44 | { 45 | return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AppOwnsData.Models 4 | { 5 | public class ErrorViewModel 6 | { 7 | public string RequestId { get; set; } 8 | 9 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace AppOwnsData 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:63983", 7 | "sslPort": 44325 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "AppOwnsData": { 19 | "commandName": "Project", 20 | "dotnetRunMessages": "true", 21 | "launchBrowser": true, 22 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 23 | "environmentVariables": { 24 | "ASPNETCORE_ENVIRONMENT": "Development" 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/Services/PowerBiServiceApi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Identity.Web; 6 | using Microsoft.Rest; 7 | using Microsoft.PowerBI.Api; 8 | using Microsoft.PowerBI.Api.Models; 9 | using Newtonsoft.Json; 10 | 11 | namespace AppOwnsData.Services { 12 | 13 | public class EmbeddedReportViewModel { 14 | public string Id; 15 | public string Name; 16 | public string EmbedUrl; 17 | public string Token; 18 | } 19 | 20 | public class PowerBiServiceApi { 21 | 22 | private ITokenAcquisition tokenAcquisition { get; } 23 | private string urlPowerBiServiceApiRoot { get; } 24 | 25 | public PowerBiServiceApi(IConfiguration configuration, ITokenAcquisition tokenAcquisition) { 26 | this.urlPowerBiServiceApiRoot = configuration["PowerBi:ServiceRootUrl"]; 27 | this.tokenAcquisition = tokenAcquisition; 28 | } 29 | 30 | public const string powerbiApiDefaultScope = "https://analysis.windows.net/powerbi/api/.default"; 31 | 32 | public string GetAccessToken() { 33 | return this.tokenAcquisition.GetAccessTokenForAppAsync(powerbiApiDefaultScope).Result; 34 | } 35 | 36 | public PowerBIClient GetPowerBiClient() { 37 | var tokenCredentials = new TokenCredentials(GetAccessToken(), "Bearer"); 38 | return new PowerBIClient(new Uri(urlPowerBiServiceApiRoot), tokenCredentials); 39 | } 40 | 41 | public async Task GetReport(Guid WorkspaceId, Guid ReportId) { 42 | 43 | PowerBIClient pbiClient = GetPowerBiClient(); 44 | 45 | // call to Power BI Service API to get embedding data 46 | var report = await pbiClient.Reports.GetReportInGroupAsync(WorkspaceId, ReportId); 47 | 48 | // generate read-only embed token for the report 49 | var datasetId = report.DatasetId; 50 | var tokenRequest = new GenerateTokenRequest(TokenAccessLevel.View, datasetId); 51 | var embedTokenResponse = await pbiClient.Reports.GenerateTokenAsync(WorkspaceId, ReportId, tokenRequest); 52 | var embedToken = embedTokenResponse.Token; 53 | 54 | // return report embedding data to caller 55 | return new EmbeddedReportViewModel { 56 | Id = report.Id.ToString(), 57 | EmbedUrl = report.EmbedUrl, 58 | Name = report.Name, 59 | Token = embedToken 60 | }; 61 | } 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Authentication; 6 | using Microsoft.AspNetCore.Authentication.OpenIdConnect; 7 | using Microsoft.AspNetCore.Authorization; 8 | using Microsoft.Identity.Web; 9 | using Microsoft.Identity.Web.UI; 10 | using Microsoft.AspNetCore.Builder; 11 | using Microsoft.AspNetCore.Hosting; 12 | using Microsoft.AspNetCore.HttpsPolicy; 13 | using Microsoft.AspNetCore.Mvc.Authorization; 14 | using Microsoft.Extensions.Configuration; 15 | using Microsoft.Extensions.DependencyInjection; 16 | using Microsoft.Extensions.Hosting; 17 | using AppOwnsData.Services; 18 | 19 | namespace AppOwnsData 20 | { 21 | public class Startup 22 | { 23 | public Startup(IConfiguration configuration) 24 | { 25 | Configuration = configuration; 26 | } 27 | 28 | public IConfiguration Configuration { get; } 29 | 30 | // This method gets called by the runtime. Use this method to add services to the container. 31 | public void ConfigureServices(IServiceCollection services) { 32 | 33 | services.AddMicrosoftIdentityWebAppAuthentication(Configuration) 34 | .EnableTokenAcquisitionToCallDownstreamApi() 35 | .AddInMemoryTokenCaches(); 36 | 37 | services.AddScoped(typeof(PowerBiServiceApi)); 38 | 39 | services.AddControllersWithViews(options => { 40 | var policy = new AuthorizationPolicyBuilder() 41 | .RequireAuthenticatedUser() 42 | .Build(); 43 | options.Filters.Add(new AuthorizeFilter(policy)); 44 | }); 45 | services.AddRazorPages() 46 | .AddMicrosoftIdentityUI(); 47 | } 48 | 49 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 50 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 51 | { 52 | if (env.IsDevelopment()) 53 | { 54 | app.UseDeveloperExceptionPage(); 55 | } 56 | else 57 | { 58 | app.UseExceptionHandler("/Home/Error"); 59 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 60 | app.UseHsts(); 61 | } 62 | app.UseHttpsRedirection(); 63 | app.UseStaticFiles(); 64 | 65 | app.UseRouting(); 66 | 67 | app.UseAuthentication(); 68 | app.UseAuthorization(); 69 | 70 | app.UseEndpoints(endpoints => 71 | { 72 | endpoints.MapControllerRoute( 73 | name: "default", 74 | pattern: "{controller=Home}/{action=Index}/{id?}"); 75 | endpoints.MapRazorPages(); 76 | }); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/Views/Home/Embed.cshtml: -------------------------------------------------------------------------------- 1 | @model AppOwnsData.Services.EmbeddedReportViewModel; 2 | 3 |
4 | 5 | @section Scripts { 6 | 7 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Security.Principal 2 | 3 | @if (User.Identity.IsAuthenticated) { 4 |
5 |

Welcome @User.FindFirst("name").Value

6 |

You have now logged into this application.

7 |
8 | } 9 | else { 10 |
11 |

Welcome to the App-Owns-Data Tutorial

12 |

Click the sign in link in the upper right to get started

13 |
14 | } -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model ErrorViewModel 2 | @{ 3 | ViewData["Title"] = "Error"; 4 | } 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (Model.ShowRequestId) 10 | { 11 |

12 | Request ID: @Model.RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |

26 | -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | App-Owns-Data Demo 7 | 8 | 9 | 10 | 11 |
12 | 32 |
33 |
34 |
35 | @RenderBody() 36 |
37 |
38 | 39 | 40 | @RenderSection("Scripts", required: false) 41 | 42 | 43 | -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/Views/Shared/_LoginPartial.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Security.Principal 2 | 3 | 20 | -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/Views/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using AppOwnsData 2 | @using AppOwnsData.Models 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "AzureAd": { 3 | "Instance": "https://login.microsoftonline.com/", 4 | "Domain": "yourtenant.onMicrosoft.com", 5 | "TenantId": "", 6 | "ClientId": "", 7 | "ClientSecret": "", 8 | "CallbackPath": "/signin-oidc", 9 | "SignedOutCallbackPath": "/signout-callback-oidc" 10 | }, 11 | "PowerBi": { 12 | "ServiceRootUrl": "https://api.powerbi.com/" 13 | }, 14 | "Logging": { 15 | "LogLevel": { 16 | "Default": "Information", 17 | "Microsoft": "Warning", 18 | "Microsoft.Hosting.Lifetime": "Information" 19 | } 20 | }, 21 | "AllowedHosts": "*" 22 | } -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | /* App-Owns-Data Lab Styles from Power BI Dev Camp */ 2 | 3 | ul i { padding-right: 4px; } 4 | h1 { font-size: 1.35em; } 5 | h2 { font-size: 1.25em; } 6 | h3 { font-size: 1.1em; } 7 | h4 { font-size: 1.0em; } 8 | 9 | .mb-3, .my-3 { 10 | margin-bottom: 0 !important; 11 | } 12 | 13 | nav { 14 | margin-bottom: 0px; 15 | } 16 | 17 | i { 18 | margin-right: 4px; 19 | } 20 | 21 | 22 | #banner { 23 | padding-left: 0px; 24 | padding-right: 0px; 25 | } 26 | 27 | .jumbotron { 28 | background-color: #EDBE11; 29 | border: 1px solid #AAA; 30 | margin-top: 16px; 31 | } 32 | 33 | /* styles for embed page */ 34 | 35 | #content-box { 36 | padding-left: 0; 37 | padding-right: 0; 38 | border: solid 1px black; 39 | } 40 | 41 | #left-nav { 42 | background-color: black; 43 | color: white; 44 | padding: 0; 45 | border: 1px solid black; 46 | } 47 | 48 | #left-nav .navbar-header { 49 | color: black; 50 | background-color: #EDBE11; 51 | padding: 2px; 52 | padding-left: 4px; 53 | border-bottom: 1px solid #333333; 54 | font-size: 14px; 55 | } 56 | 57 | #workspace-selector { 58 | width: 100%; 59 | padding: 0; 60 | } 61 | 62 | #workspace-selector-dropdown { 63 | padding: 0px; 64 | } 65 | 66 | .dropdown-toggle::after { 67 | position: absolute; 68 | right: 8px; 69 | top: 45%; 70 | } 71 | 72 | #left-nav .navbar-collapse, #left-nav .dropdown { 73 | background-color: white; 74 | color: black; 75 | padding: 8px; 76 | font-size: 12px; 77 | border-bottom: 1px solid #333333; 78 | } 79 | 80 | #left-nav .navbar-collapse { 81 | padding: 8px; 82 | } 83 | 84 | #left-nav .dropdown { 85 | padding: 0px; 86 | } 87 | 88 | #left-nav .dropdown button { 89 | margin: 0px; 90 | padding: 4px; 91 | width: 100%; 92 | text-align: left; 93 | } 94 | 95 | 96 | #embed-container { 97 | min-height: 480px; 98 | height: 600px; 99 | } 100 | 101 | 102 | .no-content { 103 | font-style: italic; 104 | margin-left: 16px; 105 | color: #88AAAA; 106 | } 107 | 108 | #embedding-instructions { 109 | margin: 24px; 110 | color: #BBBBBB; 111 | } 112 | 113 | #embed-toolbar { 114 | margin-top: -1px; 115 | background-color: black; 116 | color: white; 117 | padding: 4px; 118 | font-size: 12px; 119 | } 120 | 121 | #embed-toolbar button { 122 | margin: 0px; 123 | font-size: 9px; 124 | font-weight: bold; 125 | padding: 2px; 126 | color: #222; 127 | background-color: #F2C811; 128 | border-color: #111; 129 | display: inline; 130 | float: right; 131 | margin-left: 8px; 132 | padding-left: 6px; 133 | padding-right: 6px; 134 | } 135 | 136 | .workspaces-toolbar { 137 | background-color: #CCCCCC; 138 | border: 1px solid #666666; 139 | border-top-style: none; 140 | border-bottom-left-radius: 4px; 141 | border-bottom-right-radius: 4px; 142 | padding: 8px; 143 | margin-bottom: 16px; 144 | text-align: left; 145 | } 146 | 147 | #create-workspace-button, #back-to-workspaces-button { 148 | background-color: #F2C811; 149 | color: black; 150 | border: 1px solid black; 151 | } 152 | 153 | table a.nav-link { 154 | margin: 0px; 155 | padding: 4px; 156 | padding-left: 6px; 157 | background-color: #F2C811; 158 | color: black; 159 | border: 1px solid black; 160 | border-radius: 4px; 161 | text-align: center; 162 | } 163 | 164 | .create-workspace-fieldset { 165 | background-color: #EEEEEE; 166 | border: solid 1px #AAAAAA; 167 | padding: 24px; 168 | } 169 | 170 | .create-workspace-fieldset legend { 171 | width: auto; 172 | padding: 8px; 173 | } 174 | -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerBiDevCamp/DOTNET5-AppOwnsData-Tutorial/ad38de0afe91ad41ce5af6f2b61554f8f4ae7040/Solution/Exercise4/AppOwnsData/wwwroot/favicon.ico -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/wwwroot/js/embed.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | // 1 - get DOM object for div that is report container 4 | var reportContainer = document.getElementById("embed-container"); 5 | 6 | // 2 - get report embedding data from view model 7 | var reportId = window.viewModel.reportId; 8 | var embedUrl = window.viewModel.embedUrl; 9 | var token = window.viewModel.token 10 | 11 | // 3 - embed report using the Power BI JavaScript API. 12 | var models = window['powerbi-client'].models; 13 | 14 | var config = { 15 | type: 'report', 16 | id: reportId, 17 | embedUrl: embedUrl, 18 | accessToken: token, 19 | permissions: models.Permissions.All, 20 | tokenType: models.TokenType.Embed, 21 | viewMode: models.ViewMode.View, 22 | settings: { 23 | panes: { 24 | filters: { expanded: false, visible: true }, 25 | pageNavigation: { visible: false } 26 | } 27 | } 28 | }; 29 | 30 | // Embed the report and display it within the div container. 31 | var report = powerbi.embed(reportContainer, config); 32 | 33 | // 4 - add logic to resize embed container on window resize event 34 | var heightBuffer = 12; 35 | var newHeight = $(window).height() - ($("header").height() + heightBuffer); 36 | $("#embed-container").height(newHeight); 37 | $(window).resize(function () { 38 | var newHeight = $(window).height() - ($("header").height() + heightBuffer); 39 | $("#embed-container").height(newHeight); 40 | }); 41 | 42 | }); -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | // for details on configuring this project to bundle and minify static web assets. 3 | 4 | // Write your JavaScript code. 5 | -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2018 Twitter, Inc. 4 | Copyright (c) 2011-2018 The Bootstrap Authors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v4.3.1 (https://getbootstrap.com/) 3 | * Copyright 2011-2019 The Bootstrap Authors 4 | * Copyright 2011-2019 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important} 8 | /*# sourceMappingURL=bootstrap-reboot.min.css.map */ -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Solution/Exercise4/AppOwnsData/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors, https://js.foundation/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (web)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/net5.0/AppOwnsData.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | "stopAtEntry": false, 17 | // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser 18 | "serverReadyAction": { 19 | "action": "openExternally", 20 | "pattern": "\\bNow listening on:\\s+(https?://\\S+)" 21 | }, 22 | "env": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | }, 25 | "sourceFileMap": { 26 | "/Views": "${workspaceFolder}/Views" 27 | } 28 | }, 29 | { 30 | "name": ".NET Core Attach", 31 | "type": "coreclr", 32 | "request": "attach", 33 | "processId": "${command:pickProcess}" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/AppOwnsData.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/AppOwnsData.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "${workspaceFolder}/AppOwnsData.csproj", 36 | "/property:GenerateFullPaths=true", 37 | "/consoleloggerparameters:NoSummary" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/AppOwnsData.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | aspnet-AppOwnsData-74548193-D24B-4C99-9C55-AF543E13A630 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/AppOwnsData.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30804.86 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppOwnsData", "AppOwnsData.csproj", "{F3A2F8C1-120C-4B0E-9032-80958C1E01C0}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {F3A2F8C1-120C-4B0E-9032-80958C1E01C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {F3A2F8C1-120C-4B0E-9032-80958C1E01C0}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {F3A2F8C1-120C-4B0E-9032-80958C1E01C0}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {F3A2F8C1-120C-4B0E-9032-80958C1E01C0}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {CE4B4376-2FBD-4213-A882-438369972CF3} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Authorization; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.Extensions.Logging; 9 | using AppOwnsData.Models; 10 | using AppOwnsData.Services; 11 | 12 | namespace AppOwnsData.Controllers 13 | { 14 | [Authorize] 15 | public class HomeController : Controller 16 | { 17 | private PowerBiServiceApi powerBiServiceApi; 18 | 19 | public HomeController(PowerBiServiceApi powerBiServiceApi) 20 | { 21 | this.powerBiServiceApi = powerBiServiceApi; 22 | } 23 | 24 | [AllowAnonymous] 25 | public IActionResult Index() 26 | { 27 | return View(); 28 | } 29 | 30 | public async Task Embed() 31 | { 32 | // replace these two GUIDs with the workspace ID and report ID you recorded earlier 33 | // replace these two GUIDs with the workspace ID and report ID you recorded earlier 34 | Guid workspaceId = new Guid("ddab987a-7941-4eb5-a084-370d422dfcac"); 35 | Guid reportId = new Guid("3e75d100-b21a-4129-8e84-6c6e90ccbd7e"); 36 | 37 | var viewModel = await powerBiServiceApi.GetReport(workspaceId, reportId); 38 | 39 | return View(viewModel); 40 | } 41 | 42 | [AllowAnonymous] 43 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 44 | public IActionResult Error() 45 | { 46 | return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AppOwnsData.Models 4 | { 5 | public class ErrorViewModel 6 | { 7 | public string RequestId { get; set; } 8 | 9 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace AppOwnsData 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:63983", 7 | "sslPort": 44325 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "AppOwnsData": { 19 | "commandName": "Project", 20 | "dotnetRunMessages": "true", 21 | "launchBrowser": true, 22 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 23 | "environmentVariables": { 24 | "ASPNETCORE_ENVIRONMENT": "Development" 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/Scripts/embed.ts: -------------------------------------------------------------------------------- 1 | import * as $ from 'jquery'; 2 | 3 | import * as powerbi from "powerbi-client"; 4 | import * as models from "powerbi-models"; 5 | 6 | // ensure Power BI JavaScript API has loaded 7 | require('powerbi-models'); 8 | require('powerbi-client'); 9 | 10 | class viewModel { 11 | reportId: string; 12 | embedUrl: string; 13 | token: string; 14 | } 15 | 16 | $(() => { 17 | 18 | // get DOM object div for report container 19 | var reportContainer: HTMLElement = document.getElementById("embed-container"); 20 | 21 | var viewModel: viewModel = window["viewModel"]; 22 | 23 | var config: powerbi.IEmbedConfiguration = { 24 | type: 'report', 25 | id: viewModel.reportId, 26 | embedUrl: viewModel.embedUrl, 27 | accessToken: viewModel.token, 28 | permissions: models.Permissions.All, 29 | tokenType: models.TokenType.Embed, 30 | viewMode: models.ViewMode.View, 31 | settings: { 32 | panes: { 33 | filters: { expanded: false, visible: true }, 34 | pageNavigation: { visible: true } 35 | }, 36 | persistentFiltersEnabled: true 37 | } 38 | }; 39 | 40 | // Embed the report and display it within the div container. 41 | var report = window.powerbi.embed(reportContainer, config); 42 | 43 | // display report properties in browser console 44 | console.log(report); 45 | 46 | // add logic to resize embed container element on window rersize event 47 | var heightBuffer = 12; 48 | var newHeight = $(window).height() - ($("header").height() + heightBuffer); 49 | $("#embed-container").height(newHeight); 50 | $(window).on("resize",function () { 51 | var newHeight = $(window).height() - ($("header").height() + heightBuffer); 52 | $("#embed-container").height(newHeight); 53 | }); 54 | 55 | }); -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/Services/PowerBiServiceApi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Identity.Web; 6 | using Microsoft.Rest; 7 | using Microsoft.PowerBI.Api; 8 | using Microsoft.PowerBI.Api.Models; 9 | using Newtonsoft.Json; 10 | 11 | namespace AppOwnsData.Services { 12 | 13 | public class EmbeddedReportViewModel { 14 | public string Id; 15 | public string Name; 16 | public string EmbedUrl; 17 | public string Token; 18 | } 19 | 20 | public class PowerBiServiceApi { 21 | 22 | private ITokenAcquisition tokenAcquisition { get; } 23 | private string urlPowerBiServiceApiRoot { get; } 24 | 25 | public PowerBiServiceApi(IConfiguration configuration, ITokenAcquisition tokenAcquisition) { 26 | this.urlPowerBiServiceApiRoot = configuration["PowerBi:ServiceRootUrl"]; 27 | this.tokenAcquisition = tokenAcquisition; 28 | } 29 | 30 | public const string powerbiApiDefaultScope = "https://analysis.windows.net/powerbi/api/.default"; 31 | 32 | public string GetAccessToken() { 33 | return this.tokenAcquisition.GetAccessTokenForAppAsync(powerbiApiDefaultScope).Result; 34 | } 35 | 36 | public PowerBIClient GetPowerBiClient() { 37 | var tokenCredentials = new TokenCredentials(GetAccessToken(), "Bearer"); 38 | return new PowerBIClient(new Uri(urlPowerBiServiceApiRoot), tokenCredentials); 39 | } 40 | 41 | public async Task GetReport(Guid WorkspaceId, Guid ReportId) { 42 | 43 | PowerBIClient pbiClient = GetPowerBiClient(); 44 | 45 | // call to Power BI Service API to get embedding data 46 | var report = await pbiClient.Reports.GetReportInGroupAsync(WorkspaceId, ReportId); 47 | 48 | // generate read-only embed token for the report 49 | var datasetId = report.DatasetId; 50 | var tokenRequest = new GenerateTokenRequest(TokenAccessLevel.View, datasetId); 51 | var embedTokenResponse = await pbiClient.Reports.GenerateTokenAsync(WorkspaceId, ReportId, tokenRequest); 52 | var embedToken = embedTokenResponse.Token; 53 | 54 | // return report embedding data to caller 55 | return new EmbeddedReportViewModel { 56 | Id = report.Id.ToString(), 57 | EmbedUrl = report.EmbedUrl, 58 | Name = report.Name, 59 | Token = embedToken 60 | }; 61 | } 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Authentication; 6 | using Microsoft.AspNetCore.Authentication.OpenIdConnect; 7 | using Microsoft.AspNetCore.Authorization; 8 | using Microsoft.Identity.Web; 9 | using Microsoft.Identity.Web.UI; 10 | using Microsoft.AspNetCore.Builder; 11 | using Microsoft.AspNetCore.Hosting; 12 | using Microsoft.AspNetCore.HttpsPolicy; 13 | using Microsoft.AspNetCore.Mvc.Authorization; 14 | using Microsoft.Extensions.Configuration; 15 | using Microsoft.Extensions.DependencyInjection; 16 | using Microsoft.Extensions.Hosting; 17 | using AppOwnsData.Services; 18 | 19 | namespace AppOwnsData 20 | { 21 | public class Startup 22 | { 23 | public Startup(IConfiguration configuration) 24 | { 25 | Configuration = configuration; 26 | } 27 | 28 | public IConfiguration Configuration { get; } 29 | 30 | // This method gets called by the runtime. Use this method to add services to the container. 31 | public void ConfigureServices(IServiceCollection services) { 32 | 33 | services.AddMicrosoftIdentityWebAppAuthentication(Configuration) 34 | .EnableTokenAcquisitionToCallDownstreamApi() 35 | .AddInMemoryTokenCaches(); 36 | 37 | services.AddScoped(typeof(PowerBiServiceApi)); 38 | 39 | services.AddControllersWithViews(options => { 40 | var policy = new AuthorizationPolicyBuilder() 41 | .RequireAuthenticatedUser() 42 | .Build(); 43 | options.Filters.Add(new AuthorizeFilter(policy)); 44 | }); 45 | services.AddRazorPages() 46 | .AddMicrosoftIdentityUI(); 47 | } 48 | 49 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 50 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 51 | { 52 | if (env.IsDevelopment()) 53 | { 54 | app.UseDeveloperExceptionPage(); 55 | } 56 | else 57 | { 58 | app.UseExceptionHandler("/Home/Error"); 59 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 60 | app.UseHsts(); 61 | } 62 | app.UseHttpsRedirection(); 63 | app.UseStaticFiles(); 64 | 65 | app.UseRouting(); 66 | 67 | app.UseAuthentication(); 68 | app.UseAuthorization(); 69 | 70 | app.UseEndpoints(endpoints => 71 | { 72 | endpoints.MapControllerRoute( 73 | name: "default", 74 | pattern: "{controller=Home}/{action=Index}/{id?}"); 75 | endpoints.MapRazorPages(); 76 | }); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/Views/Home/Embed.cshtml: -------------------------------------------------------------------------------- 1 | @model AppOwnsData.Services.EmbeddedReportViewModel; 2 | 3 |
4 | 5 | @section Scripts { 6 | 7 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Security.Principal 2 | 3 | @if (User.Identity.IsAuthenticated) { 4 |
5 |

Welcome @User.FindFirst("name").Value

6 |

You have now logged into this application.

7 |
8 | } 9 | else { 10 |
11 |

Welcome to the App-Owns-Data Tutorial

12 |

Click the sign in link in the upper right to get started

13 |
14 | } -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model ErrorViewModel 2 | @{ 3 | ViewData["Title"] = "Error"; 4 | } 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (Model.ShowRequestId) 10 | { 11 |

12 | Request ID: @Model.RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |

26 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | App-Owns-Data Demo 7 | 8 | 9 | 10 | 11 |
12 | 32 |
33 |
34 |
35 | @RenderBody() 36 |
37 |
38 | 39 | 40 | @RenderSection("Scripts", required: false) 41 | 42 | 43 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/Views/Shared/_LoginPartial.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Security.Principal 2 | 3 | 20 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/Views/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using AppOwnsData 2 | @using AppOwnsData.Models 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "AzureAd": { 3 | "Instance": "https://login.microsoftonline.com/", 4 | "Domain": "yourtenant.onMicrosoft.com", 5 | "TenantId": "", 6 | "ClientId": "", 7 | "ClientSecret": "", 8 | "CallbackPath": "/signin-oidc", 9 | "SignedOutCallbackPath": "/signout-callback-oidc" 10 | }, 11 | "PowerBi": { 12 | "ServiceRootUrl": "https://api.powerbi.com/" 13 | }, 14 | "Logging": { 15 | "LogLevel": { 16 | "Default": "Information", 17 | "Microsoft": "Warning", 18 | "Microsoft.Hosting.Lifetime": "Information" 19 | } 20 | }, 21 | "AllowedHosts": "*" 22 | } -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "name": "app-owns-data", 4 | "private": true, 5 | "scripts": { 6 | "build": "webpack" 7 | }, 8 | "devDependencies": { 9 | "@types/jquery": "3.3.29", 10 | "@types/node": "^14.0.11", 11 | "awesome-typescript-loader": "^5.2.1", 12 | "jquery": "^3.5.1", 13 | "powerbi-client": "^2.13.3", 14 | "powerbi-models": "^1.4.0", 15 | "typescript": "^4.0.2", 16 | "webpack": "^4.44.1", 17 | "webpack-cli": "^3.3.12" 18 | }, 19 | "dependencies": {} 20 | } 21 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "react", 4 | "module": "commonjs", 5 | "sourceMap": true, 6 | "target": "es5", 7 | "lib": [ "es6", "dom" ], 8 | "suppressImplicitAnyIndexErrors": true, 9 | "noImplicitReturns": true, 10 | "noImplicitThis": true, 11 | "noImplicitAny": true, 12 | "strictNullChecks": false, 13 | "experimentalDecorators": true 14 | }, 15 | "exclude": [ 16 | "node_modules", "wwwroot" 17 | ] 18 | } -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './Scripts/embed.ts', 5 | output: { 6 | filename: 'embed.js', 7 | path: path.resolve(__dirname, 'wwwroot/js'), 8 | }, 9 | resolve: { 10 | extensions: ['.js', '.ts'] 11 | }, 12 | module: { 13 | rules: [ 14 | { test: /\.(ts)$/, loader: 'awesome-typescript-loader' } 15 | ], 16 | }, 17 | mode: "development", 18 | devtool: 'source-map' 19 | }; 20 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | /* App-Owns-Data Lab Styles from Power BI Dev Camp */ 2 | 3 | ul i { padding-right: 4px; } 4 | h1 { font-size: 1.35em; } 5 | h2 { font-size: 1.25em; } 6 | h3 { font-size: 1.1em; } 7 | h4 { font-size: 1.0em; } 8 | 9 | .mb-3, .my-3 { 10 | margin-bottom: 0 !important; 11 | } 12 | 13 | nav { 14 | margin-bottom: 0px; 15 | } 16 | 17 | i { 18 | margin-right: 4px; 19 | } 20 | 21 | 22 | #banner { 23 | padding-left: 0px; 24 | padding-right: 0px; 25 | } 26 | 27 | .jumbotron { 28 | background-color: #EDBE11; 29 | border: 1px solid #AAA; 30 | margin-top: 16px; 31 | } 32 | 33 | /* styles for embed page */ 34 | 35 | #content-box { 36 | padding-left: 0; 37 | padding-right: 0; 38 | border: solid 1px black; 39 | } 40 | 41 | #left-nav { 42 | background-color: black; 43 | color: white; 44 | padding: 0; 45 | border: 1px solid black; 46 | } 47 | 48 | #left-nav .navbar-header { 49 | color: black; 50 | background-color: #EDBE11; 51 | padding: 2px; 52 | padding-left: 4px; 53 | border-bottom: 1px solid #333333; 54 | font-size: 14px; 55 | } 56 | 57 | #workspace-selector { 58 | width: 100%; 59 | padding: 0; 60 | } 61 | 62 | #workspace-selector-dropdown { 63 | padding: 0px; 64 | } 65 | 66 | .dropdown-toggle::after { 67 | position: absolute; 68 | right: 8px; 69 | top: 45%; 70 | } 71 | 72 | #left-nav .navbar-collapse, #left-nav .dropdown { 73 | background-color: white; 74 | color: black; 75 | padding: 8px; 76 | font-size: 12px; 77 | border-bottom: 1px solid #333333; 78 | } 79 | 80 | #left-nav .navbar-collapse { 81 | padding: 8px; 82 | } 83 | 84 | #left-nav .dropdown { 85 | padding: 0px; 86 | } 87 | 88 | #left-nav .dropdown button { 89 | margin: 0px; 90 | padding: 4px; 91 | width: 100%; 92 | text-align: left; 93 | } 94 | 95 | 96 | #embed-container { 97 | min-height: 480px; 98 | height: 600px; 99 | } 100 | 101 | 102 | .no-content { 103 | font-style: italic; 104 | margin-left: 16px; 105 | color: #88AAAA; 106 | } 107 | 108 | #embedding-instructions { 109 | margin: 24px; 110 | color: #BBBBBB; 111 | } 112 | 113 | #embed-toolbar { 114 | margin-top: -1px; 115 | background-color: black; 116 | color: white; 117 | padding: 4px; 118 | font-size: 12px; 119 | } 120 | 121 | #embed-toolbar button { 122 | margin: 0px; 123 | font-size: 9px; 124 | font-weight: bold; 125 | padding: 2px; 126 | color: #222; 127 | background-color: #F2C811; 128 | border-color: #111; 129 | display: inline; 130 | float: right; 131 | margin-left: 8px; 132 | padding-left: 6px; 133 | padding-right: 6px; 134 | } 135 | 136 | .workspaces-toolbar { 137 | background-color: #CCCCCC; 138 | border: 1px solid #666666; 139 | border-top-style: none; 140 | border-bottom-left-radius: 4px; 141 | border-bottom-right-radius: 4px; 142 | padding: 8px; 143 | margin-bottom: 16px; 144 | text-align: left; 145 | } 146 | 147 | #create-workspace-button, #back-to-workspaces-button { 148 | background-color: #F2C811; 149 | color: black; 150 | border: 1px solid black; 151 | } 152 | 153 | table a.nav-link { 154 | margin: 0px; 155 | padding: 4px; 156 | padding-left: 6px; 157 | background-color: #F2C811; 158 | color: black; 159 | border: 1px solid black; 160 | border-radius: 4px; 161 | text-align: center; 162 | } 163 | 164 | .create-workspace-fieldset { 165 | background-color: #EEEEEE; 166 | border: solid 1px #AAAAAA; 167 | padding: 24px; 168 | } 169 | 170 | .create-workspace-fieldset legend { 171 | width: auto; 172 | padding: 8px; 173 | } 174 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerBiDevCamp/DOTNET5-AppOwnsData-Tutorial/ad38de0afe91ad41ce5af6f2b61554f8f4ae7040/Solution/Exercise5/AppOwnsData/wwwroot/favicon.ico -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | // for details on configuring this project to bundle and minify static web assets. 3 | 4 | // Write your JavaScript code. 5 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2018 Twitter, Inc. 4 | Copyright (c) 2011-2018 The Bootstrap Authors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v4.3.1 (https://getbootstrap.com/) 3 | * Copyright 2011-2019 The Bootstrap Authors 4 | * Copyright 2011-2019 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important} 8 | /*# sourceMappingURL=bootstrap-reboot.min.css.map */ -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Solution/Exercise5/AppOwnsData/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors, https://js.foundation/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (web)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/net5.0/AppOwnsData.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | "stopAtEntry": false, 17 | // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser 18 | "serverReadyAction": { 19 | "action": "openExternally", 20 | "pattern": "\\bNow listening on:\\s+(https?://\\S+)" 21 | }, 22 | "env": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | }, 25 | "sourceFileMap": { 26 | "/Views": "${workspaceFolder}/Views" 27 | } 28 | }, 29 | { 30 | "name": ".NET Core Attach", 31 | "type": "coreclr", 32 | "request": "attach", 33 | "processId": "${command:pickProcess}" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/AppOwnsData.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/AppOwnsData.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "${workspaceFolder}/AppOwnsData.csproj", 36 | "/property:GenerateFullPaths=true", 37 | "/consoleloggerparameters:NoSummary" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/AppOwnsData.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | aspnet-AppOwnsData-74548193-D24B-4C99-9C55-AF543E13A630 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/AppOwnsData.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30804.86 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppOwnsData", "AppOwnsData.csproj", "{F3A2F8C1-120C-4B0E-9032-80958C1E01C0}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {F3A2F8C1-120C-4B0E-9032-80958C1E01C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {F3A2F8C1-120C-4B0E-9032-80958C1E01C0}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {F3A2F8C1-120C-4B0E-9032-80958C1E01C0}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {F3A2F8C1-120C-4B0E-9032-80958C1E01C0}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {CE4B4376-2FBD-4213-A882-438369972CF3} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Authorization; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.Extensions.Logging; 9 | using AppOwnsData.Models; 10 | using AppOwnsData.Services; 11 | using Microsoft.Rest; 12 | 13 | namespace AppOwnsData.Controllers { 14 | 15 | [Authorize] 16 | public class HomeController : Controller { 17 | 18 | private PowerBiServiceApi powerBiServiceApi; 19 | 20 | public HomeController(PowerBiServiceApi powerBiServiceApi) { 21 | this.powerBiServiceApi = powerBiServiceApi; 22 | } 23 | 24 | [AllowAnonymous] 25 | public IActionResult Index() { 26 | return View(); 27 | } 28 | 29 | public async Task Embed(string workspaceId) { 30 | 31 | Guid validWorkspaceId; 32 | bool workspaceIdIsValid = Guid.TryParse(workspaceId, out validWorkspaceId); 33 | 34 | try { 35 | if (workspaceIdIsValid) { 36 | var viewModel = await this.powerBiServiceApi.GetEmbeddedViewModel(workspaceId); 37 | return View(viewModel as object); 38 | } 39 | else { 40 | var firstWorkspace = await this.powerBiServiceApi.GetFirstWorkspace(); 41 | if (firstWorkspace != null) { 42 | return RedirectToAction("Embed", "Home", new { workspaceId = firstWorkspace.Id }); 43 | } 44 | else { 45 | throw new ApplicationException("This service principal for the Azure AD app is not a member of any workspaces."); 46 | } 47 | } 48 | } 49 | catch (HttpOperationException ex) { 50 | return StatusCode(500, "Unexpected HTTP exception: " + ex.Response.Content); 51 | } 52 | catch (Exception ex) { 53 | return StatusCode(500, "Unexpected exception: " + ex.Message); 54 | } 55 | } 56 | 57 | [AllowAnonymous] 58 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 59 | public IActionResult Error() { 60 | return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); 61 | } 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AppOwnsData.Models 4 | { 5 | public class ErrorViewModel 6 | { 7 | public string RequestId { get; set; } 8 | 9 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace AppOwnsData { 11 | public class Program { 12 | 13 | public static void Main(string[] args) { 14 | CreateHostBuilder(args).Build().Run(); 15 | } 16 | 17 | public static IHostBuilder CreateHostBuilder(string[] args) => 18 | Host.CreateDefaultBuilder(args) 19 | .ConfigureWebHostDefaults(webBuilder => { 20 | webBuilder.UseStartup(); 21 | }); 22 | } 23 | } -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:63983", 7 | "sslPort": 44325 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "AppOwnsData": { 19 | "commandName": "Project", 20 | "dotnetRunMessages": "true", 21 | "launchBrowser": true, 22 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 23 | "environmentVariables": { 24 | "ASPNETCORE_ENVIRONMENT": "Development" 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using Microsoft.Identity.Web; 3 | using Microsoft.Identity.Web.UI; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.AspNetCore.Mvc.Authorization; 7 | using Microsoft.Extensions.Configuration; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Hosting; 10 | using AppOwnsData.Services; 11 | 12 | namespace AppOwnsData { 13 | public class Startup { 14 | 15 | public Startup(IConfiguration configuration) { 16 | Configuration = configuration; 17 | } 18 | 19 | public IConfiguration Configuration { get; } 20 | 21 | public void ConfigureServices(IServiceCollection services) { 22 | 23 | services.AddMicrosoftIdentityWebAppAuthentication(Configuration) 24 | .EnableTokenAcquisitionToCallDownstreamApi() 25 | .AddInMemoryTokenCaches(); 26 | 27 | services.AddScoped(typeof(PowerBiServiceApi)); 28 | 29 | services.AddControllersWithViews(options => { 30 | var policy = new AuthorizationPolicyBuilder() 31 | .RequireAuthenticatedUser() 32 | .Build(); 33 | options.Filters.Add(new AuthorizeFilter(policy)); 34 | }); 35 | services.AddRazorPages() 36 | .AddMicrosoftIdentityUI(); 37 | } 38 | 39 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { 40 | if (env.IsDevelopment()) { 41 | app.UseDeveloperExceptionPage(); 42 | } 43 | else { 44 | app.UseExceptionHandler("/Home/Error"); 45 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 46 | app.UseHsts(); 47 | } 48 | app.UseHttpsRedirection(); 49 | app.UseStaticFiles(); 50 | 51 | app.UseRouting(); 52 | 53 | app.UseAuthentication(); 54 | app.UseAuthorization(); 55 | 56 | app.UseEndpoints(endpoints => { 57 | endpoints.MapControllerRoute( 58 | name: "default", 59 | pattern: "{controller=Home}/{action=Index}/{id?}"); 60 | endpoints.MapRazorPages(); 61 | }); 62 | } 63 | 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/Views/Home/Embed.cshtml: -------------------------------------------------------------------------------- 1 | @model string 2 | 3 | 6 | 7 |
8 |
9 |
10 |
11 | 14 | 21 | 24 | 27 | 30 | 33 |
34 |
35 |
36 |
37 |

To Perform App-Owns-Data Embedding

38 |
    39 |
  • Use the workspace selector menu to choose a workspace.
  • 40 |
  • Click on an existing report to embed it.
  • 41 |
  • Click on an existing dataset to create a new report.
  • 42 |
43 |
44 | 49 |
50 |
51 | 52 |
53 |
54 | 55 | @section Scripts { 56 | 57 | } -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Security.Principal 2 | 3 | @if (User.Identity.IsAuthenticated) { 4 |
5 |

Welcome @User.FindFirst("name").Value

6 |

You have now logged into this application.

7 |
8 | } 9 | else { 10 |
11 |

Welcome to the App-Owns-Data Tutorial

12 |

Click the sign in link in the upper right to get started

13 |
14 | } -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model ErrorViewModel 2 | @{ 3 | ViewData["Title"] = "Error"; 4 | } 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (Model.ShowRequestId) 10 | { 11 |

12 | Request ID: @Model.RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |

26 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | App-Owns-Data Demo 7 | 8 | 9 | 10 | 11 |
12 | 32 |
33 |
34 |
35 | @RenderBody() 36 |
37 |
38 | 39 | 40 | @RenderSection("Scripts", required: false) 41 | 42 | 43 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/Views/Shared/_LoginPartial.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Security.Principal 2 | 3 | 20 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/Views/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using AppOwnsData 2 | @using AppOwnsData.Models 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "AzureAd": { 3 | "Instance": "https://login.microsoftonline.com/", 4 | "Domain": "yourtenant.onMicrosoft.com", 5 | "TenantId": "", 6 | "ClientId": "", 7 | "ClientSecret": "", 8 | "CallbackPath": "/signin-oidc", 9 | "SignedOutCallbackPath": "/signout-callback-oidc" 10 | }, 11 | "PowerBi": { 12 | "ServiceRootUrl": "https://api.powerbi.com/" 13 | }, 14 | "Logging": { 15 | "LogLevel": { 16 | "Default": "Information", 17 | "Microsoft": "Warning", 18 | "Microsoft.Hosting.Lifetime": "Information" 19 | } 20 | }, 21 | "AllowedHosts": "*" 22 | } -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/obj/Debug/net5.0/.NETCoreApp,Version=v5.0.AssemblyAttributes.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using System.Reflection; 4 | [assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v5.0", FrameworkDisplayName = "")] 5 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/obj/Debug/net5.0/AppOwnsData.AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | using System; 12 | using System.Reflection; 13 | 14 | [assembly: Microsoft.Extensions.Configuration.UserSecrets.UserSecretsIdAttribute("aspnet-AppOwnsData-74548193-D24B-4C99-9C55-AF543E13A630")] 15 | [assembly: System.Reflection.AssemblyCompanyAttribute("AppOwnsData")] 16 | [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] 17 | [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] 18 | [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")] 19 | [assembly: System.Reflection.AssemblyProductAttribute("AppOwnsData")] 20 | [assembly: System.Reflection.AssemblyTitleAttribute("AppOwnsData")] 21 | [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] 22 | 23 | // Generated by the MSBuild WriteCodeFragment class. 24 | 25 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/obj/Debug/net5.0/AppOwnsData.AssemblyInfoInputs.cache: -------------------------------------------------------------------------------- 1 | c0caebfe3dc22f6c5957b24abd96387b47de59d3 2 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/obj/Debug/net5.0/AppOwnsData.GeneratedMSBuildEditorConfig.editorconfig: -------------------------------------------------------------------------------- 1 | is_global = true 2 | build_property.TargetFramework = net5.0 3 | build_property.TargetPlatformMinVersion = 4 | build_property.UsingMicrosoftNETSdkWeb = true 5 | build_property.ProjectTypeGuids = 6 | build_property.PublishSingleFile = 7 | build_property.IncludeAllContentForSelfExtract = 8 | build_property._SupportedPlatformList = Android,iOS,Linux,macOS,Windows 9 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/obj/Debug/net5.0/AppOwnsData.RazorAssemblyInfo.cache: -------------------------------------------------------------------------------- 1 | 93f66ba0ff4a0aa1326df5e5dc942843dbfba9a0 2 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/obj/Debug/net5.0/AppOwnsData.RazorAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | using System; 12 | using System.Reflection; 13 | 14 | [assembly: Microsoft.AspNetCore.Mvc.ApplicationParts.RelatedAssemblyAttribute("AppOwnsData.Views")] 15 | 16 | // Generated by the MSBuild WriteCodeFragment class. 17 | 18 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/obj/Debug/net5.0/AppOwnsData.csprojAssemblyReference.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerBiDevCamp/DOTNET5-AppOwnsData-Tutorial/ad38de0afe91ad41ce5af6f2b61554f8f4ae7040/Solution/Final/AppOwnsData/obj/Debug/net5.0/AppOwnsData.csprojAssemblyReference.cache -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "name": "app-owns-data", 4 | "private": true, 5 | "scripts": { 6 | "build": "webpack" 7 | }, 8 | "devDependencies": { 9 | "@types/jquery": "3.3.29", 10 | "@types/node": "^14.0.11", 11 | "awesome-typescript-loader": "^5.2.1", 12 | "jquery": "^3.5.1", 13 | "powerbi-client": "^2.13.3", 14 | "powerbi-models": "^1.4.0", 15 | "typescript": "^4.0.2", 16 | "webpack": "^4.44.1", 17 | "webpack-cli": "^3.3.12" 18 | }, 19 | "dependencies": {} 20 | } 21 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "react", 4 | "module": "commonjs", 5 | "sourceMap": true, 6 | "target": "es5", 7 | "lib": [ "es6", "dom" ], 8 | "suppressImplicitAnyIndexErrors": true, 9 | "noImplicitReturns": true, 10 | "noImplicitThis": true, 11 | "noImplicitAny": true, 12 | "strictNullChecks": false, 13 | "experimentalDecorators": true 14 | }, 15 | "exclude": [ 16 | "node_modules", "wwwroot" 17 | ] 18 | } -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './Scripts/embed.ts', 5 | output: { 6 | filename: 'embed.js', 7 | path: path.resolve(__dirname, 'wwwroot/js'), 8 | }, 9 | resolve: { 10 | extensions: ['.js', '.ts'] 11 | }, 12 | module: { 13 | rules: [ 14 | { test: /\.(ts)$/, loader: 'awesome-typescript-loader' } 15 | ], 16 | }, 17 | mode: "development", 18 | devtool: 'source-map' 19 | }; 20 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | /* App-Owns-Data Lab Styles from Power BI Dev Camp */ 2 | 3 | ul i { padding-right: 4px; } 4 | h1 { font-size: 1.35em; } 5 | h2 { font-size: 1.25em; } 6 | h3 { font-size: 1.1em; } 7 | h4 { font-size: 1.0em; } 8 | 9 | .mb-3, .my-3 { 10 | margin-bottom: 0 !important; 11 | } 12 | 13 | nav { 14 | margin-bottom: 0px; 15 | } 16 | 17 | i { 18 | margin-right: 4px; 19 | } 20 | 21 | 22 | #banner { 23 | padding-left: 0px; 24 | padding-right: 0px; 25 | } 26 | 27 | .jumbotron { 28 | background-color: #EDBE11; 29 | border: 1px solid #AAA; 30 | margin-top: 16px; 31 | } 32 | 33 | /* styles for embed page */ 34 | 35 | #content-box { 36 | padding-left: 0; 37 | padding-right: 0; 38 | border: solid 1px black; 39 | } 40 | 41 | #left-nav { 42 | background-color: black; 43 | color: white; 44 | padding: 0; 45 | border: 1px solid black; 46 | } 47 | 48 | #left-nav .navbar-header { 49 | color: black; 50 | background-color: #EDBE11; 51 | padding: 2px; 52 | padding-left: 4px; 53 | border-bottom: 1px solid #333333; 54 | font-size: 14px; 55 | } 56 | 57 | #workspace-selector { 58 | width: 100%; 59 | padding: 0; 60 | } 61 | 62 | #workspace-selector-dropdown { 63 | padding: 0px; 64 | } 65 | 66 | .dropdown-toggle::after { 67 | position: absolute; 68 | right: 8px; 69 | top: 45%; 70 | } 71 | 72 | #left-nav .navbar-collapse, #left-nav .dropdown { 73 | background-color: white; 74 | color: black; 75 | padding: 8px; 76 | font-size: 12px; 77 | border-bottom: 1px solid #333333; 78 | } 79 | 80 | #left-nav .navbar-collapse { 81 | padding: 8px; 82 | } 83 | 84 | #left-nav .dropdown { 85 | padding: 0px; 86 | } 87 | 88 | #left-nav .dropdown button { 89 | margin: 0px; 90 | padding: 4px; 91 | width: 100%; 92 | text-align: left; 93 | } 94 | 95 | 96 | #embed-container { 97 | min-height: 480px; 98 | height: 600px; 99 | } 100 | 101 | 102 | .no-content { 103 | font-style: italic; 104 | margin-left: 16px; 105 | color: #88AAAA; 106 | } 107 | 108 | #embedding-instructions { 109 | margin: 24px; 110 | color: #BBBBBB; 111 | } 112 | 113 | #embed-toolbar { 114 | margin-top: -1px; 115 | background-color: black; 116 | color: white; 117 | padding: 4px; 118 | font-size: 12px; 119 | } 120 | 121 | #embed-toolbar button { 122 | margin: 0px; 123 | font-size: 9px; 124 | font-weight: bold; 125 | padding: 2px; 126 | color: #222; 127 | background-color: #F2C811; 128 | border-color: #111; 129 | display: inline; 130 | float: right; 131 | margin-left: 8px; 132 | padding-left: 6px; 133 | padding-right: 6px; 134 | } 135 | 136 | .workspaces-toolbar { 137 | background-color: #CCCCCC; 138 | border: 1px solid #666666; 139 | border-top-style: none; 140 | border-bottom-left-radius: 4px; 141 | border-bottom-right-radius: 4px; 142 | padding: 8px; 143 | margin-bottom: 16px; 144 | text-align: left; 145 | } 146 | 147 | #create-workspace-button, #back-to-workspaces-button { 148 | background-color: #F2C811; 149 | color: black; 150 | border: 1px solid black; 151 | } 152 | 153 | table a.nav-link { 154 | margin: 0px; 155 | padding: 4px; 156 | padding-left: 6px; 157 | background-color: #F2C811; 158 | color: black; 159 | border: 1px solid black; 160 | border-radius: 4px; 161 | text-align: center; 162 | } 163 | 164 | .create-workspace-fieldset { 165 | background-color: #EEEEEE; 166 | border: solid 1px #AAAAAA; 167 | padding: 24px; 168 | } 169 | 170 | .create-workspace-fieldset legend { 171 | width: auto; 172 | padding: 8px; 173 | } 174 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerBiDevCamp/DOTNET5-AppOwnsData-Tutorial/ad38de0afe91ad41ce5af6f2b61554f8f4ae7040/Solution/Final/AppOwnsData/wwwroot/favicon.ico -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | // for details on configuring this project to bundle and minify static web assets. 3 | 4 | // Write your JavaScript code. 5 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2018 Twitter, Inc. 4 | Copyright (c) 2011-2018 The Bootstrap Authors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v4.3.1 (https://getbootstrap.com/) 3 | * Copyright 2011-2019 The Bootstrap Authors 4 | * Copyright 2011-2019 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important} 8 | /*# sourceMappingURL=bootstrap-reboot.min.css.map */ -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Solution/Final/AppOwnsData/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors, https://js.foundation/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /StudentLabFiles/Create-AzureAD-Application.ps1: -------------------------------------------------------------------------------- 1 | $authResult = Connect-AzureAD 2 | 3 | $tenantId = $authResult.TenantId.ToString() 4 | $tenantDomain = $authResult.TenantDomain 5 | 6 | $userAccountId = $authResult.Account.Id 7 | $user = Get-AzureADUser -ObjectId $userAccountId 8 | 9 | $appDisplayName = "App-Owns-Data Sample App" 10 | $replyUrl = "https://localhost:5001/signin-oidc" 11 | 12 | # create app secret 13 | $newGuid = New-Guid 14 | $appSecret = ([System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(($newGuid))))+"=" 15 | $startDate = Get-Date 16 | $passwordCredential = New-Object -TypeName Microsoft.Open.AzureAD.Model.PasswordCredential 17 | $passwordCredential.StartDate = $startDate 18 | $passwordCredential.EndDate = $startDate.AddYears(1) 19 | $passwordCredential.KeyId = $newGuid 20 | $passwordCredential.Value = $appSecret 21 | 22 | # create Azure AD Application 23 | $aadApplication = New-AzureADApplication ` 24 | -DisplayName $appDisplayName ` 25 | -PublicClient $false ` 26 | -AvailableToOtherTenants $false ` 27 | -ReplyUrls @($replyUrl) ` 28 | -Homepage $replyUrl ` 29 | -PasswordCredentials $passwordCredential 30 | 31 | # create applicaiton's service principal 32 | $appId = $aadApplication.AppId 33 | $appObjectId = $aadApplication.ObjectId 34 | $serviceServicePrincipal = New-AzureADServicePrincipal -AppId $appId 35 | $serviceServicePrincipalObjectId = $serviceServicePrincipal.ObjectId 36 | 37 | Write-Host "appObjectId: $appObjectId" 38 | 39 | # assign current user as owner 40 | Add-AzureADApplicationOwner -ObjectId $aadApplication.ObjectId -RefObjectId $user.ObjectId 41 | 42 | # add the service princiapl to Azure AD security group named 'Power BI Apps' 43 | $adSecurityGroupName = "Power BI Apps" 44 | $adSecurityGroup = Get-AzureADGroup -Filter "DisplayName eq '$adSecurityGroupName'" 45 | if($adSecurityGroup) { 46 | # Add service principal of the new app as member of Power BI Apps group 47 | Add-AzureADGroupMember -ObjectId $($adSecurityGroup.ObjectId) ` 48 | -RefObjectId $($serviceServicePrincipalObjectId) 49 | 50 | # Display members of the Power BI Apps group 51 | Write-Host "Members of Azure AD group named $adSecurityGroupName" 52 | $members = Get-AzureADGroupMember -ObjectId $($adSecurityGroup.ObjectId) 53 | $members | Format-Table ObjectType, ObjectId, DisplayName 54 | } 55 | 56 | $outputFile = "$PSScriptRoot\AppOwnsDataSampleApp.txt" 57 | Write-Host "Writing info to $outputFile" 58 | Out-File -FilePath $outputFile -Append -InputObject "{" 59 | Out-File -FilePath $outputFile -Append -InputObject " ""AzureAd"": {" 60 | Out-File -FilePath $outputFile -Append -InputObject " ""Instance"": ""https://login.microsoftonline.com/""," 61 | Out-File -FilePath $outputFile -Append -InputObject " ""Domain"": ""$tenantDomain""," 62 | Out-File -FilePath $outputFile -Append -InputObject " ""TenantId"": ""$tenantId""," 63 | Out-File -FilePath $outputFile -Append -InputObject " ""ClientId"": ""$appId""," 64 | Out-File -FilePath $outputFile -Append -InputObject " ""ClientSecret"": ""$appSecret""," 65 | Out-File -FilePath $outputFile -Append -InputObject " ""CallbackPath"": ""/signin-oidc""," 66 | Out-File -FilePath $outputFile -Append -InputObject " ""SignedOutCallbackPath"": ""/signout-callback-oidc""" 67 | Out-File -FilePath $outputFile -Append -InputObject " }," 68 | Out-File -FilePath $outputFile -Append -InputObject " ""PowerBi"": {" 69 | Out-File -FilePath $outputFile -Append -InputObject " ""ServiceRootUrl"": ""https://api.powerbi.com/""" 70 | Out-File -FilePath $outputFile -Append -InputObject " }," 71 | Out-File -FilePath $outputFile -Append -InputObject " ""Logging"": {" 72 | Out-File -FilePath $outputFile -Append -InputObject " ""LogLevel"": {" 73 | Out-File -FilePath $outputFile -Append -InputObject " ""Default"": ""Information""," 74 | Out-File -FilePath $outputFile -Append -InputObject " ""Microsoft"": ""Warning""," 75 | Out-File -FilePath $outputFile -Append -InputObject " ""Microsoft.Hosting.Lifetime"": ""Information""" 76 | Out-File -FilePath $outputFile -Append -InputObject " }" 77 | Out-File -FilePath $outputFile -Append -InputObject " }," 78 | Out-File -FilePath $outputFile -Append -InputObject " ""AllowedHosts"": ""*""" 79 | Out-File -FilePath $outputFile -Append -InputObject "}" 80 | 81 | Notepad $outputFile -------------------------------------------------------------------------------- /StudentLabFiles/Create-AzureAD-Group.ps1: -------------------------------------------------------------------------------- 1 | $authResult = Connect-AzureAD 2 | 3 | $adSecurityGroupName = "Power BI Apps" 4 | 5 | $adSecurityGroup = New-AzureADGroup ` 6 | -DisplayName $adSecurityGroupName ` 7 | -SecurityEnabled $true ` 8 | -MailEnabled $false ` 9 | -MailNickName notSet 10 | 11 | $adSecurityGroup | Format-Table DisplayName, ObjectId -------------------------------------------------------------------------------- /StudentLabFiles/Exercise 2 - Embed.cshtml.txt: -------------------------------------------------------------------------------- 1 |

TODO: Embed Report Here

-------------------------------------------------------------------------------- /StudentLabFiles/Exercise 2 - HomeController.cs.txt: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Authorization; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.Extensions.Logging; 9 | using UserOwnsData.Models; 10 | using UserOwnsData.Services; 11 | 12 | namespace UserOwnsData.Controllers { 13 | 14 | [Authorize] 15 | public class HomeController : Controller { 16 | 17 | private PowerBiServiceApi powerBiServiceApi; 18 | 19 | public HomeController(PowerBiServiceApi powerBiServiceApi) { 20 | this.powerBiServiceApi = powerBiServiceApi; 21 | } 22 | 23 | [AllowAnonymous] 24 | public IActionResult Index() { 25 | return View(); 26 | } 27 | 28 | public async Task Embed() { 29 | return View(); 30 | } 31 | 32 | [AllowAnonymous] 33 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 34 | public IActionResult Error() { 35 | return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); 36 | } 37 | 38 | } 39 | } -------------------------------------------------------------------------------- /StudentLabFiles/Exercise 2 - Startup.cs.txt: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Authentication; 6 | using Microsoft.AspNetCore.Authorization; 7 | using Microsoft.AspNetCore.Builder; 8 | using Microsoft.AspNetCore.Hosting; 9 | using Microsoft.AspNetCore.Http; 10 | using Microsoft.AspNetCore.HttpsPolicy; 11 | using Microsoft.AspNetCore.Mvc.Authorization; 12 | using Microsoft.Extensions.Configuration; 13 | using Microsoft.Extensions.DependencyInjection; 14 | using Microsoft.Extensions.Hosting; 15 | using Microsoft.Identity.Web; 16 | using Microsoft.Identity.Web.UI; 17 | using Microsoft.Identity.Web.TokenCacheProviders; 18 | using Microsoft.Identity.Web.TokenCacheProviders.InMemory; 19 | 20 | using UserOwnsData.Services; 21 | 22 | namespace UserOwnsData { 23 | 24 | public class Startup { 25 | 26 | public Startup(IConfiguration configuration) { 27 | Configuration = configuration; 28 | } 29 | 30 | public IConfiguration Configuration { get; } 31 | 32 | // This method gets called by the runtime. Use this method to add services to the container. 33 | public void ConfigureServices(IServiceCollection services) { 34 | 35 | services 36 | .AddMicrosoftIdentityWebAppAuthentication(Configuration) 37 | .EnableTokenAcquisitionToCallDownstreamApi(PowerBiServiceApi.RequiredScopes) 38 | .AddInMemoryTokenCaches(); 39 | 40 | services.AddScoped(typeof(PowerBiServiceApi)); 41 | 42 | var mvcBuilder = services.AddControllersWithViews(options => { 43 | var policy = new AuthorizationPolicyBuilder() 44 | .RequireAuthenticatedUser() 45 | .Build(); 46 | options.Filters.Add(new AuthorizeFilter(policy)); 47 | }); 48 | 49 | mvcBuilder.AddMicrosoftIdentityUI(); 50 | 51 | services.AddRazorPages(); 52 | } 53 | 54 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 55 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { 56 | if (env.IsDevelopment()) { 57 | app.UseDeveloperExceptionPage(); 58 | } 59 | else { 60 | app.UseExceptionHandler("/Home/Error"); 61 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 62 | app.UseHsts(); 63 | } 64 | app.UseHttpsRedirection(); 65 | app.UseStaticFiles(); 66 | 67 | app.UseRouting(); 68 | 69 | app.UseAuthentication(); 70 | app.UseAuthorization(); 71 | 72 | app.UseEndpoints(endpoints => { 73 | endpoints.MapControllerRoute( 74 | name: "default", 75 | pattern: "{controller=Home}/{action=Index}/{id?}"); 76 | endpoints.MapRazorPages(); 77 | }); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /StudentLabFiles/Exercise 2 - _Layout.cshtml.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | App-Owns-Data Demo 7 | 8 | 9 | 10 | 11 |
12 | 32 |
33 |
34 |
35 | @RenderBody() 36 |
37 |
38 | 39 | 40 | @RenderSection("Scripts", required: false) 41 | 42 | 43 | -------------------------------------------------------------------------------- /StudentLabFiles/Exercise 2 - _LoginPartial.cshtml.txt: -------------------------------------------------------------------------------- 1 | @using System.Security.Principal 2 | 3 | -------------------------------------------------------------------------------- /StudentLabFiles/Exercise 2 - index.cshtml.txt: -------------------------------------------------------------------------------- 1 | @using System.Security.Principal 2 | 3 | @if (User.Identity.IsAuthenticated) { 4 |
5 |

Welcome @User.FindFirst("name").Value

6 |

You have now logged into this application.

7 |
8 | } 9 | else { 10 |
11 |

Welcome to the App-Owns-Data Tutorial

12 |

Click the sign in link in the upper right to get started

13 |
14 | } -------------------------------------------------------------------------------- /StudentLabFiles/Exercise 2 - site.css.txt: -------------------------------------------------------------------------------- 1 | /* App-Owns-Data Lab Styles from Power BI Dev Camp */ 2 | 3 | ul i { padding-right: 4px; } 4 | h1 { font-size: 1.35em; } 5 | h2 { font-size: 1.25em; } 6 | h3 { font-size: 1.1em; } 7 | h4 { font-size: 1.0em; } 8 | 9 | .mb-3, .my-3 { 10 | margin-bottom: 0 !important; 11 | } 12 | 13 | nav { 14 | margin-bottom: 0px; 15 | } 16 | 17 | i { 18 | margin-right: 4px; 19 | } 20 | 21 | 22 | #banner { 23 | padding-left: 0px; 24 | padding-right: 0px; 25 | } 26 | 27 | .jumbotron { 28 | background-color: #EDBE11; 29 | border: 1px solid #AAA; 30 | margin-top: 16px; 31 | } 32 | 33 | /* styles for embed page */ 34 | 35 | #content-box { 36 | padding-left: 0; 37 | padding-right: 0; 38 | border: solid 1px black; 39 | } 40 | 41 | #left-nav { 42 | background-color: black; 43 | color: white; 44 | padding: 0; 45 | border: 1px solid black; 46 | } 47 | 48 | #left-nav .navbar-header { 49 | color: black; 50 | background-color: #EDBE11; 51 | padding: 2px; 52 | padding-left: 4px; 53 | border-bottom: 1px solid #333333; 54 | font-size: 14px; 55 | } 56 | 57 | #workspace-selector { 58 | width: 100%; 59 | padding: 0; 60 | } 61 | 62 | #workspace-selector-dropdown { 63 | padding: 0px; 64 | } 65 | 66 | .dropdown-toggle::after { 67 | position: absolute; 68 | right: 8px; 69 | top: 45%; 70 | } 71 | 72 | #left-nav .navbar-collapse, #left-nav .dropdown { 73 | background-color: white; 74 | color: black; 75 | padding: 8px; 76 | font-size: 12px; 77 | border-bottom: 1px solid #333333; 78 | } 79 | 80 | #left-nav .navbar-collapse { 81 | padding: 8px; 82 | } 83 | 84 | #left-nav .dropdown { 85 | padding: 0px; 86 | } 87 | 88 | #left-nav .dropdown button { 89 | margin: 0px; 90 | padding: 4px; 91 | width: 100%; 92 | text-align: left; 93 | } 94 | 95 | 96 | #embed-container { 97 | min-height: 480px; 98 | height: 600px; 99 | } 100 | 101 | 102 | .no-content { 103 | font-style: italic; 104 | margin-left: 16px; 105 | color: #88AAAA; 106 | } 107 | 108 | #embedding-instructions { 109 | margin: 24px; 110 | color: #BBBBBB; 111 | } 112 | 113 | #embed-toolbar { 114 | margin-top: -1px; 115 | background-color: black; 116 | color: white; 117 | padding: 4px; 118 | font-size: 12px; 119 | } 120 | 121 | #embed-toolbar button { 122 | margin: 0px; 123 | font-size: 9px; 124 | font-weight: bold; 125 | padding: 2px; 126 | color: #222; 127 | background-color: #F2C811; 128 | border-color: #111; 129 | display: inline; 130 | float: right; 131 | margin-left: 8px; 132 | padding-left: 6px; 133 | padding-right: 6px; 134 | } 135 | 136 | .workspaces-toolbar { 137 | background-color: #CCCCCC; 138 | border: 1px solid #666666; 139 | border-top-style: none; 140 | border-bottom-left-radius: 4px; 141 | border-bottom-right-radius: 4px; 142 | padding: 8px; 143 | margin-bottom: 16px; 144 | text-align: left; 145 | } 146 | 147 | #create-workspace-button, #back-to-workspaces-button { 148 | background-color: #F2C811; 149 | color: black; 150 | border: 1px solid black; 151 | } 152 | 153 | table a.nav-link { 154 | margin: 0px; 155 | padding: 4px; 156 | padding-left: 6px; 157 | background-color: #F2C811; 158 | color: black; 159 | border: 1px solid black; 160 | border-radius: 4px; 161 | text-align: center; 162 | } 163 | 164 | .create-workspace-fieldset { 165 | background-color: #EEEEEE; 166 | border: solid 1px #AAAAAA; 167 | padding: 24px; 168 | } 169 | 170 | .create-workspace-fieldset legend { 171 | width: auto; 172 | padding: 8px; 173 | } 174 | -------------------------------------------------------------------------------- /StudentLabFiles/Exercise 3 - Embed.cshtml.txt: -------------------------------------------------------------------------------- 1 | @model UserOwnsData.Services.EmbeddedReportViewModel; 2 | 3 | 11 | 12 |

Report View Model Data

13 | 14 | 15 | 16 | 17 | 18 | 19 |
Report Name@Model.Name
Report ID@Model.Id
Embed Url@Model.EmbedUrl
Token@Model.Token
-------------------------------------------------------------------------------- /StudentLabFiles/Exercise 3 - HomeController.cs.txt: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Authorization; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.Extensions.Logging; 9 | using UserOwnsData.Models; 10 | using UserOwnsData.Services; 11 | 12 | namespace UserOwnsData.Controllers { 13 | 14 | [Authorize] 15 | public class HomeController : Controller { 16 | 17 | private PowerBiServiceApi powerBiServiceApi; 18 | 19 | public HomeController(PowerBiServiceApi powerBiServiceApi) { 20 | this.powerBiServiceApi = powerBiServiceApi; 21 | } 22 | 23 | [AllowAnonymous] 24 | public IActionResult Index() { 25 | return View(); 26 | } 27 | 28 | public async Task Embed() { 29 | 30 | Guid workspaceId = new Guid("33333333-3333-3333-3333-333333333333"); 31 | Guid reportId = new Guid("44444444-4444-4444-4444-444444444444"); 32 | 33 | var viewModel = await powerBiServiceApi.GetReport(workspaceId, reportId); 34 | return View(viewModel); 35 | 36 | } 37 | 38 | [AllowAnonymous] 39 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 40 | public IActionResult Error() { 41 | return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); 42 | } 43 | 44 | } 45 | } -------------------------------------------------------------------------------- /StudentLabFiles/Exercise 3 - PowerBiServiceApi.cs.txt: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Identity.Web; 6 | using Microsoft.Rest; 7 | using Microsoft.PowerBI.Api; 8 | using Microsoft.PowerBI.Api.Models; 9 | using Newtonsoft.Json; 10 | 11 | namespace AppOwnsData.Services { 12 | 13 | public class EmbeddedReportViewModel { 14 | public string Id; 15 | public string Name; 16 | public string EmbedUrl; 17 | public string Token; 18 | } 19 | 20 | public class PowerBiServiceApi { 21 | 22 | private ITokenAcquisition tokenAcquisition { get; } 23 | private string urlPowerBiServiceApiRoot { get; } 24 | 25 | public PowerBiServiceApi(IConfiguration configuration, ITokenAcquisition tokenAcquisition) { 26 | this.urlPowerBiServiceApiRoot = configuration["PowerBi:ServiceRootUrl"]; 27 | this.tokenAcquisition = tokenAcquisition; 28 | } 29 | 30 | public const string powerbiApiDefaultScope = "https://analysis.windows.net/powerbi/api/.default"; 31 | 32 | public string GetAccessToken() { 33 | return this.tokenAcquisition.GetAccessTokenForAppAsync(powerbiApiDefaultScope).Result; 34 | } 35 | 36 | public PowerBIClient GetPowerBiClient() { 37 | var tokenCredentials = new TokenCredentials(GetAccessToken(), "Bearer"); 38 | return new PowerBIClient(new Uri(urlPowerBiServiceApiRoot), tokenCredentials); 39 | } 40 | 41 | public async Task GetReport(Guid WorkspaceId, Guid ReportId) { 42 | 43 | PowerBIClient pbiClient = GetPowerBiClient(); 44 | 45 | // call to Power BI Service API to get embedding data 46 | var report = await pbiClient.Reports.GetReportInGroupAsync(WorkspaceId, ReportId); 47 | 48 | // generate read-only embed token for the report 49 | var datasetId = report.DatasetId; 50 | var tokenRequest = new GenerateTokenRequest(TokenAccessLevel.View, datasetId); 51 | var embedTokenResponse = await pbiClient.Reports.GenerateTokenAsync(WorkspaceId, ReportId, tokenRequest); 52 | var embedToken = embedTokenResponse.Token; 53 | 54 | // return report embedding data to caller 55 | return new EmbeddedReportViewModel { 56 | Id = report.Id.ToString(), 57 | EmbedUrl = report.EmbedUrl, 58 | Name = report.Name, 59 | Token = embedToken 60 | }; 61 | } 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /StudentLabFiles/Exercise 4 - Embed.cshtml.txt: -------------------------------------------------------------------------------- 1 | @model UserOwnsData.Services.EmbeddedReportViewModel; 2 | 3 |
4 | 5 | @section Scripts { 6 | 13 | 14 | } -------------------------------------------------------------------------------- /StudentLabFiles/Exercise 4 - embed.js.txt: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | // 1 - get DOM object for div that is report container 4 | var reportContainer = document.getElementById("embed-container"); 5 | 6 | // 2 - get report embedding data from view model 7 | var reportId = window.viewModel.reportId; 8 | var embedUrl = window.viewModel.embedUrl; 9 | var token = window.viewModel.token 10 | 11 | // 3 - embed report using the Power BI JavaScript API. 12 | var models = window['powerbi-client'].models; 13 | 14 | var config = { 15 | type: 'report', 16 | id: reportId, 17 | embedUrl: embedUrl, 18 | accessToken: token, 19 | permissions: models.Permissions.All, 20 | tokenType: models.TokenType.Embed, 21 | viewMode: models.ViewMode.View, 22 | settings: { 23 | panes: { 24 | filters: { expanded: false, visible: true }, 25 | pageNavigation: { visible: false } 26 | } 27 | } 28 | }; 29 | 30 | // Embed the report and display it within the div container. 31 | var report = powerbi.embed(reportContainer, config); 32 | 33 | // 4 - add logic to resize embed container on window resize event 34 | var heightBuffer = 12; 35 | var newHeight = $(window).height() - ($("header").height() + heightBuffer); 36 | $("#embed-container").height(newHeight); 37 | $(window).resize(function () { 38 | var newHeight = $(window).height() - ($("header").height() + heightBuffer); 39 | $("#embed-container").height(newHeight); 40 | }); 41 | 42 | }); -------------------------------------------------------------------------------- /StudentLabFiles/Exercise 5 - embed.ts.txt: -------------------------------------------------------------------------------- 1 | import * as $ from 'jquery'; 2 | 3 | import * as powerbi from "powerbi-client"; 4 | import * as models from "powerbi-models"; 5 | 6 | // ensure Power BI JavaScript API has loaded 7 | require('powerbi-models'); 8 | require('powerbi-client'); 9 | 10 | class viewModel { 11 | reportId: string; 12 | embedUrl: string; 13 | token: string; 14 | } 15 | 16 | $(() => { 17 | 18 | // get DOM object div for report container 19 | var reportContainer: HTMLElement = document.getElementById("embed-container"); 20 | 21 | var viewModel: viewModel = window["viewModel"]; 22 | 23 | var config: powerbi.IEmbedConfiguration = { 24 | type: 'report', 25 | id: viewModel.reportId, 26 | embedUrl: viewModel.embedUrl, 27 | accessToken: viewModel.token, 28 | permissions: models.Permissions.All, 29 | tokenType: models.TokenType.Embed, 30 | viewMode: models.ViewMode.View, 31 | settings: { 32 | panes: { 33 | filters: { expanded: false, visible: true }, 34 | pageNavigation: { visible: true } 35 | }, 36 | persistentFiltersEnabled: true 37 | } 38 | }; 39 | 40 | // Embed the report and display it within the div container. 41 | var report = window.powerbi.embed(reportContainer, config); 42 | 43 | // display report properties in browser console 44 | console.log(report); 45 | 46 | // add logic to resize embed container element on window rersize event 47 | var heightBuffer = 12; 48 | var newHeight = $(window).height() - ($("header").height() + heightBuffer); 49 | $("#embed-container").height(newHeight); 50 | $(window).on("resize",function () { 51 | var newHeight = $(window).height() - ($("header").height() + heightBuffer); 52 | $("#embed-container").height(newHeight); 53 | }); 54 | 55 | }); -------------------------------------------------------------------------------- /StudentLabFiles/Exercise 6 - Embed.cshtml.txt: -------------------------------------------------------------------------------- 1 | @model string 2 | 3 | 6 | 7 |
8 |
9 |
10 |
11 | 14 | 21 | 24 | 27 | 30 | 33 |
34 |
35 |
36 |
37 |

To Perform App-Owns-Data Embedding

38 |
    39 |
  • Use the workspace selector menu to choose a workspace.
  • 40 |
  • Click on an existing report to embed it.
  • 41 |
  • Click on an existing dataset to create a new report.
  • 42 |
43 |
44 | 49 |
50 |
51 | 52 |
53 |
54 | 55 | @section Scripts { 56 | 57 | } -------------------------------------------------------------------------------- /StudentLabFiles/Exercise 6 - HomeController.cs.txt: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Authorization; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.Extensions.Logging; 9 | using AppOwnsData.Models; 10 | using AppOwnsData.Services; 11 | using Microsoft.Rest; 12 | 13 | namespace AppOwnsData.Controllers { 14 | 15 | [Authorize] 16 | public class HomeController : Controller { 17 | 18 | private PowerBiServiceApi powerBiServiceApi; 19 | 20 | public HomeController(PowerBiServiceApi powerBiServiceApi) { 21 | this.powerBiServiceApi = powerBiServiceApi; 22 | } 23 | 24 | [AllowAnonymous] 25 | public IActionResult Index() { 26 | return View(); 27 | } 28 | 29 | public async Task Embed(string workspaceId) { 30 | 31 | Guid validWorkspaceId; 32 | bool workspaceIdIsValid = Guid.TryParse(workspaceId, out validWorkspaceId); 33 | 34 | try { 35 | if (workspaceIdIsValid) { 36 | var viewModel = await this.powerBiServiceApi.GetEmbeddedViewModel(workspaceId); 37 | return View(viewModel as object); 38 | } 39 | else { 40 | var firstWorkspace = await this.powerBiServiceApi.GetFirstWorkspace(); 41 | if (firstWorkspace != null) { 42 | return RedirectToAction("Embed", "Home", new { workspaceId = firstWorkspace.Id }); 43 | } 44 | else { 45 | throw new ApplicationException("This service principal for the Azure AD app is not a member of any workspaces."); 46 | } 47 | } 48 | } 49 | catch (HttpOperationException ex) { 50 | return StatusCode(500, "Unexpected HTTP exception: " + ex.Response.Content); 51 | } 52 | catch (Exception ex) { 53 | return StatusCode(500, "Unexpected exception: " + ex.Message); 54 | } 55 | } 56 | 57 | [AllowAnonymous] 58 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 59 | public IActionResult Error() { 60 | return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); 61 | } 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /StudentLabFiles/Troubleshooting/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (web)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/net5.0/AppOwnsData.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | "stopAtEntry": false, 17 | // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser 18 | "serverReadyAction": { 19 | "action": "openExternally", 20 | "pattern": "\\bNow listening on:\\s+(https?://\\S+)" 21 | }, 22 | "env": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | }, 25 | "sourceFileMap": { 26 | "/Views": "${workspaceFolder}/Views" 27 | } 28 | }, 29 | { 30 | "name": ".NET Core Attach", 31 | "type": "coreclr", 32 | "request": "attach", 33 | "processId": "${command:pickProcess}" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /StudentLabFiles/Troubleshooting/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/AppOwnsData.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/AppOwnsData.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "${workspaceFolder}/AppOwnsData.csproj", 36 | "/property:GenerateFullPaths=true", 37 | "/consoleloggerparameters:NoSummary" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /StudentLabFiles/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerBiDevCamp/DOTNET5-AppOwnsData-Tutorial/ad38de0afe91ad41ce5af6f2b61554f8f4ae7040/StudentLabFiles/favicon.ico -------------------------------------------------------------------------------- /StudentLabFiles/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "name": "app-owns-data", 4 | "private": true, 5 | "scripts": { 6 | "build": "webpack" 7 | }, 8 | "devDependencies": { 9 | "@types/jquery": "3.3.29", 10 | "@types/node": "^14.0.11", 11 | "awesome-typescript-loader": "^5.2.1", 12 | "jquery": "^3.5.1", 13 | "powerbi-client": "^2.13.3", 14 | "powerbi-models": "^1.4.0", 15 | "typescript": "^4.0.2", 16 | "webpack": "^4.44.1", 17 | "webpack-cli": "^3.3.12" 18 | }, 19 | "dependencies": {} 20 | } -------------------------------------------------------------------------------- /StudentLabFiles/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "react", 4 | "module": "commonjs", 5 | "sourceMap": true, 6 | "target": "es5", 7 | "lib": [ "es6", "dom" ], 8 | "suppressImplicitAnyIndexErrors": true, 9 | "noImplicitReturns": true, 10 | "noImplicitThis": true, 11 | "noImplicitAny": true, 12 | "strictNullChecks": false, 13 | "experimentalDecorators": true 14 | }, 15 | "exclude": [ 16 | "node_modules", "wwwroot" 17 | ] 18 | } -------------------------------------------------------------------------------- /StudentLabFiles/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './Scripts/embed.ts', 5 | output: { 6 | filename: 'embed.js', 7 | path: path.resolve(__dirname, 'wwwroot/js'), 8 | }, 9 | resolve: { 10 | extensions: ['.js', '.ts'] 11 | }, 12 | module: { 13 | rules: [ 14 | { test: /\.(ts)$/, loader: 'awesome-typescript-loader' } 15 | ], 16 | }, 17 | mode: "development", 18 | devtool: 'source-map' 19 | }; 20 | -------------------------------------------------------------------------------- /Tutorial.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerBiDevCamp/DOTNET5-AppOwnsData-Tutorial/ad38de0afe91ad41ce5af6f2b61554f8f4ae7040/Tutorial.docx -------------------------------------------------------------------------------- /Tutorial.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerBiDevCamp/DOTNET5-AppOwnsData-Tutorial/ad38de0afe91ad41ce5af6f2b61554f8f4ae7040/Tutorial.pdf --------------------------------------------------------------------------------