Rendering: Add PDFRendering capability (#84438)

* Rendering: Add PDFRendering capability

* fix tests
This commit is contained in:
Agnès Toulet 2024-03-19 08:48:46 +01:00 committed by GitHub
parent d3571c399a
commit 58170d4141
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 47 additions and 14 deletions

View File

@ -3,6 +3,7 @@ package rendering
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"github.com/Masterminds/semver" "github.com/Masterminds/semver"
) )
@ -17,7 +18,8 @@ type CapabilityName string
const ( const (
ScalingDownImages CapabilityName = "ScalingDownImages" ScalingDownImages CapabilityName = "ScalingDownImages"
FullHeightImages CapabilityName = "FullHeightImages" FullHeightImages CapabilityName = "FullHeightImages"
SvgSanitization CapabilityName = "SvgSanitization" SVGSanitization CapabilityName = "SvgSanitization"
PDFRendering CapabilityName = "PdfRendering"
) )
var ErrUnknownCapability = errors.New("unknown capability") var ErrUnknownCapability = errors.New("unknown capability")
@ -55,3 +57,16 @@ func (rs *RenderingService) HasCapability(ctx context.Context, capability Capabi
return CapabilitySupportRequestResult{IsSupported: compiledSemverConstraint.Check(compiledImageRendererVersion), SemverConstraint: semverConstraint}, nil return CapabilitySupportRequestResult{IsSupported: compiledSemverConstraint.Check(compiledImageRendererVersion), SemverConstraint: semverConstraint}, nil
} }
func (rs *RenderingService) IsCapabilitySupported(ctx context.Context, capabilityName CapabilityName) error {
capability, err := rs.HasCapability(ctx, capabilityName)
if err != nil {
return err
}
if !capability.IsSupported {
return fmt.Errorf("%s unsupported, requires image renderer version: %s", capabilityName, capability.SemverConstraint)
}
return nil
}

View File

