Skip to content

Commit

Permalink
Add generation of unsigned URLs and add missing latest options
Browse files Browse the repository at this point in the history
  • Loading branch information
krasun committed Jan 15, 2025
1 parent fd681ed commit 9e211bb
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 1 deletion.
130 changes: 129 additions & 1 deletion client.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ func NewClient(accessKey, secretKey string) (*Client, error) {
return client, nil
}

// GenerateTakeURL generates URL for taking screenshots, but does not send any request.
// GenerateTakeURL generates URL for taking screenshots with request signing.
func (client *Client) GenerateTakeURL(options *TakeOptions) (*url.URL, error) {
if client.secretKey == "" {
return nil, fmt.Errorf("secret key is required for signed URLs")
}

// generate query
query := options.query
query.Set("access_key", client.accessKey)
Expand All @@ -54,6 +58,22 @@ func (client *Client) GenerateTakeURL(options *TakeOptions) (*url.URL, error) {
return u, nil
}

// GenerateUnsignedTakeURL generates URL for taking screenshots without signing the request.
func (client *Client) GenerateUnsignedTakeURL(options *TakeOptions) (*url.URL, error) {
// generate query
query := options.query
query.Set("access_key", client.accessKey)
queryString := query.Encode()

u, err := url.Parse(baseURL + takePath)
if err != nil {
return nil, fmt.Errorf("failed to parse URL \"%s\": %w", baseURL+takePath, err)
}
u.RawQuery = queryString

return u, nil
}

// Take takes screenshot and returns image or error if the request failed.
func (client *Client) Take(ctx context.Context, options *TakeOptions) ([]byte, *http.Response, error) {
u, err := client.GenerateTakeURL(options)
Expand Down Expand Up @@ -663,3 +683,111 @@ func (o *TakeOptions) FailIfContentContains(text string) *TakeOptions {

return o
}

// PDFPrintBackground sets whether to print background graphics in PDF.
func (o *TakeOptions) PDFPrintBackground(pdfPrintBackground bool) *TakeOptions {
o.query.Add("pdf_print_background", strconv.FormatBool(pdfPrintBackground))
return o
}

// PDFFitOnePage tries to fit the website on one page for PDF output.
func (o *TakeOptions) PDFFitOnePage(pdfFitOnePage bool) *TakeOptions {
o.query.Add("pdf_fit_one_page", strconv.FormatBool(pdfFitOnePage))
return o
}

// PDFLandscape sets PDF orientation to landscape.
func (o *TakeOptions) PDFLandscape(pdfLandscape bool) *TakeOptions {
o.query.Add("pdf_landscape", strconv.FormatBool(pdfLandscape))
return o
}

// PDFPaperFormat specifies the paper format for PDF output.
func (o *TakeOptions) PDFPaperFormat(format string) *TakeOptions {
o.query.Add("pdf_paper_format", format)
return o
}

// ClipX sets the x coordinate of the area to clip.
func (o *TakeOptions) ClipX(x int) *TakeOptions {
o.query.Add("clip_x", strconv.Itoa(x))
return o
}

// ClipY sets the y coordinate of the area to clip.
func (o *TakeOptions) ClipY(y int) *TakeOptions {
o.query.Add("clip_y", strconv.Itoa(y))
return o
}

// ClipWidth sets the width of the area to clip.
func (o *TakeOptions) ClipWidth(width int) *TakeOptions {
o.query.Add("clip_width", strconv.Itoa(width))
return o
}

// ClipHeight sets the height of the area to clip.
func (o *TakeOptions) ClipHeight(height int) *TakeOptions {
o.query.Add("clip_height", strconv.Itoa(height))
return o
}

// FullPageAlgorithm sets the algorithm for full page screenshots.
func (o *TakeOptions) FullPageAlgorithm(algorithm string) *TakeOptions {
o.query.Add("full_page_algorithm", algorithm)
return o
}

// SelectorScrollIntoView controls scrolling behavior when taking element screenshots.
func (o *TakeOptions) SelectorScrollIntoView(enable bool) *TakeOptions {
o.query.Add("selector_scroll_into_view", strconv.FormatBool(enable))
return o
}

// IgnoreHostErrors allows taking screenshots even when site returns error status codes.
func (o *TakeOptions) IgnoreHostErrors(ignore bool) *TakeOptions {
o.query.Add("ignore_host_errors", strconv.FormatBool(ignore))
return o
}

// ErrorOnClickSelectorNotFound controls error behavior when click selector is not found.
func (o *TakeOptions) ErrorOnClickSelectorNotFound(errorOn bool) *TakeOptions {
o.query.Add("error_on_click_selector_not_found", strconv.FormatBool(errorOn))
return o
}

// StorageEndpoint sets custom S3-compatible storage endpoint.
func (o *TakeOptions) StorageEndpoint(endpoint string) *TakeOptions {
o.query.Add("storage_endpoint", endpoint)
return o
}

// StorageAccessKeyID sets storage access key ID.
func (o *TakeOptions) StorageAccessKeyID(keyID string) *TakeOptions {
o.query.Add("storage_access_key_id", keyID)
return o
}

// StorageSecretAccessKey sets storage secret access key.
func (o *TakeOptions) StorageSecretAccessKey(key string) *TakeOptions {
o.query.Add("storage_secret_access_key", key)
return o
}

// StorageBucket sets storage bucket name.
func (o *TakeOptions) StorageBucket(bucket string) *TakeOptions {
o.query.Add("storage_bucket", bucket)
return o
}

// StorageClass sets storage class for the object.
func (o *TakeOptions) StorageClass(class string) *TakeOptions {
o.query.Add("storage_class", class)
return o
}

// MetadataIcon enables returning the favicon metadata.
func (o *TakeOptions) MetadataIcon(enable bool) *TakeOptions {
o.query.Add("metadata_icon", strconv.FormatBool(enable))
return o
}
53 changes: 53 additions & 0 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,38 @@ func TestTakeURLGeneratesURL(t *testing.T) {
screenshots.NewTakeWithHTML("<h1>Hello, world!</h1>"),
"https://api.screenshotone.com/take?access_key=IVmt2ghj9TG_jQ&html=%3Ch1%3EHello%2C+world%21%3C%2Fh1%3E&signature=2e9559eaeb5ff8a6b0aa85ddeaaf2e65d8e7cf636741964488784864327e3901",
},
{
screenshots.NewTakeOptions("https://example.com").
PDFPrintBackground(true).
PDFFitOnePage(true).
PDFLandscape(true).
PDFPaperFormat("a4"),
"https://api.screenshotone.com/take?access_key=IVmt2ghj9TG_jQ&pdf_fit_one_page=true&pdf_landscape=true&pdf_paper_format=a4&pdf_print_background=true&url=https%3A%2F%2Fexample.com&signature=2be4758936d2392a1776fdac4e0bb6dc4ad3aab1a380bfdb2c34eae6899ca556",
},
{
screenshots.NewTakeOptions("https://example.com").
ClipX(100).
ClipY(200).
ClipWidth(300).
ClipHeight(400),
"https://api.screenshotone.com/take?access_key=IVmt2ghj9TG_jQ&clip_height=400&clip_width=300&clip_x=100&clip_y=200&url=https%3A%2F%2Fexample.com&signature=8c6815f9c6123177a65826b51f14aa774bd54e075a3e04156a0bee2e9b2650c7",
},
{
screenshots.NewTakeOptions("https://example.com").
FullPageAlgorithm("by_sections").
SelectorScrollIntoView(true).
IgnoreHostErrors(true),
"https://api.screenshotone.com/take?access_key=IVmt2ghj9TG_jQ&full_page_algorithm=by_sections&ignore_host_errors=true&selector_scroll_into_view=true&url=https%3A%2F%2Fexample.com&signature=8c7dd91d28a8289d75affde1aff70e6a73afa594a0557b55e1176fcabc321e26",
},
{
screenshots.NewTakeOptions("https://example.com").
StorageEndpoint("https://storage.example.com").
StorageAccessKeyID("access123").
StorageSecretAccessKey("secret456").
StorageBucket("mybucket").
StorageClass("standard"),
"https://api.screenshotone.com/take?access_key=IVmt2ghj9TG_jQ&storage_access_key_id=access123&storage_bucket=mybucket&storage_class=standard&storage_endpoint=https%3A%2F%2Fstorage.example.com&storage_secret_access_key=secret456&url=https%3A%2F%2Fexample.com&signature=0b27223cf5ec9f47f43d902603e9b9578ca850fca9d5638e944eaef67c51d9d2",
},
}

client, err := screenshots.NewClient("IVmt2ghj9TG_jQ", "Sxt94yAj9aQSgg")
Expand All @@ -86,6 +118,27 @@ func TestTakeURLGeneratesURL(t *testing.T) {
}
}

func TestGenerateUnsignedTakeURL(t *testing.T) {
client, err := screenshots.NewClient("test-key", "")
ok(t, err)

options := screenshots.NewTakeOptions("https://example.com")
u, err := client.GenerateUnsignedTakeURL(options)
ok(t, err)

expected := "https://api.screenshotone.com/take?access_key=test-key&url=https%3A%2F%2Fexample.com"
equals(t, expected, u.String())
}

func TestGenerateTakeURLRequiresSecretKey(t *testing.T) {
client, err := screenshots.NewClient("test-key", "")
ok(t, err)

options := screenshots.NewTakeOptions("https://example.com")
_, err = client.GenerateTakeURL(options)
errorred(t, err, "secret key is required")
}

// errorred fails the test if an err is nil or message is not found in the message string.
func errorred(tb testing.TB, err error, message string) {
if err == nil {
Expand Down

0 comments on commit 9e211bb

Please sign in to comment.