[MM-63556] mmctl: Add compliance export download cmd (#30576)

* add mmctl compliance export download command and tests

- Introduced `ComplianceExportDownloadCmd` to facilitate downloading compliance export files.
- Implemented the `DownloadComplianceExport` method in the Client interface for handling file downloads.
- Added unit tests for the download command, covering successful downloads, error handling for non-existent jobs, and retries on failure.
- Included end-to-end tests to validate the command's functionality.
- Updated documentation to include usage examples and options for the new command.

* don't know why this was left out

* PR comments

* adjust test for new retry logic

* refactored download fn for compliance_export and export

* fix test due to fixed logic

* docs
Этот коммит содержится в:
Christopher Poile
2025-06-24 16:27:54 -04:00
коммит произвёл GitHub
родитель 60a747f975
Коммит b33a7e362f
12 изменённых файлов: 548 добавлений и 22 удалений

Просмотреть файл

@@ -1512,6 +1512,7 @@ func (c *Client4) GetUsersByIdsWithOptions(ctx context.Context, userIds []string
return nil, BuildResponse(r), err
}
defer closeBody(r)
var list []*User
if err := json.NewDecoder(r.Body).Decode(&list); err != nil {
return nil, nil, NewAppError("GetUsersByIdsWithOptions", "api.unmarshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
@@ -3207,7 +3208,7 @@ func (c *Client4) PatchChannel(ctx context.Context, channelId string, patch *Cha
var ch *Channel
err = json.NewDecoder(r.Body).Decode(&ch)
if err != nil {
return nil, BuildResponse(r), NewAppError("PatchChannel", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
return nil, BuildResponse(r), NewAppError("PatchChannel", "api.unmarshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
return ch, BuildResponse(r), nil
}
@@ -8927,6 +8928,31 @@ func (c *Client4) GetUserThreads(ctx context.Context, userId, teamId string, opt
return &threads, BuildResponse(r), nil
}
func (c *Client4) DownloadComplianceExport(ctx context.Context, jobId string, wr io.Writer) (string, error) {
r, err := c.DoAPIGet(ctx, c.jobsRoute()+fmt.Sprintf("/%s/download", jobId), "")
if err != nil {
return "", err
}
defer closeBody(r)
// Try to get the filename from the Content-Disposition header
var filename string
if cd := r.Header.Get("Content-Disposition"); cd != "" {
var params map[string]string
if _, params, err = mime.ParseMediaType(cd); err == nil {
if params["filename"] != "" {
filename = params["filename"]
}
}
}
_, err = io.Copy(wr, r.Body)
if err != nil {
return filename, NewAppError("DownloadComplianceExport", "model.client.copy.app_error", nil, "", r.StatusCode).Wrap(err)
}
return filename, nil
}
func (c *Client4) GetUserThread(ctx context.Context, userId, teamId, threadId string, extended bool) (*ThreadResponse, *Response, error) {
url := c.userThreadRoute(userId, teamId, threadId)
if extended {