Skip to content

Commit

Permalink
Add support for uploading large PBIX reports (#332)
Browse files Browse the repository at this point in the history
* Add support for uploading large PBIX reports

* Added getting server configurations related to the maximum possible size of the uploaded file

* Add tests

* Resolve review points

* Resolve review points

Co-authored-by: Maksim Melnikov (Akvelon INC) <[email protected]>
  • Loading branch information
Melnikov37 and Maksim Melnikov (Akvelon INC) authored Jun 3, 2021
1 parent ef3aa0d commit 60db0c7
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
function Get-RsRestPublicServerSetting
{
<#
.SYNOPSIS
This function gets the public settings of the RS server.
.DESCRIPTION
This function gets the value of the specified property from the public settings of the RS server.
.PARAMETER Property
Specify the name of the property.
.PARAMETER ReportPortalUri
Specify the Report Portal URL to your SQL Server Reporting Services Instance.
.PARAMETER RestApiVersion
Specify the version of REST Endpoint to use. Valid values are: "v2.0".
.PARAMETER Credential
Specify the credentials to use when connecting to the Report Server.
.PARAMETER WebSession
Specify the session to be used when making calls to REST Endpoint.
.EXAMPLE
Get-RsRestPublicServerSetting -Property "MaxFileSizeMb"
Description
-----------
Gets the value of the property "MaxFileSizeMb" from the Report Server located at http://localhost/reports.
#>

[CmdletBinding()]
param
(
[Parameter(Mandatory = $True)]
[string]
$Property,

[string]
$ReportPortalUri,

[Alias('ApiVersion')]
[ValidateSet("v2.0")]
[string]
$RestApiVersion = "v2.0",

[Alias('ReportServerCredentials')]
[System.Management.Automation.PSCredential]
$Credential,

[Microsoft.PowerShell.Commands.WebRequestSession]
$WebSession
)
Begin
{
$WebSession = New-RsRestSessionHelper -BoundParameters $PSBoundParameters
$ReportPortalUri = Get-RsPortalUriHelper -WebSession $WebSession
$systemPropertiesUri = $ReportPortalUri + "api/$RestApiVersion/System/Properties?properties={0}"
}
Process
{
try
{
Write-Verbose "Getting server configuration - $Property"

$uri = [String]::Format($systemPropertiesUri, $Property)

if ($Credential -ne $null)
{
$response = Invoke-RestMethod -Uri $uri -Method Get -WebSession $WebSession -Credential $Credential -Verbose:$false
}
else
{
$response = Invoke-RestMethod -Uri $uri -Method Get -WebSession $WebSession -UseDefaultCredentials -Verbose:$false
}

if ($response -ne $null -and $response.value -ne $null -and $response.value[0] -ne $null -and $response.value[0].Name -eq $Property)
{
return $response.value[0].Value
}
else
{
return $null
}
}
catch
{
Write-Error (New-Object System.Exception("Failed to get server setting: $($_.Exception.Message)", $_.Exception))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function Write-RsRestCatalogItem
Specify the Report Portal URL to your SQL Server Reporting Services Instance.
.PARAMETER RestApiVersion
Specify the version of REST Endpoint to use. Valid values are: "v1.0", "v2.0".
Specify the version of REST Endpoint to use. Valid values are: "v1.0", "v2.0".
NOTE:
- v1.0 of REST Endpoint is not supported by Microsoft and is for SSRS 2016.
- v2.0 of REST Endpoint is supported by Microsoft and is for SSRS 2017, PBIRS October 2017 and newer releases.
Expand All @@ -37,6 +37,12 @@ function Write-RsRestCatalogItem
.PARAMETER WebSession
Specify the session to be used when making calls to REST Endpoint.
.PARAMETER MaxFileSizeInMb
Specify the maximum file size for the PBIX report.
.PARAMETER MinLargeFileSizeInMb
Specify the smallest possible size for a large PBIX report.
.EXAMPLE
Write-RsRestCatalogItem -Path 'c:\reports\monthlyreport.rdl' -RsFolder '/monthlyreports'
Expand Down Expand Up @@ -96,7 +102,13 @@ function Write-RsRestCatalogItem
$Credential,

[Microsoft.PowerShell.Commands.WebRequestSession]
$WebSession
$WebSession,

[float]
$MaxFileSizeInMb = 2000,

[float]
$MinLargeFileSizeInMb = 25
)
Begin
{
Expand All @@ -110,6 +122,7 @@ function Write-RsRestCatalogItem
else
{
$catalogItemsByPathApi = $ReportPortalUri + "api/$RestApiVersion/CatalogItems(Path='{0}')"
$powerBIReportsByPathApi = $ReportPortalUri + "api/$RestApiVersion/PowerBIReports(Path='{0}')/Model.Upload"
}
$catalogItemsUpdateUri = $ReportPortalUri + "api/$RestApiVersion/CatalogItems({0})"
}
Expand Down Expand Up @@ -226,37 +239,103 @@ function Write-RsRestCatalogItem
}
else
{
$bytes = [System.IO.File]::ReadAllBytes($EntirePath)
$payload = @{
"@odata.type" = "#Model.$itemType";
"Content" = [System.Convert]::ToBase64String($bytes);
"ContentType"="";
"Name" = $itemName;
"Description" = $Description
"Path" = $itemPath;
$fileBytes = [System.IO.File]::ReadAllBytes($EntirePath)
$fileSizeInMb = (Get-Item $EntirePath).length/1MB

if ($itemType -eq "PowerBIReport" -and $fileSizeInMb -ge $MinLargeFileSizeInMb)
{
$maxServerFileSizeInMb = Get-RsRestPublicServerSetting -Property "MaxFileSizeMb" -ReportPortalUri $ReportPortalUri -WebSession $WebSession
if ($fileSizeInMb -gt $MaxFileSizeInMb)
{
throw "This file is too large to be uploaded. Files larger than $MaxFileSizeInMb MB are not currently supported: $item!"
}
elseif ($maxServerFileSizeInMb -gt 0 -and $fileSizeInMb -gt $maxServerFileSizeInMb) {
throw "This file is too large to be uploaded. Files larger than $maxServerFileSizeInMb MB are not currently supported: $item!"
}

Write-Verbose "PowerBIReport $item is a large"

$isLargePowerBIReport = $true
$pbixPayload = [System.Text.Encoding]::GetEncoding('ISO-8859-1').GetString($fileBytes)
$boundary = [System.Guid]::NewGuid().ToString()
$LF = "`r`n"

$bodyLines = (
# Name
"--$boundary",
"Content-Disposition: form-data; name=`"Name`"$LF",
$itemName,
# ContentType
"--$boundary",
"Content-Disposition: form-data; name=`"ContentType`"$LF",
"",
# Content
"--$boundary",
"Content-Disposition: form-data; name=`"Content`"$LF",
"undefined",
# Path
"--$boundary",
"Content-Disposition: form-data; name=`"Path`"$LF",
$itemPath,
# @odata.type
"--$boundary",
"Content-Disposition: form-data; name=`"@odata.type`"$LF",
"#Model.PowerBIReport",
# File
"--$boundary",
"Content-Disposition: form-data; name=`"File`"; filename=`"$itemName`"",
"Content-Type: application/octet-stream$LF",
$pbixPayload,
"--$boundary--"
) -join $LF
}
else {
Write-Verbose "$item is a small"

$isLargePowerBIReport = $false
$payload = @{
"@odata.type" = "#Model.$itemType";
"Content" = [System.Convert]::ToBase64String($fileBytes);
"ContentType"="";
"Name" = $itemName;
"Description" = $Description
"Path" = $itemPath;
}
}
}

try
{
Write-Verbose "Uploading $EntirePath to $RsFolder..."

$payloadJson = ConvertTo-Json $payload
if ($itemType -eq "PowerBIReport" -and $isLargePowerBIReport -eq $true)
{
Write-Verbose "Uploading $EntirePath to $RsFolder via endpoint for large files..."
$endpointUrl = [String]::Format($powerBIReportsByPathApi, $itemPath)
$contentType = "multipart/form-data; boundary=$boundary"
$requestBody = $bodyLines
}
else
{
Write-Verbose "Uploading $EntirePath to $RsFolder..."
$endpointUrl = $catalogItemsUri
$contentType = "application/json"
$payloadJson = ConvertTo-Json $payload
$requestBody = ([System.Text.Encoding]::UTF8.GetBytes($payloadJson))
}

if ($Credential -ne $null)
{
Invoke-WebRequest -Uri $catalogItemsUri -Method Post -WebSession $WebSession -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -Credential $Credential -Verbose:$false | Out-Null
Invoke-WebRequest -Uri $endpointUrl -Method Post -WebSession $WebSession -Body $requestBody -ContentType $contentType -Credential $Credential -Verbose:$false | Out-Null
}
else
{
Invoke-WebRequest -Uri $catalogItemsUri -Method Post -WebSession $WebSession -Body ([System.Text.Encoding]::UTF8.GetBytes($payloadJson)) -ContentType "application/json" -UseDefaultCredentials -Verbose:$false | Out-Null
Invoke-WebRequest -Uri $endpointUrl -Method Post -WebSession $WebSession -Body $requestBody -ContentType $contentType -UseDefaultCredentials -Verbose:$false | Out-Null
}

Write-Verbose "$EntirePath was uploaded to $RsFolder successfully!"
}
catch
{
if ($_.Exception.Response -ne $null -and $_.Exception.Response.StatusCode -eq 409 -and $Overwrite)
if ($isLargePowerBIReport -ne $true -and $_.Exception.Response -ne $null -and $_.Exception.Response.StatusCode -eq 409 -and $Overwrite)
{
try
{
Expand Down
3 changes: 2 additions & 1 deletion ReportingServicesTools/ReportingServicesTools.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,13 @@
'Get-RsRestItemDataModelParameter',
'Get-RsRestItem',
'Get-RsRestItemAccessPolicy',
'Get-RsRestPublicServerSetting',
'Get-RsSubscription',
'Grant-RsCatalogItemRole',
'Grant-RsRestItemAccessPolicy',
'Grant-RsSystemRole',
'Import-RsSubscriptionXml',
'Initialize-Rs',
'Initialize-Rs',
'New-RsRestCacheRefreshPlan',
'New-RsConfigurationSettingObject',
'New-RsDataSource',
Expand Down
24 changes: 24 additions & 0 deletions Tests/Admin/Rest/Get-RsRestPublicServerSetting.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright (c) 2020 Microsoft Corporation. All Rights Reserved.
# Licensed under the MIT License (MIT)

Describe "Get-RsRestPublicServerSetting" {
Context "Get Catalog Item Policy"{
$reportPortalUri = if ($env:PesterPortalUrl -eq $null) { 'http://localhost/reports' } else { $env:PesterPortalUrl }

It "Should get MaxFileSizeMb property" {
$property = Get-RsRestPublicServerSetting -ReportPortalUri $reportPortalUri -Property "MaxFileSizeMb"
$property | Should -Not -BeNullOrEmpty
$property | Should -Be '1000'
}

It "Should get ShowDownloadMenu property" {
$property = Get-RsRestPublicServerSetting -ReportPortalUri $reportPortalUri -Property "ShowDownloadMenu"
$property | Should -Not -BeNullOrEmpty
$property | Should -Be 'true'
}

It "Should not get BadRandomProperty property" {
Get-RsRestPublicServerSetting -ReportPortalUri $reportPortalUri -Property "BadRandomProperty" | Should -BeNullOrEmpty
}
}
}
15 changes: 13 additions & 2 deletions Tests/CatalogItems/Rest/Write-RsRestCatalogItem.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ function VerifyCatalogItemExists()

Describe "Write-RsRestCatalogItem" {
$rsFolderPath = ""
$localPath = (Get-Item -Path ".\").FullName + '\Tests\CatalogItems\testResources'
$localPath = (Get-Item -Path ".\").FullName + '\Tests\CatalogItems\testResources'

BeforeEach {
$folderName = 'SUT_WriteRsRestCatalogItem_' + [guid]::NewGuid()
Expand Down Expand Up @@ -66,12 +66,23 @@ Describe "Write-RsRestCatalogItem" {
VerifyCatalogItemExists -itemName 'SimpleMobileReport' -itemType 'MobileReport' -folderPath $rsFolderPath -reportServerUri $reportServerUri
}

It "Should upload a local PBIX file" {
It "Should upload a local small PBIX file" {
$itemPath = $localPath + '\SimplePowerBIReport.pbix'
Write-RsRestCatalogItem -ReportPortalUri $reportPortalUri -Path $itemPath -RsFolder $rsFolderPath -Verbose
VerifyCatalogItemExists -itemName 'SimplePowerBIReport' -itemType 'PowerBIReport' -folderPath $rsFolderPath -reportServerUri $reportServerUri
}

It "Should upload a local large PBIX file" {
$itemPath = $localPath + '\SimplePowerBIReport.pbix'
Write-RsRestCatalogItem -ReportPortalUri $reportPortalUri -Path $itemPath -RsFolder $rsFolderPath -MinLargeFileSizeInMb '0.01' -Verbose
VerifyCatalogItemExists -itemName 'SimplePowerBIReport' -itemType 'PowerBIReport' -folderPath $rsFolderPath -reportServerUri $reportServerUri
}

It "Should upload a local large PBIX file larger than the maximum size" {
$itemPath = $localPath + '\SimplePowerBIReport.pbix'
{ Write-RsRestCatalogItem -ReportPortalUri $reportPortalUri -Path $itemPath -RsFolder $rsFolderPath -MinLargeFileSizeInMb '0.001' -MaxFileSizeInMb '0.01' -Verbose } | Should Throw
}

It "Should upload a local XLS file" {
$itemPath = $localPath + '\OldExcelWorkbook.xls'
Write-RsRestCatalogItem -ReportPortalUri $reportPortalUri -Path $itemPath -RsFolder $rsFolderPath -Verbose
Expand Down

0 comments on commit 60db0c7

Please sign in to comment.