Verified Commit f086cdb4 authored by Andreas Kunze's avatar Andreas Kunze Committed by GitLab
Browse files

feat(jobs): Support job artifacts file_type attribute

Changelog: Improvements
parent 3262069c
Loading
Loading
Loading
Loading
+22 −1
Original line number Diff line number Diff line
@@ -75,8 +75,15 @@ type (
		// GetJobArtifacts gets jobs artifacts of a project
		//
		// GitLab API docs:
		// https://docs.gitlab.com/api/job_artifacts/#get-job-artifacts
		// https://docs.gitlab.com/api/job_artifacts/#download-job-artifacts-by-job-id
		GetJobArtifacts(pid any, jobID int64, options ...RequestOptionFunc) (*bytes.Reader, *Response, error)
		// GetJobArtifactsWithOptions gets jobs artifacts of a project with options.
		//
		// Deprecated: GetJobArtifactsWithOptions will be removed in version 4.0, and the `GetJobArtifactsOptions` will be moved into the normal `GetJobArtifacts`.
		//
		// GitLab API docs:
		// https://docs.gitlab.com/api/job_artifacts/#download-job-artifacts-by-job-id
		GetJobArtifactsWithOptions(pid any, jobID int64, opt *GetJobArtifactsOptions, options ...RequestOptionFunc) (*bytes.Reader, *Response, error)
		// DownloadArtifactsFile downloads the artifacts file from the given
		// reference name and job provided the job finished successfully.
		//
@@ -319,9 +326,23 @@ func (s *JobsService) GetJob(pid any, jobID int64, options ...RequestOptionFunc)
	)
}

// GetJobArtifactsOptions represents the available GetJobArtifacts() options.
//
// GitLab API docs:
// https://docs.gitlab.com/api/job_artifacts/#get-job-artifacts
type GetJobArtifactsOptions struct {
	FileType *ArtifactFileTypeValue `url:"file_type,omitempty" json:"file_type,omitempty"`
}

func (s *JobsService) GetJobArtifacts(pid any, jobID int64, options ...RequestOptionFunc) (*bytes.Reader, *Response, error) {
	return s.GetJobArtifactsWithOptions(pid, jobID, nil, options...)
}

