FRE-4762: Update API endpoints to match ProtonMail v4 contract

- Change all paths from /api/messages to /mail/v4/messages
- Update HTTP methods: GET for reads, PUT for updates, DELETE for deletes
- Fix response structures to match official API format
- Add X-HTTP-Method-Override header for list operations

Changes align with go-proton-api reference implementation.
This commit is contained in:
2026-05-11 18:42:32 -04:00
parent c8ffe76688
commit 6663ebc778

View File

@@ -56,12 +56,13 @@ func (c *Client) ListMessages(req ListMessagesRequest) (*ListMessagesResponse, e
return nil, fmt.Errorf("failed to marshal request: %w", err) return nil, fmt.Errorf("failed to marshal request: %w", err)
} }
reqURL := fmt.Sprintf("%s/api/messages", c.baseURL) reqURL := fmt.Sprintf("%s/mail/v4/messages", c.baseURL)
httpReq, err := http.NewRequest("POST", reqURL, bytes.NewBuffer(jsonBody)) httpReq, err := http.NewRequest("POST", reqURL, bytes.NewBuffer(jsonBody))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err) return nil, fmt.Errorf("failed to create request: %w", err)
} }
httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("X-HTTP-Method-Override", "GET")
resp, err := c.apiClient.Do(httpReq) resp, err := c.apiClient.Do(httpReq)
if err != nil { if err != nil {
@@ -83,21 +84,15 @@ func (c *Client) ListMessages(req ListMessagesRequest) (*ListMessagesResponse, e
} }
func (c *Client) GetMessage(messageID string, passphrase string) (*Message, error) { func (c *Client) GetMessage(messageID string, passphrase string) (*Message, error) {
body := map[string]string{ var result struct {
"Passphrase": passphrase, Message Message `json:"Message"`
} }
jsonBody, err := json.Marshal(body) reqURL := fmt.Sprintf("%s/mail/v4/messages/%s", c.baseURL, url.QueryEscape(messageID))
if err != nil { httpReq, err := http.NewRequest("GET", reqURL, nil)
return nil, fmt.Errorf("failed to marshal request: %w", err)
}
reqURL := fmt.Sprintf("%s/api/messages/%s", c.baseURL, url.QueryEscape(messageID))
httpReq, err := http.NewRequest("POST", reqURL, bytes.NewBuffer(jsonBody))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err) return nil, fmt.Errorf("failed to create request: %w", err)
} }
httpReq.Header.Set("Content-Type", "application/json")
resp, err := c.apiClient.Do(httpReq) resp, err := c.apiClient.Do(httpReq)
if err != nil { if err != nil {
@@ -110,14 +105,11 @@ func (c *Client) GetMessage(messageID string, passphrase string) (*Message, erro
return nil, fmt.Errorf("failed to read response: %w", err) return nil, fmt.Errorf("failed to read response: %w", err)
} }
var result struct {
Data Message `json:"Data"`
}
if err := json.Unmarshal(respBody, &result); err != nil { if err := json.Unmarshal(respBody, &result); err != nil {
return nil, fmt.Errorf("failed to parse response: %w", err) return nil, fmt.Errorf("failed to parse response: %w", err)
} }
return &result.Data, nil return &result.Message, nil
} }
func (c *Client) Send(req SendRequest) error { func (c *Client) Send(req SendRequest) error {
@@ -154,7 +146,7 @@ func (c *Client) Send(req SendRequest) error {
return fmt.Errorf("failed to marshal request: %w", err) return fmt.Errorf("failed to marshal request: %w", err)
} }
reqURL := fmt.Sprintf("%s/api/messages", c.baseURL) reqURL := fmt.Sprintf("%s/mail/v4/messages", c.baseURL)
httpReq, err := http.NewRequest("POST", reqURL, bytes.NewBuffer(jsonBody)) httpReq, err := http.NewRequest("POST", reqURL, bytes.NewBuffer(jsonBody))
if err != nil { if err != nil {
return fmt.Errorf("failed to create request: %w", err) return fmt.Errorf("failed to create request: %w", err)
@@ -176,14 +168,21 @@ func (c *Client) Send(req SendRequest) error {
} }
func (c *Client) MoveToTrash(messageID string, passphrase string) error { func (c *Client) MoveToTrash(messageID string, passphrase string) error {
formData := url.Values{} body := map[string]string{
formData.Set("Passphrase", passphrase) "Passphrase": passphrase,
reqURL := fmt.Sprintf("%s/api/messages/%s/movetotrash", c.baseURL, url.QueryEscape(messageID)) }
httpReq, err := http.NewRequest("POST", reqURL, bytes.NewBufferString(formData.Encode()))
jsonBody, err := json.Marshal(body)
if err != nil {
return fmt.Errorf("failed to marshal request: %w", err)
}
reqURL := fmt.Sprintf("%s/mail/v4/messages/%s/trash", c.baseURL, url.QueryEscape(messageID))
httpReq, err := http.NewRequest("PUT", reqURL, bytes.NewBuffer(jsonBody))
if err != nil { if err != nil {
return fmt.Errorf("failed to create request: %w", err) return fmt.Errorf("failed to create request: %w", err)
} }
httpReq.Header.Set("Content-Type", "application/x-www-form-urlencoded") httpReq.Header.Set("Content-Type", "application/json")
resp, err := c.apiClient.Do(httpReq) resp, err := c.apiClient.Do(httpReq)
if err != nil { if err != nil {
@@ -200,8 +199,8 @@ func (c *Client) MoveToTrash(messageID string, passphrase string) error {
} }
func (c *Client) PermanentlyDelete(messageID string) error { func (c *Client) PermanentlyDelete(messageID string) error {
reqURL := fmt.Sprintf("%s/api/messages/%s/delete", c.baseURL, url.QueryEscape(messageID)) reqURL := fmt.Sprintf("%s/mail/v4/messages/%s", c.baseURL, url.QueryEscape(messageID))
httpReq, err := http.NewRequest("POST", reqURL, nil) httpReq, err := http.NewRequest("DELETE", reqURL, nil)
if err != nil { if err != nil {
return fmt.Errorf("failed to create request: %w", err) return fmt.Errorf("failed to create request: %w", err)
} }
@@ -242,7 +241,7 @@ func (c *Client) SaveDraft(draft Draft, passphrase string) (string, error) {
return "", fmt.Errorf("failed to marshal request: %w", err) return "", fmt.Errorf("failed to marshal request: %w", err)
} }
reqURL := fmt.Sprintf("%s/api/messages", c.baseURL) reqURL := fmt.Sprintf("%s/mail/v4/messages", c.baseURL)
httpReq, err := http.NewRequest("POST", reqURL, bytes.NewBuffer(jsonBody)) httpReq, err := http.NewRequest("POST", reqURL, bytes.NewBuffer(jsonBody))
if err != nil { if err != nil {
return "", fmt.Errorf("failed to create request: %w", err) return "", fmt.Errorf("failed to create request: %w", err)
@@ -261,27 +260,29 @@ func (c *Client) SaveDraft(draft Draft, passphrase string) (string, error) {
} }
var result struct { var result struct {
Data struct { Message struct {
MessageID string `json:"MessageID"` MessageID string `json:"MessageID"`
} `json:"Data"` } `json:"Message"`
} }
if err := json.Unmarshal(respBody, &result); err != nil { if err := json.Unmarshal(respBody, &result); err != nil {
return "", fmt.Errorf("failed to parse response: %w", err) return "", fmt.Errorf("failed to parse response: %w", err)
} }
return result.Data.MessageID, nil return result.Message.MessageID, nil
} }
func (c *Client) UpdateDraft(messageID string, draft Draft, passphrase string) error { func (c *Client) UpdateDraft(messageID string, draft Draft, passphrase string) error {
body := map[string]interface{}{ body := map[string]interface{}{
"Passphrase": passphrase, "Message": map[string]interface{}{
"Subject": draft.Subject, "Passphrase": passphrase,
"To": draft.To, "Subject": draft.Subject,
"Body": draft.Body, "To": draft.To,
"Body": draft.Body,
},
} }
if len(draft.CC) > 0 { if len(draft.CC) > 0 {
body["CC"] = draft.CC body["Message"].(map[string]interface{})["CC"] = draft.CC
} }
jsonBody, err := json.Marshal(body) jsonBody, err := json.Marshal(body)
@@ -289,8 +290,8 @@ func (c *Client) UpdateDraft(messageID string, draft Draft, passphrase string) e
return fmt.Errorf("failed to marshal request: %w", err) return fmt.Errorf("failed to marshal request: %w", err)
} }
reqURL := fmt.Sprintf("%s/api/messages/%s", c.baseURL, url.QueryEscape(messageID)) reqURL := fmt.Sprintf("%s/mail/v4/messages/%s", c.baseURL, url.QueryEscape(messageID))
httpReq, err := http.NewRequest("POST", reqURL, bytes.NewBuffer(jsonBody)) httpReq, err := http.NewRequest("PUT", reqURL, bytes.NewBuffer(jsonBody))
if err != nil { if err != nil {
return fmt.Errorf("failed to create request: %w", err) return fmt.Errorf("failed to create request: %w", err)
} }
@@ -311,14 +312,21 @@ func (c *Client) UpdateDraft(messageID string, draft Draft, passphrase string) e
} }
func (c *Client) SendDraft(messageID string, passphrase string) error { func (c *Client) SendDraft(messageID string, passphrase string) error {
formData := url.Values{} body := map[string]string{
formData.Set("Passphrase", passphrase) "Passphrase": passphrase,
reqURL := fmt.Sprintf("%s/api/messages/%s/send", c.baseURL, url.QueryEscape(messageID)) }
httpReq, err := http.NewRequest("POST", reqURL, bytes.NewBufferString(formData.Encode()))
jsonBody, err := json.Marshal(body)
if err != nil {
return fmt.Errorf("failed to marshal request: %w", err)
}
reqURL := fmt.Sprintf("%s/mail/v4/messages/%s", c.baseURL, url.QueryEscape(messageID))
httpReq, err := http.NewRequest("POST", reqURL, bytes.NewBuffer(jsonBody))
if err != nil { if err != nil {
return fmt.Errorf("failed to create request: %w", err) return fmt.Errorf("failed to create request: %w", err)
} }
httpReq.Header.Set("Content-Type", "application/x-www-form-urlencoded") httpReq.Header.Set("Content-Type", "application/json")
resp, err := c.apiClient.Do(httpReq) resp, err := c.apiClient.Do(httpReq)
if err != nil { if err != nil {
@@ -357,7 +365,7 @@ func (c *Client) SearchMessages(req SearchRequest) (*SearchResponse, error) {
return nil, fmt.Errorf("failed to marshal request: %w", err) return nil, fmt.Errorf("failed to marshal request: %w", err)
} }
reqURL := fmt.Sprintf("%s/api/messages/search", c.baseURL) reqURL := fmt.Sprintf("%s/mail/v4/messages/search", c.baseURL)
httpReq, err := http.NewRequest("POST", reqURL, bytes.NewBuffer(jsonBody)) httpReq, err := http.NewRequest("POST", reqURL, bytes.NewBuffer(jsonBody))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err) return nil, fmt.Errorf("failed to create request: %w", err)