// Package officeconvertclient provides typed Connect RPC helpers for conversions. package officeconvertclient import ( "context" "errors" "net/http" "time" "connectrpc.com/connect" officeconvertapiv1 "github.com/end/officeconvert/gen/go/officeconvertapi/v1" "github.com/end/officeconvert/gen/go/officeconvertapi/v1/officeconvertapiv1connect" ) const defaultPollInterval = 2 * time.Second // 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, ) (*officeconvertapiv1.CreateConversionResponse, error) { req := connect.NewRequest(&officeconvertapiv1.CreateConversionRequest{ SourceFilename: sourceFilename, }) res, err := c.rpc.CreateConversion(ctx, req) if err != nil { return nil, err } return res.Msg, nil } // StartConversion signals that upload is complete and conversion can begin. func (c *Client) StartConversion( ctx context.Context, conversionID string, ) (*officeconvertapiv1.StartConversionResponse, error) { req := connect.NewRequest(&officeconvertapiv1.StartConversionRequest{ ConversionId: conversionID, }) res, err := c.rpc.StartConversion(ctx, req) if err != nil { return nil, err } return res.Msg, nil } // GetConversionStatus returns the latest status for a conversion session. func (c *Client) GetConversionStatus( ctx context.Context, conversionID string, ) (*officeconvertapiv1.GetConversionStatusResponse, error) { req := connect.NewRequest(&officeconvertapiv1.GetConversionStatusRequest{ ConversionId: conversionID, }) res, err := c.rpc.GetConversionStatus(ctx, req) if err != nil { return nil, err } return res.Msg, nil } // GetSlideDeck retrieves the final converted deck response. func (c *Client) GetSlideDeck( ctx context.Context, conversionID string, ) (*officeconvertapiv1.GetSlideDeckResponse, error) { req := connect.NewRequest(&officeconvertapiv1.GetSlideDeckRequest{ ConversionId: conversionID, }) 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, conversionID string, ) (*officeconvertapiv1.DeleteConversionResponse, error) { req := connect.NewRequest(&officeconvertapiv1.DeleteConversionRequest{ ConversionId: conversionID, }) res, err := c.rpc.DeleteConversion(ctx, req) if err != nil { return nil, err } return res.Msg, nil } // WaitForCompletion polls status until terminal completion or context cancellation. func (c *Client) WaitForCompletion( ctx context.Context, conversionID string, ) (*officeconvertapiv1.GetConversionStatusResponse, error) { ticker := time.NewTicker(c.pollInterval) defer ticker.Stop() for { status, err := c.GetConversionStatus(ctx, conversionID) 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: } } }