// Deprecated: use GetJobArtifacts instead unless you need "opt", which will be merged into GetJobArtifacts in version 4.0.
func (s *JobsService) GetJobArtifactsWithOptions(pid any, jobID int64, opt *GetJobArtifactsOptions, options ...RequestOptionFunc) (*bytes.Reader, *Response, error) {
	b, resp, err := do[bytes.Buffer](s.client,
		withPath(routeProjectsIDJobsIDArtifacts, ProjectID{pid}, jobID),
		withAPIOpts(opt),
		withRequestOpts(options...),
	)

+79 −0
Original line number Diff line number Diff line
@@ -174,6 +174,85 @@ func TestJobsService_ListProjectJobs(t *testing.T) {
	assert.Equal(t, want, jobs)
}

func TestGetJobArtifacts(t *testing.T) {
	t.Parallel()
	mux, client := setup(t)

	wantContent := []byte("This is the artifacts archive")

	// GIVEN a job artifacts endpoint
	mux.HandleFunc("/api/v4/projects/1/jobs/2/artifacts", func(w http.ResponseWriter, r *http.Request) {
		// WHEN the artifacts are requested without options
		testMethod(t, r, http.MethodGet)
		// THEN no file_type parameter is sent, so the server default applies
		assert.False(t, r.URL.Query().Has("file_type"))
		w.WriteHeader(http.StatusOK)
		fmt.Fprint(w, `This is the artifacts archive`)
	})

	reader, resp, err := client.Jobs.GetJobArtifacts(1, 2)
	assert.NoError(t, err)

	// THEN the archive content is returned
	content, err := io.ReadAll(reader)
	assert.NoError(t, err)
	assert.Equal(t, wantContent, content)
	assert.Equal(t, http.StatusOK, resp.StatusCode)
}

func TestGetJobArtifactsWithOptions(t *testing.T) {
	t.Parallel()
	mux, client := setup(t)

	wantContent := []byte(`<testsuites/>`)

	// GIVEN a job artifacts endpoint serving a single report file
	mux.HandleFunc("/api/v4/projects/1/jobs/2/artifacts", func(w http.ResponseWriter, r *http.Request) {
		// WHEN the artifacts are requested with a file type
		testMethod(t, r, http.MethodGet)
		// THEN the file_type parameter is sent
		testParam(t, r, "file_type", "junit")
		w.WriteHeader(http.StatusOK)
		fmt.Fprint(w, `<testsuites/>`)
	})

	opt := &GetJobArtifactsOptions{FileType: new(ArtifactFileTypeJUnit)}
	reader, resp, err := client.Jobs.GetJobArtifactsWithOptions(1, 2, opt)
	assert.NoError(t, err)

	// THEN the report content is returned
	content, err := io.ReadAll(reader)
	assert.NoError(t, err)
	assert.Equal(t, wantContent, content)
	assert.Equal(t, http.StatusOK, resp.StatusCode)
}

func TestGetJobArtifactsWithOptions_NilOptions(t *testing.T) {
	t.Parallel()
	mux, client := setup(t)

	wantContent := []byte("This is the artifacts archive")

	// GIVEN a job artifacts endpoint
	mux.HandleFunc("/api/v4/projects/1/jobs/2/artifacts", func(w http.ResponseWriter, r *http.Request) {
		// WHEN the artifacts are requested with nil options
		testMethod(t, r, http.MethodGet)
		// THEN no file_type parameter is sent
		assert.False(t, r.URL.Query().Has("file_type"))
		w.WriteHeader(http.StatusOK)
		fmt.Fprint(w, `This is the artifacts archive`)
	})

	reader, resp, err := client.Jobs.GetJobArtifactsWithOptions(1, 2, nil)
	assert.NoError(t, err)

	// THEN the archive content is returned
	content, err := io.ReadAll(reader)
	assert.NoError(t, err)
	assert.Equal(t, wantContent, content)
	assert.Equal(t, http.StatusOK, resp.StatusCode)
}

func TestDownloadArtifactsFile(t *testing.T) {
	t.Parallel()
	mux, client := setup(t)
+45 −0
Original line number Diff line number Diff line
@@ -488,6 +488,51 @@ func (c *MockJobsServiceInterfaceGetJobArtifactsCall) DoAndReturn(f func(any, in
	return c
}

// GetJobArtifactsWithOptions mocks base method.
func (m *MockJobsServiceInterface) GetJobArtifactsWithOptions(pid any, jobID int64, opt *gitlab.GetJobArtifactsOptions, options ...gitlab.RequestOptionFunc) (*bytes.Reader, *gitlab.Response, error) {
	m.ctrl.T.Helper()
	varargs := []any{pid, jobID, opt}
	for _, a := range options {
		varargs = append(varargs, a)
	}
	ret := m.ctrl.Call(m, "GetJobArtifactsWithOptions", varargs...)
	ret0, _ := ret[0].(*bytes.Reader)
	ret1, _ := ret[1].(*gitlab.Response)
	ret2, _ := ret[2].(error)
	return ret0, ret1, ret2
}

// GetJobArtifactsWithOptions indicates an expected call of GetJobArtifactsWithOptions.
func (mr *MockJobsServiceInterfaceMockRecorder) GetJobArtifactsWithOptions(pid, jobID, opt any, options ...any) *MockJobsServiceInterfaceGetJobArtifactsWithOptionsCall {
	mr.mock.ctrl.T.Helper()
	varargs := append([]any{pid, jobID, opt}, options...)
	call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetJobArtifactsWithOptions", reflect.TypeOf((*MockJobsServiceInterface)(nil).GetJobArtifactsWithOptions), varargs...)
	return &MockJobsServiceInterfaceGetJobArtifactsWithOptionsCall{Call: call}
}

// MockJobsServiceInterfaceGetJobArtifactsWithOptionsCall wrap *gomock.Call
type MockJobsServiceInterfaceGetJobArtifactsWithOptionsCall struct {
	*gomock.Call
}

// Return rewrite *gomock.Call.Return
func (c *MockJobsServiceInterfaceGetJobArtifactsWithOptionsCall) Return(arg0 *bytes.Reader, arg1 *gitlab.Response, arg2 error) *MockJobsServiceInterfaceGetJobArtifactsWithOptionsCall {
	c.Call = c.Call.Return(arg0, arg1, arg2)
	return c
}

// Do rewrite *gomock.Call.Do
func (c *MockJobsServiceInterfaceGetJobArtifactsWithOptionsCall) Do(f func(any, int64, *gitlab.GetJobArtifactsOptions, ...gitlab.RequestOptionFunc) (*bytes.Reader, *gitlab.Response, error)) *MockJobsServiceInterfaceGetJobArtifactsWithOptionsCall {
	c.Call = c.Call.Do(f)
	return c
}

// DoAndReturn rewrite *gomock.Call.DoAndReturn
func (c *MockJobsServiceInterfaceGetJobArtifactsWithOptionsCall) DoAndReturn(f func(any, int64, *gitlab.GetJobArtifactsOptions, ...gitlab.RequestOptionFunc) (*bytes.Reader, *gitlab.Response, error)) *MockJobsServiceInterfaceGetJobArtifactsWithOptionsCall {
	c.Call = c.Call.DoAndReturn(f)
	return c
}

// GetJobTokensJob mocks base method.
func (m *MockJobsServiceInterface) GetJobTokensJob(opts *gitlab.GetJobTokensJobOptions, options ...gitlab.RequestOptionFunc) (*gitlab.Job, *gitlab.Response, error) {
	m.ctrl.T.Helper()
+34 −0
Original line number Diff line number Diff line
@@ -306,6 +306,40 @@ const (
	Scheduled          BuildStateValue = "scheduled"
)

// ArtifactFileTypeValue represents a job artifact type.
//
// GitLab API docs:
// https://docs.gitlab.com/api/job_artifacts/#get-job-artifacts
type ArtifactFileTypeValue string

// List of downloadable artifact file types.
const (
	ArtifactFileTypeAccessibility        ArtifactFileTypeValue = "accessibility"
	ArtifactFileTypeAPIFuzzing           ArtifactFileTypeValue = "api_fuzzing"
	ArtifactFileTypeArchive              ArtifactFileTypeValue = "archive"
	ArtifactFileTypeBrowserPerformance   ArtifactFileTypeValue = "browser_performance"
	ArtifactFileTypeClusterImageScanning ArtifactFileTypeValue = "cluster_image_scanning"
	ArtifactFileTypeCobertura            ArtifactFileTypeValue = "cobertura"
	ArtifactFileTypeCodequality          ArtifactFileTypeValue = "codequality"
	ArtifactFileTypeContainerScanning    ArtifactFileTypeValue = "container_scanning"
	ArtifactFileTypeCycloneDX            ArtifactFileTypeValue = "cyclonedx"
	ArtifactFileTypeDAST                 ArtifactFileTypeValue = "dast"
	ArtifactFileTypeDependencyScanning   ArtifactFileTypeValue = "dependency_scanning"
	ArtifactFileTypeDotenv               ArtifactFileTypeValue = "dotenv"
	ArtifactFileTypeJacoco               ArtifactFileTypeValue = "jacoco"
	ArtifactFileTypeJUnit                ArtifactFileTypeValue = "junit"
	ArtifactFileTypeLicenseScanning      ArtifactFileTypeValue = "license_scanning"
	ArtifactFileTypeLoadPerformance      ArtifactFileTypeValue = "load_performance"
	ArtifactFileTypeLSIF                 ArtifactFileTypeValue = "lsif"
	ArtifactFileTypeMetrics              ArtifactFileTypeValue = "metrics"
	ArtifactFileTypePerformance          ArtifactFileTypeValue = "performance"
	ArtifactFileTypeRequirements         ArtifactFileTypeValue = "requirements"
	ArtifactFileTypeRequirementsV2       ArtifactFileTypeValue = "requirements_v2"
	ArtifactFileTypeSARIF                ArtifactFileTypeValue = "sarif"
	ArtifactFileTypeSAST                 ArtifactFileTypeValue = "sast"
	ArtifactFileTypeSecretDetection      ArtifactFileTypeValue = "secret_detection"
)

// BranchFilterStrategy represents the strategy used to filter branches for
// push event hooks.
//