Files
officeconvert/clients/go/officeconvertclient/client.go
T
end 30cbfaadad
Docker server image / build-and-push (push) Successful in 3m48s
add detailed jpg quality opts & thumbnail pass
2026-03-30 05:05:27 -07:00

186 lines
5.0 KiB
Go

// Package officeconvertclient provides typed Connect RPC helpers for conversions.
package officeconvertclient
import (
"context"
"errors"
"net/http"
"time"
"connectrpc.com/connect"
officeconvertapiv1 "gitea.auvem.com/end-internal/officeconvert/gen/go/officeconvertapi/v1"
"gitea.auvem.com/end-internal/officeconvert/gen/go/officeconvertapi/v1/officeconvertapiv1connect"
"github.com/segmentio/ksuid"
)
const defaultPollInterval = 2 * time.Second
// RasterTierOptions defines per-tier raster settings for conversion output.
type RasterTierOptions struct {
Resolution officeconvertapiv1.ConversionResolution
JPEGQuality int32
}
// CreateConversionOptions configures optional full and thumbnail raster tiers.
type CreateConversionOptions struct {
Full *RasterTierOptions
Thumbnail *RasterTierOptions
}
// Client wraps the generated Connect client with orchestration-focused helpers.
type Client struct {
rpc officeconvertapiv1connect.ConversionServiceClient
httpClient *http.Client
baseURL string
pollInterval time.Duration
}
// NewClient creates a new typed Officeconvert client.
func NewClient(baseURL string, httpClient *http.Client, options ...connect.ClientOption) *Client {
if httpClient == nil {
httpClient = http.DefaultClient
}
return &Client{
rpc: officeconvertapiv1connect.NewConversionServiceClient(httpClient, baseURL, options...),
httpClient: httpClient,
baseURL: baseURL,
pollInterval: defaultPollInterval,
}
}
// SetPollInterval configures the polling cadence used by WaitForCompletion.
func (c *Client) SetPollInterval(interval time.Duration) {
if interval > 0 {
c.pollInterval = interval
}
}
// CreateConversion starts a conversion session and returns upload metadata.
func (c *Client) CreateConversion(
ctx context.Context,
sourceFilename string,
options *CreateConversionOptions,
) (*CreateConversionResponse, error) {
message := &officeconvertapiv1.CreateConversionRequest{
SourceFilename: sourceFilename,
}
if options != nil {
message.Full = toProtoSlideRasterOptions(options.Full)
message.Thumbnail = toProtoSlideRasterOptions(options.Thumbnail)
}
req := connect.NewRequest(message)
res, err := c.rpc.CreateConversion(ctx, req)
if err != nil {
return nil, err
}
return augmentCreateConversionResponse(res.Msg)
}
func toProtoSlideRasterOptions(
options *RasterTierOptions,
) *officeconvertapiv1.SlideRasterOptions {
if options == nil {
return nil
}
proto := &officeconvertapiv1.SlideRasterOptions{
Resolution: options.Resolution,
}
if options.JPEGQuality != 0 {
proto.Format = &officeconvertapiv1.SlideRasterOptions_Jpeg{
Jpeg: &officeconvertapiv1.JpegOutputOptions{
Quality: options.JPEGQuality,
},
}
}
return proto
}
// StartConversion signals that upload is complete and conversion can begin.
func (c *Client) StartConversion(
ctx context.Context,
id ksuid.KSUID,
) (*StartConversionResponse, error) {
req := connect.NewRequest(&officeconvertapiv1.StartConversionRequest{
ConversionId: id.String(),
})
res, err := c.rpc.StartConversion(ctx, req)
if err != nil {
return nil, err
}
return augmentStartConversionResponse(res.Msg)
}
// GetConversionStatus returns the latest status for a conversion session.
func (c *Client) GetConversionStatus(
ctx context.Context,
id ksuid.KSUID,
) (*GetConversionStatusResponse, error) {
req := connect.NewRequest(&officeconvertapiv1.GetConversionStatusRequest{
ConversionId: id.String(),
})
res, err := c.rpc.GetConversionStatus(ctx, req)
if err != nil {
return nil, err
}
return augmentGetConversionStatusResponse(res.Msg)
}
// GetSlideDeck retrieves the final converted deck response.
func (c *Client) GetSlideDeck(
ctx context.Context,
id ksuid.KSUID,
) (*officeconvertapiv1.GetSlideDeckResponse, error) {
req := connect.NewRequest(&officeconvertapiv1.GetSlideDeckRequest{
ConversionId: id.String(),
})
res, err := c.rpc.GetSlideDeck(ctx, req)
if err != nil {
return nil, err
}
return res.Msg, nil
}
// DeleteConversion triggers immediate resource cleanup for a session.
func (c *Client) DeleteConversion(
ctx context.Context,
id ksuid.KSUID,
) (*DeleteConversionResponse, error) {
req := connect.NewRequest(&officeconvertapiv1.DeleteConversionRequest{
ConversionId: id.String(),
})
res, err := c.rpc.DeleteConversion(ctx, req)
if err != nil {
return nil, err
}
return augmentDeleteConversionResponse(res.Msg)
}
// WaitForCompletion polls status until terminal completion or context cancellation.
func (c *Client) WaitForCompletion(
ctx context.Context,
id ksuid.KSUID,
) (*GetConversionStatusResponse, error) {
ticker := time.NewTicker(c.pollInterval)
defer ticker.Stop()
for {
status, err := c.GetConversionStatus(ctx, id)
if err != nil {
return nil, err
}
switch status.Status {
case officeconvertapiv1.ConversionStatus_CONVERSION_STATUS_SUCCEEDED:
return status, nil
case officeconvertapiv1.ConversionStatus_CONVERSION_STATUS_FAILED:
return nil, errors.New(status.ErrorMessage)
}
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-ticker.C:
}
}
}