@ -14,8 +14,6 @@ import (
"os" "os"
"strconv" "strconv"
"time" "time"
"github.com/grafana/grafana/pkg/services/featuremgmt"
) )
var netTransport = &http.Transport{ var netTransport = &http.Transport{
@ -40,10 +38,6 @@ var (
func (rs *RenderingService) renderViaHTTP(ctx context.Context, renderType RenderType, renderKey string, opts Opts) (*RenderResult, error) { func (rs *RenderingService) renderViaHTTP(ctx context.Context, renderType RenderType, renderKey string, opts Opts) (*RenderResult, error) {
if renderType == RenderPDF { if renderType == RenderPDF {
if !rs.features.IsEnabled(ctx, featuremgmt.FlagNewPDFRendering) {
return nil, fmt.Errorf("feature 'newPDFRendering' disabled")
}
opts.Encoding = "pdf" opts.Encoding = "pdf"
} }

View File

@ -127,6 +127,7 @@ type Service interface {
RenderErrorImage(theme models.Theme, error error) (*RenderResult, error) RenderErrorImage(theme models.Theme, error error) (*RenderResult, error)
GetRenderUser(ctx context.Context, key string) (*RenderUser, bool) GetRenderUser(ctx context.Context, key string) (*RenderUser, bool)
HasCapability(ctx context.Context, capability CapabilityName) (CapabilitySupportRequestResult, error) HasCapability(ctx context.Context, capability CapabilityName) (CapabilitySupportRequestResult, error)
IsCapabilitySupported(ctx context.Context, capability CapabilityName) error
CreateRenderingSession(ctx context.Context, authOpts AuthOpts, sessionOpts SessionOpts) (Session, error) CreateRenderingSession(ctx context.Context, authOpts AuthOpts, sessionOpts SessionOpts) (Session, error)
SanitizeSVG(ctx context.Context, req *SanitizeSVGRequest) (*SanitizeSVGResponse, error) SanitizeSVG(ctx context.Context, req *SanitizeSVGRequest) (*SanitizeSVGResponse, error)
} }

View File

@ -80,6 +80,20 @@ func (mr *MockServiceMockRecorder) HasCapability(ctx, capability interface{}) *g
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasCapability", reflect.TypeOf((*MockService)(nil).HasCapability), ctx, capability) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasCapability", reflect.TypeOf((*MockService)(nil).HasCapability), ctx, capability)
} }
// IsCapabilitySupported mocks base method.
func (m *MockService) IsCapabilitySupported(ctx context.Context, capability CapabilityName) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "IsCapabilitySupported", ctx, capability)
ret0, _ := ret[0].(error)
return ret0
}
// IsCapabilitySupported indicates an expected call of IsCapabilitySupported.
func (mr *MockServiceMockRecorder) IsCapabilitySupported(ctx, capability interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsCapabilitySupported", reflect.TypeOf((*MockService)(nil).IsCapabilitySupported), ctx, capability)
}
// IsAvailable mocks base method. // IsAvailable mocks base method.
func (m *MockService) IsAvailable(ctx context.Context) bool { func (m *MockService) IsAvailable(ctx context.Context) bool {
m.ctrl.T.Helper() m.ctrl.T.Helper()

View File

@ -6,15 +6,10 @@ import (
"fmt" "fmt"
"github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2" "github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2"
"github.com/grafana/grafana/pkg/services/featuremgmt"
) )
func (rs *RenderingService) renderViaPlugin(ctx context.Context, renderType RenderType, renderKey string, opts Opts) (*RenderResult, error) { func (rs *RenderingService) renderViaPlugin(ctx context.Context, renderType RenderType, renderKey string, opts Opts) (*RenderResult, error) {
if renderType == RenderPDF { if renderType == RenderPDF {
if !rs.features.IsEnabled(ctx, featuremgmt.FlagNewPDFRendering) {
return nil, fmt.Errorf("feature 'newPDFRendering' disabled")
}
opts.Encoding = "pdf" opts.Encoding = "pdf"
} }

View File

@ -125,9 +125,13 @@ func ProvideService(cfg *setting.Cfg, features *featuremgmt.FeatureManager, remo
semverConstraint: ">= 3.4.0", semverConstraint: ">= 3.4.0",
}, },
{ {
name: SvgSanitization, name: SVGSanitization,
semverConstraint: ">= 3.5.0", semverConstraint: ">= 3.5.0",
}, },
{
name: PDFRendering,
semverConstraint: ">= 3.10.0",
},
}, },
Cfg: cfg, Cfg: cfg,
features: features, features: features,
@ -292,6 +296,16 @@ func (rs *RenderingService) render(ctx context.Context, renderType RenderType, o
return rs.renderUnavailableImage(), nil return rs.renderUnavailableImage(), nil
} }
if renderType == RenderPDF || opts.Encoding == "pdf" {
if !rs.features.IsEnabled(ctx, featuremgmt.FlagNewPDFRendering) {
return nil, fmt.Errorf("feature 'newPDFRendering' disabled")
}
if err := rs.IsCapabilitySupported(ctx, PDFRendering); err != nil {
return nil, err
}
}
rs.log.Info("Rendering", "path", opts.Path) rs.log.Info("Rendering", "path", opts.Path)
if math.IsInf(opts.DeviceScaleFactor, 0) || math.IsNaN(opts.DeviceScaleFactor) || opts.DeviceScaleFactor == 0 { if math.IsInf(opts.DeviceScaleFactor, 0) || math.IsNaN(opts.DeviceScaleFactor) || opts.DeviceScaleFactor == 0 {
opts.DeviceScaleFactor = 1 opts.DeviceScaleFactor = 1
@ -327,7 +341,7 @@ func (rs *RenderingService) RenderCSV(ctx context.Context, opts CSVOpts, session
} }
func (rs *RenderingService) SanitizeSVG(ctx context.Context, req *SanitizeSVGRequest) (*SanitizeSVGResponse, error) { func (rs *RenderingService) SanitizeSVG(ctx context.Context, req *SanitizeSVGRequest) (*SanitizeSVGResponse, error) {
capability, err := rs.HasCapability(ctx, SvgSanitization) capability, err := rs.HasCapability(ctx, SVGSanitization)
if err != nil { if err != nil {
return nil, err return nil, err
} }