diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4b165ff..23d2fec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: - name: Set up Go uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: - go-version: "1.27.1" + go-version: "1.27.2" cache: true - name: Cache Go tools @@ -45,7 +45,7 @@ jobs: - name: Set up Go uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: - go-version: "1.27.1" + go-version: "1.27.2" cache: true - name: Cache Go tools @@ -62,7 +62,7 @@ jobs: make coverage - name: Upload codequality artifacts - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: codequality path: codequality/ @@ -93,7 +93,7 @@ jobs: - name: Set up Go uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: - go-version: "1.27.1" + go-version: "1.27.2" cache: true - name: Cache Go tools @@ -113,7 +113,7 @@ jobs: runs-on: ubuntu-latest services: sonarqube: - image: docker.io/library/sonarqube:26.8.0.126808-community@sha256:9026624a61cd25542a402a9e7213dd7dbb39724ac9597e331e6b85362558c079 + image: docker.io/library/sonarqube:26.9.0.129388-community@sha256:c0f1160bccfa435db4168c2d7df69af3c3ea7bfeb87395613014048fb33a68c1 ports: - 9000:9000 env: @@ -125,7 +125,7 @@ jobs: - name: Set up Go uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: - go-version: "1.27.1" + go-version: "1.27.2" cache: true - name: Cache Go tools @@ -149,7 +149,7 @@ jobs: # secret to also exercise license-dependent behavior. services: sonarqube-enterprise: - image: docker.io/library/sonarqube:2026.5.0-enterprise@sha256:eb87329f5afbb5737e6bcb5d6195ad9649cf08875f767e2035780f4c2caf4602 + image: docker.io/library/sonarqube:2026.5.1-enterprise@sha256:0f80c7c321803470eff5fff84a220aea8af964df5c5c05246601acc9a4d51a89 ports: - 9001:9000 env: @@ -161,7 +161,7 @@ jobs: - name: Set up Go uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: - go-version: "1.27.1" + go-version: "1.27.2" cache: true - name: Cache Go tools @@ -189,7 +189,7 @@ jobs: - name: Set up Go uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: - go-version: "1.27.1" + go-version: "1.27.2" - name: Build run: make build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 54bddc7..5488ed0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -55,7 +55,7 @@ jobs: - name: Set up Go uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: - go-version: "1.27.1" + go-version: "1.27.2" - name: Build run: make build diff --git a/Makefile b/Makefile index 44677a7..28c809c 100644 --- a/Makefile +++ b/Makefile @@ -6,20 +6,20 @@ enterprise_endpoint := http://127.0.0.1:9001 username := admin password := admin # renovate: datasource=docker depName=docker.io/library/sonarqube versioning=docker -sonarqube_version := 26.8.0.126808-community +sonarqube_version := 26.9.0.129388-community # renovate: datasource=docker depName=docker.io/library/sonarqube versioning=docker -sonarqube_enterprise_version := 2026.5.0-enterprise +sonarqube_enterprise_version := 2026.5.1-enterprise # Versions of the Go tools installed on demand by the targets below. # Kept as annotated variables so Renovate bumps them (see renovate.json customManagers). # renovate: datasource=go depName=gotest.tools/gotestsum gotestsum_version := v1.13.0 # renovate: datasource=go depName=github.com/onsi/ginkgo/v2 -ginkgo_version := v2.33.0 +ginkgo_version := v2.33.1 # renovate: datasource=go depName=github.com/golangci/golangci-lint/v2 golangci_lint_version := v2.14.0 # renovate: datasource=go depName=golang.org/x/vuln -govulncheck_version := v1.8.0 +govulncheck_version := v1.9.0 version := $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev") build_time := $(shell date -u +%Y-%m-%dT%H:%M:%SZ) diff --git a/assets/api.enterprise.json b/assets/api.enterprise.json index 1e64621..0a65fe3 100644 --- a/assets/api.enterprise.json +++ b/assets/api.enterprise.json @@ -6368,14 +6368,14 @@ "required": false, "internal": false, "possibleValues": [ - "UPDATE_DATE", - "TYPE_SEVERITY", - "HOTSPOTS", - "IMPACT_RANK", - "CREATION_DATE", - "SEVERITY", - "STATUS", "FILE_LINE", + "STATUS", + "SEVERITY", + "CREATION_DATE", + "IMPACT_RANK", + "HOTSPOTS", + "TYPE_SEVERITY", + "UPDATE_DATE", "CLOSE_DATE" ] }, @@ -7210,7 +7210,7 @@ }, { "key": "metricKeys", - "description": "Comma-separated list of metric keys. Types DISTRIB are not allowed. For type DATA only reliability_issues, maintainability_issues, security_issues, new_reliability_issues, new_security_issues, new_maintainability_issues metrics are supported", + "description": "Comma-separated list of metric keys. Types DISTRIB are not allowed. For type DATA only security_issues, maintainability_issues, reliability_issues, new_maintainability_issues, new_security_issues, new_reliability_issues metrics are supported", "required": true, "internal": false, "exampleValue": "ncloc,complexity,violations", @@ -7314,9 +7314,9 @@ "internal": false, "defaultValue": "all", "possibleValues": [ - "all", + "children", "leaves", - "children" + "all" ] } ] @@ -10909,7 +10909,7 @@ }, { "key": "metric", - "description": "Condition metric.\u003cbr/\u003e Only metric of the following types are allowed:\u003cul\u003e\u003cli\u003eINT\u003c/li\u003e\u003cli\u003eMILLISEC\u003c/li\u003e\u003cli\u003eRATING\u003c/li\u003e\u003cli\u003eWORK_DUR\u003c/li\u003e\u003cli\u003eFLOAT\u003c/li\u003e\u003cli\u003ePERCENT\u003c/li\u003e\u003cli\u003eLEVEL\u003c/li\u003e\u003c/ul\u003eFollowing metrics are forbidden:\u003cul\u003e\u003cli\u003ealert_status\u003c/li\u003e\u003cli\u003enew_security_hotspots\u003c/li\u003e\u003cli\u003esecurity_hotspots\u003c/li\u003e\u003c/ul\u003e", + "description": "Condition metric.\u003cbr/\u003e Only metric of the following types are allowed:\u003cul\u003e\u003cli\u003eINT\u003c/li\u003e\u003cli\u003eMILLISEC\u003c/li\u003e\u003cli\u003eRATING\u003c/li\u003e\u003cli\u003eWORK_DUR\u003c/li\u003e\u003cli\u003eFLOAT\u003c/li\u003e\u003cli\u003ePERCENT\u003c/li\u003e\u003cli\u003eLEVEL\u003c/li\u003e\u003c/ul\u003eFollowing metrics are forbidden:\u003cul\u003e\u003cli\u003enew_security_hotspots\u003c/li\u003e\u003cli\u003ealert_status\u003c/li\u003e\u003cli\u003esecurity_hotspots\u003c/li\u003e\u003c/ul\u003e", "required": true, "internal": false, "exampleValue": "blocker_violations, vulnerabilities, new_code_smells" @@ -11710,7 +11710,7 @@ }, { "key": "metric", - "description": "Condition metric.\u003cbr/\u003e Only metric of the following types are allowed:\u003cul\u003e\u003cli\u003eINT\u003c/li\u003e\u003cli\u003eMILLISEC\u003c/li\u003e\u003cli\u003eRATING\u003c/li\u003e\u003cli\u003eWORK_DUR\u003c/li\u003e\u003cli\u003eFLOAT\u003c/li\u003e\u003cli\u003ePERCENT\u003c/li\u003e\u003cli\u003eLEVEL\u003c/li\u003e\u003c/ul\u003eFollowing metrics are forbidden:\u003cul\u003e\u003cli\u003ealert_status\u003c/li\u003e\u003cli\u003enew_security_hotspots\u003c/li\u003e\u003cli\u003esecurity_hotspots\u003c/li\u003e\u003c/ul\u003e", + "description": "Condition metric.\u003cbr/\u003e Only metric of the following types are allowed:\u003cul\u003e\u003cli\u003eINT\u003c/li\u003e\u003cli\u003eMILLISEC\u003c/li\u003e\u003cli\u003eRATING\u003c/li\u003e\u003cli\u003eWORK_DUR\u003c/li\u003e\u003cli\u003eFLOAT\u003c/li\u003e\u003cli\u003ePERCENT\u003c/li\u003e\u003cli\u003eLEVEL\u003c/li\u003e\u003c/ul\u003eFollowing metrics are forbidden:\u003cul\u003e\u003cli\u003enew_security_hotspots\u003c/li\u003e\u003cli\u003ealert_status\u003c/li\u003e\u003cli\u003esecurity_hotspots\u003c/li\u003e\u003c/ul\u003e", "required": true, "internal": false, "exampleValue": "blocker_violations, vulnerabilities, new_code_smells" @@ -12108,12 +12108,12 @@ "description": "Sort field", "required": false, "internal": false, - "exampleValue": "key", + "exampleValue": "updatedAt", "possibleValues": [ - "key", "updatedAt", - "createdAt", - "name" + "key", + "name", + "createdAt" ] }, { @@ -13258,12 +13258,12 @@ "description": "Sort field", "required": false, "internal": false, - "exampleValue": "key", + "exampleValue": "updatedAt", "possibleValues": [ - "key", "updatedAt", - "createdAt", - "name" + "key", + "name", + "createdAt" ] }, { @@ -15142,7 +15142,7 @@ "description": "Comma-separated list of additional fields to be returned in the response. All the fields are returned by default, except actives.", "required": false, "internal": false, - "exampleValue": "noteLogin,isExternal", + "exampleValue": "mdNote,actives", "possibleValues": [ "actives", "cleanCodeAttribute", @@ -15400,12 +15400,12 @@ "description": "Sort field", "required": false, "internal": false, - "exampleValue": "key", + "exampleValue": "updatedAt", "possibleValues": [ - "key", "updatedAt", - "createdAt", - "name" + "key", + "name", + "createdAt" ] }, { diff --git a/assets/api.enterprise.v2.json b/assets/api.enterprise.v2.json index eb97567..e59b099 100644 --- a/assets/api.enterprise.v2.json +++ b/assets/api.enterprise.v2.json @@ -3,7 +3,7 @@ "info": { "title": "SonarQube Web API v2", "description": "The SonarQube API v2 is a REST API which enables you to interact with SonarQube programmatically.\nWhile not all endpoints of the former Web API are available yet, the ones available are stable and can be used in production environments.\n", - "version": "2026.5.0.132233" + "version": "2026.5.1.132435" }, "servers": [ { @@ -523,9 +523,9 @@ "/a3s/context-uploads/{contextId}": { "put": { "tags": [ - "q" + "y" ], - "operationId": "젔", + "operationId": "悛", "parameters": [ { "name": "contextId", @@ -1545,7 +1545,7 @@ ], "summary": "Fetch Jira work items", "description": "Fetch the Jira work items for a specific Sonar project and resource.\nAccepts only authenticated requests.\n", - "operationId": "젔_1", + "operationId": "悛_1", "parameters": [ { "name": "sonarProjectId", @@ -1596,7 +1596,7 @@ ], "summary": "Create Jira work item", "description": "Create a Jira work item for a specific resource.\nAccepts only authenticated requests with issue administration permission.\n", - "operationId": "젔_2", + "operationId": "悛_2", "requestBody": { "content": { "application/json": { @@ -1627,7 +1627,7 @@ ], "summary": "Delete Jira work items", "description": "Delete the Jira work items associated with a specific resource.\n", - "operationId": "藕", + "operationId": "嬊", "parameters": [ { "name": "sonarProjectId", @@ -1673,7 +1673,7 @@ ], "summary": "Fetch Jira project bindings", "description": "Fetch the Jira project binding for a specific Sonar project.\nAccepts only authenticated requests.\n", - "operationId": "젔_3", + "operationId": "悛_3", "parameters": [ { "name": "sonarProjectId", @@ -1704,7 +1704,7 @@ ], "summary": "Create or update Jira project binding", "description": "Create or update a Jira project binding for a specific Sonar project.\nAccepts only authenticated requests.\n", - "operationId": "젔_4", + "operationId": "悛_4", "requestBody": { "content": { "application/json": { @@ -1735,7 +1735,7 @@ ], "summary": "Delete Jira project binding", "description": "Delete the Jira project binding for a specific Sonar project.\nAccepts only authenticated requests.\n", - "operationId": "藕_1", + "operationId": "嬊_1", "parameters": [ { "name": "sonarProjectId", @@ -1759,7 +1759,7 @@ ], "summary": "Update Jira project binding", "description": "Update an existing Jira project binding for a specific Sonar project.\nAccepts only authenticated requests.\n", - "operationId": "젔_5", + "operationId": "悛_5", "requestBody": { "content": { "application/json": { @@ -1792,7 +1792,7 @@ ], "summary": "Fetch the Jira instance binding", "description": "Fetch the Jira instance binding.\nRequires global administrator permission.\n", - "operationId": "藕_2", + "operationId": "嬊_2", "parameters": [ { "name": "sonarOrganizationUuid", @@ -1824,7 +1824,7 @@ ], "summary": "Create a new Jira instance binding", "description": "Receives the 3LO state and authorization code parameters and attempts to create an instance binding.\nIf successful, returns the binding. Otherwise, returns a list of available resources.\nRequires global administrator permission.\n", - "operationId": "젔_6", + "operationId": "悛_6", "requestBody": { "content": { "application/json": { @@ -1855,7 +1855,7 @@ ], "summary": "Delete the Jira instance binding", "description": "Deletes the Jira instance binding from the database.\nRequires global administrator permission.\n", - "operationId": "젔_7", + "operationId": "悛_7", "parameters": [ { "name": "sonarOrganizationUuid", @@ -1880,7 +1880,7 @@ ], "summary": "Binds the specified pending instance binding with the specified jira cloud ID", "description": "Binds the specified pending instance binding with the specified jira cloud ID\n", - "operationId": "젔_8", + "operationId": "悛_8", "requestBody": { "content": { "application/json": { @@ -2814,9 +2814,9 @@ "/detection-agent/jobs/results": { "post": { "tags": [ - "n" + "h" ], - "operationId": "젔_9", + "operationId": "悛_9", "parameters": [ { "name": "jobId", @@ -3593,7 +3593,7 @@ ], "summary": "Fetch the Atlassian Authentication details if they exist", "description": "Fetch the Atlassian Authentication details if they exist.\nSpecifically, only the client ID is returned for security purposes.\n", - "operationId": "젔_10", + "operationId": "悛_10", "responses": { "200": { "description": "OK", @@ -3614,7 +3614,7 @@ ], "summary": "Create/Update the Atlassian Authentication details", "description": "Create/Update the Atlassian Authentication details.\n", - "operationId": "젔_11", + "operationId": "悛_11", "requestBody": { "content": { "application/json": { @@ -6560,7 +6560,7 @@ ], "summary": "Returns a list of all the available Jira work types for a specific Jira project", "description": "Returns a list of all the available Jira work types for a specific Jira project.\nAlso checks which work types are selected for a given sonar project. Conditionally, also includes field metadata.\n", - "operationId": "젔_12", + "operationId": "悛_12", "parameters": [ { "name": "jiraProjectKey", @@ -6619,7 +6619,7 @@ ], "summary": "Saves the selected Jira work types for a project", "description": "Receives a Jira project key and a list of work type and stores the work types.\n", - "operationId": "젔_13", + "operationId": "悛_13", "requestBody": { "content": { "application/json": { @@ -6645,7 +6645,7 @@ ], "summary": "Update Jira organization binding properties", "description": "Update properties of an existing Jira organization binding, such as token sharing configuration.\nRequires global administrator permission.\n", - "operationId": "젔_14", + "operationId": "悛_14", "requestBody": { "content": { "application/json": { @@ -9763,7 +9763,7 @@ ], "summary": "Get compliance report", "description": "Get the compliance report for a project, application, or portfolio.", - "operationId": "젔_15", + "operationId": "悛_15", "parameters": [ { "name": "branchKey", @@ -9850,7 +9850,7 @@ ], "summary": "Get accessibility report", "description": "Get the accessibility report for a project branch.", - "operationId": "젔_16", + "operationId": "悛_16", "parameters": [ { "name": "branchKey", @@ -11951,7 +11951,7 @@ ], "summary": "Returns a list of available user actions for a project", "description": "Returns a list of available user actions for the authenticated user in the context of a specific project.\nIf the user is not authenticated, returns an empty list.\n", - "operationId": "젔_17", + "operationId": "悛_17", "parameters": [ { "name": "sonarProjectId", @@ -11987,7 +11987,7 @@ ], "summary": "Returns a list of all the available Jira projects", "description": "Returns a list of all the available Jira projects", - "operationId": "젔_18", + "operationId": "悛_18", "parameters": [ { "name": "sonarOrganizationUuid", @@ -12024,7 +12024,7 @@ ], "summary": "Count linked Jira issues", "description": "Count the number of Jira issues linked to a specific Sonar project.\nAccepts only authenticated requests.\n", - "operationId": "젔_19", + "operationId": "悛_19", "parameters": [ { "name": "sonarProjectId", @@ -14556,7 +14556,7 @@ ], "summary": "Get JFrog evidence for a Compute Engine task", "description": "Returns a JFrog evidence statement for the specified Compute Engine task.\nThe evidence contains quality gate status and conditions in the in-toto Statement format.\nRequires 'Browse' permission on the project associated with the task.", - "operationId": "젔_20", + "operationId": "悛_20", "parameters": [ { "name": "taskId", @@ -14574,7 +14574,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/n" + "$ref": "#/components/schemas/H" } } } @@ -15693,7 +15693,7 @@ ], "summary": "Generate Jira authentication URL", "description": "Generate the authentication URL for Jira OAuth flow.\nRequires authenticated user.\n", - "operationId": "젔_21", + "operationId": "悛_21", "parameters": [ { "name": "sonarOrganizationKey", @@ -27191,6 +27191,9 @@ "supported": { "type": "boolean" }, + "legacy": { + "type": "boolean" + }, "expired": { "type": "boolean" }, @@ -27203,9 +27206,6 @@ "officialDistribution": { "type": "boolean" }, - "legacy": { - "type": "boolean" - }, "disabled": { "type": "boolean" } @@ -27409,47 +27409,7 @@ } } }, - "C": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "QUALITY" - ] - }, - "status": { - "type": "string", - "enum": [ - "OK", - "ERROR", - "WARN", - "NONE" - ] - }, - "ignoredConditions": { - "type": "boolean" - }, - "conditions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/e" - } - } - } - }, - "W": { - "type": "object", - "properties": { - "gates": { - "type": "array", - "items": { - "$ref": "#/components/schemas/C" - } - } - } - }, - "e": { + "E": { "type": "object", "properties": { "status": { @@ -27484,7 +27444,7 @@ } } }, - "n": { + "H": { "type": "object", "properties": { "_type": { @@ -27494,7 +27454,7 @@ "type": "string" }, "predicate": { - "$ref": "#/components/schemas/W" + "$ref": "#/components/schemas/N" }, "createdAt": { "type": "string", @@ -27508,6 +27468,46 @@ } } }, + "N": { + "type": "object", + "properties": { + "gates": { + "type": "array", + "items": { + "$ref": "#/components/schemas/l" + } + } + } + }, + "l": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "QUALITY" + ] + }, + "status": { + "type": "string", + "enum": [ + "OK", + "ERROR", + "WARN", + "NONE" + ] + }, + "ignoredConditions": { + "type": "boolean" + }, + "conditions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/E" + } + } + } + }, "PermissionMappingsSearchRestResponse": { "type": "object", "properties": { diff --git a/assets/api.json b/assets/api.json index 83bf399..bd69a67 100644 --- a/assets/api.json +++ b/assets/api.json @@ -982,6 +982,10 @@ "post": false, "hasResponseExample": true, "changelog": [ + { + "description": "GitHub and GitLab bindings now contain a slug field with the repository\u0027s full name or path", + "version": "2026.5" + }, { "description": "GitHub, GitLab and Azure bindings now contain a repositoryUrl field with the URL to the repository", "version": "2025.6" @@ -2134,6 +2138,7 @@ "new_duplicated_lines_density", "new_lines", "new_maintainability_rating", + "new_ncloc", "new_reliability_rating", "new_sca_rating_any_issue", "new_security_hotspots_reviewed", @@ -2157,7 +2162,7 @@ }, { "key": "filter", - "description": "Filter of projects on name, key, measure value, quality gate, language, tag or whether a project is a favorite or not.\u003cbr\u003eThe filter must be encoded to form a valid URL (for example \u0027\u003d\u0027 must be replaced by \u0027%3D\u0027).\u003cbr\u003eExamples of use:\u003cul\u003e \u003cli\u003eto filter my favorite projects with a failed quality gate and a coverage greater than or equals to 60% and a coverage strictly lower than 80%:\u003cbr\u003e \u003ccode\u003efilter\u003d\"alert_status \u003d ERROR and isFavorite and coverage \u003e\u003d 60 and coverage \u003c 80\"\u003c/code\u003e\u003c/li\u003e \u003cli\u003eto filter projects with a reliability, security and maintainability rating equals or worse than B:\u003cbr\u003e \u003ccode\u003efilter\u003d\"reliability_rating\u003e\u003d2 and security_rating\u003e\u003d2 and sqale_rating\u003e\u003d2\"\u003c/code\u003e\u003c/li\u003e \u003cli\u003eto filter projects without duplication data:\u003cbr\u003e \u003ccode\u003efilter\u003d\"duplicated_lines_density \u003d NO_DATA\"\u003c/code\u003e\u003c/li\u003e\u003c/ul\u003eTo filter on project name or key, use the \u0027query\u0027 keyword, for instance : \u003ccode\u003efilter\u003d\u0027query \u003d \"Sonar\"\u0027\u003c/code\u003e.\u003cbr\u003e\u003cbr\u003eTo filter on a numeric metric, provide the metric key.\u003cbr\u003eThese are the supported metric keys:\u003cbr\u003e\u003cul\u003e\u003cli\u003ealert_status\u003c/li\u003e\u003cli\u003ecoverage\u003c/li\u003e\u003cli\u003eduplicated_lines_density\u003c/li\u003e\u003cli\u003elines\u003c/li\u003e\u003cli\u003encloc\u003c/li\u003e\u003cli\u003encloc_language_distribution\u003c/li\u003e\u003cli\u003enew_coverage\u003c/li\u003e\u003cli\u003enew_duplicated_lines_density\u003c/li\u003e\u003cli\u003enew_lines\u003c/li\u003e\u003cli\u003enew_maintainability_rating\u003c/li\u003e\u003cli\u003enew_reliability_rating\u003c/li\u003e\u003cli\u003enew_sca_rating_any_issue\u003c/li\u003e\u003cli\u003enew_security_hotspots_reviewed\u003c/li\u003e\u003cli\u003enew_security_rating\u003c/li\u003e\u003cli\u003enew_security_review_rating\u003c/li\u003e\u003cli\u003enew_software_quality_maintainability_rating\u003c/li\u003e\u003cli\u003enew_software_quality_reliability_rating\u003c/li\u003e\u003cli\u003enew_software_quality_security_rating\u003c/li\u003e\u003cli\u003ereliability_rating\u003c/li\u003e\u003cli\u003esca_rating_any_issue\u003c/li\u003e\u003cli\u003esecurity_hotspots_reviewed\u003c/li\u003e\u003cli\u003esecurity_rating\u003c/li\u003e\u003cli\u003esecurity_review_rating\u003c/li\u003e\u003cli\u003esoftware_quality_maintainability_rating\u003c/li\u003e\u003cli\u003esoftware_quality_reliability_rating\u003c/li\u003e\u003cli\u003esoftware_quality_security_rating\u003c/li\u003e\u003cli\u003esqale_rating\u003c/li\u003e\u003c/ul\u003e\u003cbr\u003eTo filter on a rating, provide the corresponding metric key (ex: reliability_rating for reliability rating).\u003cbr\u003eThe possible values are:\u003cul\u003e \u003cli\u003e\u00271\u0027 for rating A\u003c/li\u003e \u003cli\u003e\u00272\u0027 for rating B\u003c/li\u003e \u003cli\u003e\u00273\u0027 for rating C\u003c/li\u003e \u003cli\u003e\u00274\u0027 for rating D\u003c/li\u003e \u003cli\u003e\u00275\u0027 for rating E\u003c/li\u003e\u003c/ul\u003eTo filter on a Quality Gate status use the metric key \u0027alert_status\u0027. Only the \u0027\u003d\u0027 operator can be used.\u003cbr\u003eThe possible values are:\u003cul\u003e \u003cli\u003e\u0027OK\u0027 for Passed\u003c/li\u003e \u003cli\u003e\u0027WARN\u0027 for Warning\u003c/li\u003e \u003cli\u003e\u0027ERROR\u0027 for Failed\u003c/li\u003e\u003c/ul\u003eTo filter on languages use the \u0027languages\u0027 keyword: \u003cul\u003e \u003cli\u003eto filter on a single language you can use \u0027languages \u003d java\u0027\u003c/li\u003e \u003cli\u003eto filter on several languages you must use \u0027languages IN (java, js)\u0027\u003c/li\u003e\u003c/ul\u003eUse the WS api/languages/list to find the key of a language.\u003cbr\u003e To filter on tags use the \u0027tags\u0027 keyword:\u003cul\u003e \u003cli\u003eto filter on one tag you can use \u003ccode\u003etags \u003d finance\u003c/code\u003e\u003c/li\u003e \u003cli\u003eto filter on several tags you must use \u003ccode\u003etags in (offshore, java)\u003c/code\u003e\u003c/li\u003e\u003c/ul\u003eTo filter on a qualifier use key \u0027qualifier\u0027. Only the \u0027\u003d\u0027 operator can be used.\u003cbr\u003eThe possible values are:\u003cul\u003e \u003cli\u003eTRK - for projects\u003c/li\u003e \u003cli\u003eAPP - for applications\u003c/li\u003e\u003c/ul\u003e", + "description": "Filter of projects on name, key, measure value, quality gate, language, tag or whether a project is a favorite or not.\u003cbr\u003eThe filter must be encoded to form a valid URL (for example \u0027\u003d\u0027 must be replaced by \u0027%3D\u0027).\u003cbr\u003eExamples of use:\u003cul\u003e \u003cli\u003eto filter my favorite projects with a failed quality gate and a coverage greater than or equals to 60% and a coverage strictly lower than 80%:\u003cbr\u003e \u003ccode\u003efilter\u003d\"alert_status \u003d ERROR and isFavorite and coverage \u003e\u003d 60 and coverage \u003c 80\"\u003c/code\u003e\u003c/li\u003e \u003cli\u003eto filter projects with a reliability, security and maintainability rating equals or worse than B:\u003cbr\u003e \u003ccode\u003efilter\u003d\"reliability_rating\u003e\u003d2 and security_rating\u003e\u003d2 and sqale_rating\u003e\u003d2\"\u003c/code\u003e\u003c/li\u003e \u003cli\u003eto filter projects without duplication data:\u003cbr\u003e \u003ccode\u003efilter\u003d\"duplicated_lines_density \u003d NO_DATA\"\u003c/code\u003e\u003c/li\u003e\u003c/ul\u003eTo filter on project name or key, use the \u0027query\u0027 keyword, for instance : \u003ccode\u003efilter\u003d\u0027query \u003d \"Sonar\"\u0027\u003c/code\u003e.\u003cbr\u003e\u003cbr\u003eTo filter on a numeric metric, provide the metric key.\u003cbr\u003eThese are the supported metric keys:\u003cbr\u003e\u003cul\u003e\u003cli\u003ealert_status\u003c/li\u003e\u003cli\u003ecoverage\u003c/li\u003e\u003cli\u003eduplicated_lines_density\u003c/li\u003e\u003cli\u003elines\u003c/li\u003e\u003cli\u003encloc\u003c/li\u003e\u003cli\u003encloc_language_distribution\u003c/li\u003e\u003cli\u003enew_coverage\u003c/li\u003e\u003cli\u003enew_duplicated_lines_density\u003c/li\u003e\u003cli\u003enew_lines\u003c/li\u003e\u003cli\u003enew_maintainability_rating\u003c/li\u003e\u003cli\u003enew_ncloc\u003c/li\u003e\u003cli\u003enew_reliability_rating\u003c/li\u003e\u003cli\u003enew_sca_rating_any_issue\u003c/li\u003e\u003cli\u003enew_security_hotspots_reviewed\u003c/li\u003e\u003cli\u003enew_security_rating\u003c/li\u003e\u003cli\u003enew_security_review_rating\u003c/li\u003e\u003cli\u003enew_software_quality_maintainability_rating\u003c/li\u003e\u003cli\u003enew_software_quality_reliability_rating\u003c/li\u003e\u003cli\u003enew_software_quality_security_rating\u003c/li\u003e\u003cli\u003ereliability_rating\u003c/li\u003e\u003cli\u003esca_rating_any_issue\u003c/li\u003e\u003cli\u003esecurity_hotspots_reviewed\u003c/li\u003e\u003cli\u003esecurity_rating\u003c/li\u003e\u003cli\u003esecurity_review_rating\u003c/li\u003e\u003cli\u003esoftware_quality_maintainability_rating\u003c/li\u003e\u003cli\u003esoftware_quality_reliability_rating\u003c/li\u003e\u003cli\u003esoftware_quality_security_rating\u003c/li\u003e\u003cli\u003esqale_rating\u003c/li\u003e\u003c/ul\u003e\u003cbr\u003eTo filter on a rating, provide the corresponding metric key (ex: reliability_rating for reliability rating).\u003cbr\u003eThe possible values are:\u003cul\u003e \u003cli\u003e\u00271\u0027 for rating A\u003c/li\u003e \u003cli\u003e\u00272\u0027 for rating B\u003c/li\u003e \u003cli\u003e\u00273\u0027 for rating C\u003c/li\u003e \u003cli\u003e\u00274\u0027 for rating D\u003c/li\u003e \u003cli\u003e\u00275\u0027 for rating E\u003c/li\u003e\u003c/ul\u003eTo filter on a Quality Gate status use the metric key \u0027alert_status\u0027. Only the \u0027\u003d\u0027 operator can be used.\u003cbr\u003eThe possible values are:\u003cul\u003e \u003cli\u003e\u0027OK\u0027 for Passed\u003c/li\u003e \u003cli\u003e\u0027WARN\u0027 for Warning\u003c/li\u003e \u003cli\u003e\u0027ERROR\u0027 for Failed\u003c/li\u003e\u003c/ul\u003eTo filter on languages use the \u0027languages\u0027 keyword: \u003cul\u003e \u003cli\u003eto filter on a single language you can use \u0027languages \u003d java\u0027\u003c/li\u003e \u003cli\u003eto filter on several languages you must use \u0027languages IN (java, js)\u0027\u003c/li\u003e\u003c/ul\u003eUse the WS api/languages/list to find the key of a language.\u003cbr\u003e To filter on tags use the \u0027tags\u0027 keyword:\u003cul\u003e \u003cli\u003eto filter on one tag you can use \u003ccode\u003etags \u003d finance\u003c/code\u003e\u003c/li\u003e \u003cli\u003eto filter on several tags you must use \u003ccode\u003etags in (offshore, java)\u003c/code\u003e\u003c/li\u003e\u003c/ul\u003eTo filter on a qualifier use key \u0027qualifier\u0027. Only the \u0027\u003d\u0027 operator can be used.\u003cbr\u003eThe possible values are:\u003cul\u003e \u003cli\u003eTRK - for projects\u003c/li\u003e \u003cli\u003eAPP - for applications\u003c/li\u003e\u003c/ul\u003e", "required": false, "internal": false, "minimumLength": 2 @@ -2200,6 +2205,7 @@ "new_duplicated_lines_density", "new_lines", "new_maintainability_rating", + "new_ncloc", "new_reliability_rating", "new_sca_rating_any_issue", "new_security_hotspots_reviewed", @@ -3051,6 +3057,53 @@ } ] }, + { + "key": "migrate_to_issues", + "description": "Migrate Security Hotspots to Issues. Requires \u0027Administer System\u0027 permission.", + "since": "2026.4", + "internal": true, + "post": true, + "hasResponseExample": false, + "changelog": [], + "params": [ + { + "key": "dryRun", + "description": "If true, only count hotspots to migrate without performing any writes.", + "required": false, + "internal": false, + "defaultValue": "false", + "possibleValues": [ + "true", + "false", + "yes", + "no" + ] + }, + { + "key": "project", + "description": "Project key. If not provided, all projects are migrated.", + "required": false, + "internal": false + } + ] + }, + { + "key": "migration_status", + "description": "Report how many Security Hotspots remain to be migrated to Issues (optionally scoped to a project). The migration is complete for the scope once this reaches zero. Requires \u0027Administer System\u0027 permission.", + "since": "2026.4", + "internal": true, + "post": false, + "hasResponseExample": false, + "changelog": [], + "params": [ + { + "key": "project", + "description": "Project key. If not provided, the status is reported for the whole instance.", + "required": false, + "internal": false + } + ] + }, { "key": "pull", "description": "This endpoint fetches and returns all (unless filtered by optional params) the hotspots for a given branch. The hotspots returned are not paginated, so the response size can be big. Requires project \u0027Browse\u0027 permission.", @@ -5140,15 +5193,15 @@ "required": false, "internal": false, "possibleValues": [ - "CREATION_DATE", - "HOTSPOTS", - "TYPE_SEVERITY", - "UPDATE_DATE", "CLOSE_DATE", - "QUALITY_SEVERITY", - "FILE_LINE", + "UPDATE_DATE", + "TYPE_SEVERITY", + "HOTSPOTS", + "IMPACT_RANK", + "CREATION_DATE", + "SEVERITY", "STATUS", - "SEVERITY" + "FILE_LINE" ] }, { @@ -5982,7 +6035,7 @@ }, { "key": "metricKeys", - "description": "Comma-separated list of metric keys. Types DISTRIB are not allowed. For type DATA only security_issues, maintainability_issues, reliability_issues, new_maintainability_issues, new_security_issues, new_reliability_issues metrics are supported", + "description": "Comma-separated list of metric keys. Types DISTRIB are not allowed. For type DATA only new_maintainability_issues, reliability_issues, maintainability_issues, security_issues, new_reliability_issues, new_security_issues metrics are supported", "required": true, "internal": false, "exampleValue": "ncloc,complexity,violations", @@ -6084,8 +6137,8 @@ "defaultValue": "all", "possibleValues": [ "all", - "children", - "leaves" + "leaves", + "children" ] } ] @@ -9257,7 +9310,7 @@ }, { "key": "metric", - "description": "Condition metric.\u003cbr/\u003e Only metric of the following types are allowed:\u003cul\u003e\u003cli\u003eINT\u003c/li\u003e\u003cli\u003eMILLISEC\u003c/li\u003e\u003cli\u003eRATING\u003c/li\u003e\u003cli\u003eWORK_DUR\u003c/li\u003e\u003cli\u003eFLOAT\u003c/li\u003e\u003cli\u003ePERCENT\u003c/li\u003e\u003cli\u003eLEVEL\u003c/li\u003e\u003c/ul\u003eFollowing metrics are forbidden:\u003cul\u003e\u003cli\u003ealert_status\u003c/li\u003e\u003cli\u003esecurity_hotspots\u003c/li\u003e\u003cli\u003enew_security_hotspots\u003c/li\u003e\u003c/ul\u003e", + "description": "Condition metric.\u003cbr/\u003e Only metric of the following types are allowed:\u003cul\u003e\u003cli\u003eINT\u003c/li\u003e\u003cli\u003eMILLISEC\u003c/li\u003e\u003cli\u003eRATING\u003c/li\u003e\u003cli\u003eWORK_DUR\u003c/li\u003e\u003cli\u003eFLOAT\u003c/li\u003e\u003cli\u003ePERCENT\u003c/li\u003e\u003cli\u003eLEVEL\u003c/li\u003e\u003c/ul\u003eFollowing metrics are forbidden:\u003cul\u003e\u003cli\u003ealert_status\u003c/li\u003e\u003cli\u003enew_security_hotspots\u003c/li\u003e\u003cli\u003esecurity_hotspots\u003c/li\u003e\u003c/ul\u003e", "required": true, "internal": false, "exampleValue": "blocker_violations, vulnerabilities, new_code_smells" @@ -10028,7 +10081,7 @@ }, { "key": "metric", - "description": "Condition metric.\u003cbr/\u003e Only metric of the following types are allowed:\u003cul\u003e\u003cli\u003eINT\u003c/li\u003e\u003cli\u003eMILLISEC\u003c/li\u003e\u003cli\u003eRATING\u003c/li\u003e\u003cli\u003eWORK_DUR\u003c/li\u003e\u003cli\u003eFLOAT\u003c/li\u003e\u003cli\u003ePERCENT\u003c/li\u003e\u003cli\u003eLEVEL\u003c/li\u003e\u003c/ul\u003eFollowing metrics are forbidden:\u003cul\u003e\u003cli\u003ealert_status\u003c/li\u003e\u003cli\u003esecurity_hotspots\u003c/li\u003e\u003cli\u003enew_security_hotspots\u003c/li\u003e\u003c/ul\u003e", + "description": "Condition metric.\u003cbr/\u003e Only metric of the following types are allowed:\u003cul\u003e\u003cli\u003eINT\u003c/li\u003e\u003cli\u003eMILLISEC\u003c/li\u003e\u003cli\u003eRATING\u003c/li\u003e\u003cli\u003eWORK_DUR\u003c/li\u003e\u003cli\u003eFLOAT\u003c/li\u003e\u003cli\u003ePERCENT\u003c/li\u003e\u003cli\u003eLEVEL\u003c/li\u003e\u003c/ul\u003eFollowing metrics are forbidden:\u003cul\u003e\u003cli\u003ealert_status\u003c/li\u003e\u003cli\u003enew_security_hotspots\u003c/li\u003e\u003cli\u003esecurity_hotspots\u003c/li\u003e\u003c/ul\u003e", "required": true, "internal": false, "exampleValue": "blocker_violations, vulnerabilities, new_code_smells" @@ -10426,12 +10479,12 @@ "description": "Sort field", "required": false, "internal": false, - "exampleValue": "name", + "exampleValue": "key", "possibleValues": [ - "name", - "createdAt", + "key", "updatedAt", - "key" + "createdAt", + "name" ] }, { @@ -11408,12 +11461,12 @@ "description": "Sort field", "required": false, "internal": false, - "exampleValue": "name", + "exampleValue": "key", "possibleValues": [ - "name", - "createdAt", + "key", "updatedAt", - "key" + "createdAt", + "name" ] }, { @@ -13020,7 +13073,7 @@ "description": "Comma-separated list of additional fields to be returned in the response. All the fields are returned by default, except actives.", "required": false, "internal": false, - "exampleValue": "templateKey,sysTags", + "exampleValue": "htmlDesc,langName", "possibleValues": [ "actives", "cleanCodeAttribute", @@ -13278,12 +13331,12 @@ "description": "Sort field", "required": false, "internal": false, - "exampleValue": "name", + "exampleValue": "key", "possibleValues": [ - "name", - "createdAt", + "key", "updatedAt", - "key" + "createdAt", + "name" ] }, { @@ -14372,6 +14425,10 @@ "post": false, "hasResponseExample": true, "changelog": [ + { + "description": "The field \u0027lta\u0027 is added to each upgrade entry to indicate whether it belongs to a Long-Term Active line", + "version": "2026.5" + }, { "description": "The field \u0027documentationUrl\u0027 is added to each upgrade entry to point to the target version\u0027s documentation", "version": "2026.2" @@ -15240,13 +15297,17 @@ }, { "key": "search", - "description": "Get a list of users. By default, only active users are returned.\u003cbr/\u003eThe following fields are only returned when user has Administer System permission or for logged-in in user :\u003cul\u003e \u003cli\u003e\u0027email\u0027\u003c/li\u003e \u003cli\u003e\u0027externalIdentity\u0027\u003c/li\u003e \u003cli\u003e\u0027externalProvider\u0027\u003c/li\u003e \u003cli\u003e\u0027groups\u0027\u003c/li\u003e \u003cli\u003e\u0027lastConnectionDate\u0027\u003c/li\u003e \u003cli\u003e\u0027sonarLintLastConnectionDate\u0027\u003c/li\u003e \u003cli\u003e\u0027tokensCount\u0027\u003c/li\u003e\u003c/ul\u003eField \u0027lastConnectionDate\u0027 is only updated every hour, so it may not be accurate, for instance when a user authenticates many times in less than one hour.", + "description": "Get a list of users. By default, only active users are returned.\u003cbr/\u003eThe following fields are only returned when user has Administer System permission or for logged-in in user :\u003cul\u003e \u003cli\u003e\u0027email\u0027\u003c/li\u003e \u003cli\u003e\u0027externalIdentity\u0027\u003c/li\u003e \u003cli\u003e\u0027externalProvider\u0027\u003c/li\u003e \u003cli\u003e\u0027groups\u0027\u003c/li\u003e \u003cli\u003e\u0027lastConnectionDate\u0027\u003c/li\u003e \u003cli\u003e\u0027scmAccounts\u0027\u003c/li\u003e \u003cli\u003e\u0027sonarLintLastConnectionDate\u0027\u003c/li\u003e \u003cli\u003e\u0027tokensCount\u0027\u003c/li\u003e\u003c/ul\u003eField \u0027lastConnectionDate\u0027 is only updated every hour, so it may not be accurate, for instance when a user authenticates many times in less than one hour.", "since": "3.6", "deprecatedSince": "10.4", "internal": false, "post": false, "hasResponseExample": true, "changelog": [ + { + "description": "Field \u0027scmAccounts\u0027 is only returned when user has Administer System permission or for the logged-in user", + "version": "26.9" + }, { "description": "Deprecated. Use GET api/v2/users-management/users instead", "version": "10.4" diff --git a/assets/api.v2.json b/assets/api.v2.json index 2c1a610..b9a2cf9 100644 --- a/assets/api.v2.json +++ b/assets/api.v2.json @@ -3,7 +3,7 @@ "info": { "title": "SonarQube Web API v2", "description": "The SonarQube API v2 is a REST API which enables you to interact with SonarQube programmatically.\nWhile not all endpoints of the former Web API are available yet, the ones available are stable and can be used in production environments.\n", - "version": "26.8.0.126808" + "version": "26.9.0.129388" }, "servers": [ { @@ -16,17 +16,33 @@ "name": "project-measures", "description": "the project-measures API" }, + { + "name": "issue-density-history", + "description": "the issue-density-history API" + }, { "name": "measures-history", "description": "the measures-history API" }, + { + "name": "built-in-dashboards", + "description": "the built-in-dashboards API" + }, { "name": "issue-count-history", "description": "the issue-count-history API" }, + { + "name": "issue-resolution-history", + "description": "the issue-resolution-history API" + }, { "name": "project-issue-counts", "description": "the project-issue-counts API" + }, + { + "name": "project-issue-resolution", + "description": "the project-issue-resolution API" } ], "paths": { @@ -402,6 +418,40 @@ } } }, + "/dop-translation/scm-access-tokens": { + "post": { + "tags": [ + "Dop Translation" + ], + "summary": "Mint a scoped, short-lived SCM access token", + "description": "Mint a scoped, short-lived git credential for the given project's bound DevOps Platform (GitHub or\nGitLab). Internal endpoint used by the agentic-workflows remediation orchestrator (SONAR-31165).\nRequires the 'Administer System' permission or trusted privileged-service authentication.\n", + "operationId": "generateScmAccessToken", + "parameters": [ + { + "name": "project", + "in": "query", + "description": "Project key", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ScmAccessTokenRestResponse" + } + } + } + } + }, + "x-sonar-internal": "true" + } + }, "/dop-translation/gitlab-configurations": { "get": { "tags": [ @@ -1447,6 +1497,194 @@ } } }, + "/onboarding/statistics": { + "get": { + "tags": [ + "onboarding-controller" + ], + "operationId": "getOnboardingStatistics", + "parameters": [ + { + "name": "organizationKey", + "in": "query", + "required": false, + "schema": { + "type": [ + "string", + "null" + ] + } + }, + { + "name": "startDate", + "in": "query", + "required": false, + "schema": { + "type": [ + "string", + "null" + ], + "format": "date-time" + } + }, + { + "name": "endDate", + "in": "query", + "required": false, + "schema": { + "type": [ + "string", + "null" + ], + "format": "date-time" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OnboardingStatisticsResponse" + } + } + } + } + } + } + }, + "/onboarding/projects": { + "get": { + "tags": [ + "onboarding-controller" + ], + "operationId": "getOnboardingProjects", + "parameters": [ + { + "name": "organizationKey", + "in": "query", + "required": false, + "schema": { + "type": [ + "string", + "null" + ] + } + }, + { + "name": "q", + "in": "query", + "required": false, + "schema": { + "type": [ + "string", + "null" + ] + } + }, + { + "name": "scanStatus", + "in": "query", + "required": false, + "schema": { + "type": [ + "string", + "null" + ], + "enum": [ + "SCANNED", + "NOT_SCANNED" + ] + } + }, + { + "name": "analysisMode", + "in": "query", + "required": false, + "schema": { + "type": [ + "string", + "null" + ], + "enum": [ + "CI", + "AUTOMATIC", + "NONE" + ] + } + }, + { + "name": "pageIndex", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 1, + "minimum": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 100, + "maximum": 500, + "minimum": 1 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OnboardingProjectsResponse" + } + } + } + } + } + } + }, + "/onboarding/overview": { + "get": { + "tags": [ + "onboarding-controller" + ], + "operationId": "getOnboardingOverview", + "parameters": [ + { + "name": "organizationKey", + "in": "query", + "required": false, + "schema": { + "type": [ + "string", + "null" + ] + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OnboardingOverviewResponse" + } + } + } + } + } + } + }, "/history/project-measures": { "get": { "tags": [ @@ -1534,6 +1772,10 @@ "type": [ "string", "null" + ], + "enum": [ + "PORTFOLIO", + "APPLICATION" ] } }, @@ -1667,15 +1909,29 @@ ] } }, - "/history/project-issue-counts": { + "/history/project-issue-resolution": { "get": { "tags": [ - "project-issue-counts" + "project-issue-resolution" ], - "summary": "Get project issue counts.", - "description": "Get issue counts for the flattened list of projects in a portfolio or application. Identify the selection with either the legacy portfolioId parameter, or with both entityType and entityId. The two selector forms are mutually exclusive. ", - "operationId": "getProjectIssueCounts", + "summary": "Get project issue resolution", + "description": "Get issue resolution values for the flattened list of projects in a portfolio or application. Identify the selection with either the legacy portfolioId parameter, or with both entityType and entityId. The two selector forms are mutually exclusive. ", + "operationId": "getProjectIssueResolution", "parameters": [ + { + "name": "statistic", + "in": "query", + "description": "The project issue resolution statistic to return.", + "required": true, + "schema": { + "type": "string", + "enum": [ + "MTTR", + "RECENT_MTTR", + "RESOLVED_ISSUES" + ] + } + }, { "name": "portfolioId", "in": "query", @@ -1697,6 +1953,10 @@ "type": [ "string", "null" + ], + "enum": [ + "PORTFOLIO", + "APPLICATION" ] } }, @@ -1712,21 +1972,6 @@ ] } }, - { - "name": "ruleKeys", - "in": "query", - "description": "Comma separated list of ruleKeys", - "required": false, - "schema": { - "type": [ - "array", - "null" - ], - "items": { - "type": "string" - } - } - }, { "name": "severities", "in": "query", @@ -1771,9 +2016,9 @@ } }, { - "name": "statuses", + "name": "impacts", "in": "query", - "description": "Limit results to issues with specific statuses (comma-separated). Reviewed security hotspots also support SAFE and FIXED.", + "description": "Limit results to issues with specific impacts (comma-separated). An impact is a combination of software quality and severity. ", "required": false, "schema": { "type": [ @@ -1781,24 +2026,276 @@ "null" ], "items": { - "type": "string", - "enum": [ - "ACCEPTED", - "CONFIRMED", - "FALSE_POSITIVE", - "FIXED", - "OPEN", - "REVIEWED", - "SAFE", - "TO_REVIEW" - ] + "type": "string" } } }, { - "name": "impacts", + "name": "nameContains", "in": "query", - "description": "Limit results to issues with specific impacts (comma-separated). An impact is a combination of software quality and severity. ", + "description": "Search query for project names, case insensitive", + "required": false, + "schema": { + "type": [ + "string", + "null" + ] + } + }, + { + "name": "trendSince", + "in": "query", + "description": "Date used as the trend comparison point.", + "required": false, + "schema": { + "type": [ + "string", + "null" + ], + "format": "date-time" + } + }, + { + "name": "pageIndex", + "in": "query", + "description": "Index of the page to fetch", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 1, + "minimum": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "description": "Number of projects to return. A value of 0 returns pagination only.", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 50, + "maximum": 5000, + "minimum": 0 + } + } + ], + "responses": { + "200": { + "description": "Successfully got project issue resolution values", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProjectIssueResolutionResponse" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "TokenAuth": [] + } + ] + } + }, + "/history/project-issue-counts": { + "get": { + "tags": [ + "project-issue-counts" + ], + "summary": "Get project issue counts.", + "description": "Get issue counts for the flattened list of projects in a portfolio or application. Identify the selection with either the legacy portfolioId parameter, or with both entityType and entityId. The two selector forms are mutually exclusive. ", + "operationId": "getProjectIssueCounts", + "parameters": [ + { + "name": "portfolioId", + "in": "query", + "description": "Legacy portfolio ID. Either portfolioId or both entityType and entityId must be provided, but not both forms. ", + "required": false, + "schema": { + "type": [ + "string", + "null" + ] + } + }, + { + "name": "entityType", + "in": "query", + "description": "Type of the selection whose projects are returned as a flattened list. Must be provided together with entityId and cannot be combined with portfolioId. ", + "required": false, + "schema": { + "type": [ + "string", + "null" + ], + "enum": [ + "PORTFOLIO", + "APPLICATION" + ] + } + }, + { + "name": "entityId", + "in": "query", + "description": "Portfolio ID or application branch ID, according to entityType. Must be provided together with entityType and cannot be combined with portfolioId. ", + "required": false, + "schema": { + "type": [ + "string", + "null" + ] + } + }, + { + "name": "ruleKeys", + "in": "query", + "description": "Comma separated list of ruleKeys", + "required": false, + "schema": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + } + }, + { + "name": "severities", + "in": "query", + "description": "Limit results to issues with specific severities (comma-separated).", + "required": false, + "schema": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string", + "enum": [ + "BLOCKER", + "HIGH", + "INFO", + "LOW", + "MEDIUM" + ] + } + } + }, + { + "name": "issueTypes", + "in": "query", + "description": "Limit results to issues with specific types (comma-separated).", + "required": false, + "schema": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string", + "enum": [ + "BUG", + "CODE_SMELL", + "SECURITY_HOTSPOT", + "VULNERABILITY" + ] + } + } + }, + { + "name": "statuses", + "in": "query", + "description": "Limit results to issues with specific statuses (comma-separated). Reviewed security hotspots also support SAFE and FIXED.", + "required": false, + "schema": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string", + "enum": [ + "ACCEPTED", + "CONFIRMED", + "FALSE_POSITIVE", + "FIXED", + "OPEN", + "REVIEWED", + "SAFE", + "TO_REVIEW" + ] + } + } + }, + { + "name": "impacts", + "in": "query", + "description": "Limit results to issues with specific impacts (comma-separated). An impact is a combination of software quality and severity. ", "required": false, "schema": { "type": [ @@ -1982,7 +2479,8 @@ "type": "string", "enum": [ "PORTFOLIO", - "PROJECT_BRANCH" + "PROJECT_BRANCH", + "APPLICATION" ] } }, @@ -2112,19 +2610,19 @@ ] } }, - "/history/issue-count-history": { + "/history/issue-resolution-history": { "get": { "tags": [ - "issue-count-history" + "issue-resolution-history" ], - "summary": "Provides issue count history data for an entity", - "description": "Returns the history of issue counts for an entity. The maximum resolution for history data is 1 day. You cannot zoom in on a period less than 1 day (e.g. 1hour). All history is recorded in UTC timezone. Issue counts will carry-forward for days with no changes. ", - "operationId": "getIssueCountHistory", + "summary": "Provides issue resolution history data for an entity", + "description": "Returns issue resolution metrics for an entity. All history is recorded at UTC midnight. MTTR and RECENT_MTTR use a trailing 30-day window ending on each returned day. SQS deliberately permits dates older than one year under the approved history date-range policy. ", + "operationId": "getIssueResolutionHistory", "parameters": [ { "name": "entityId", "in": "query", - "description": "The ID of the entity to retrieve issue count history for.", + "description": "The ID of the entity to retrieve issue resolution history for.", "required": true, "schema": { "type": "string", @@ -2138,13 +2636,32 @@ "description": "The entity's type.", "required": true, "schema": { - "type": "string" + "type": "string", + "enum": [ + "PORTFOLIO", + "PROJECT_BRANCH", + "APPLICATION" + ] + } + }, + { + "name": "statistic", + "in": "query", + "description": "The issue resolution statistic to return.", + "required": true, + "schema": { + "type": "string", + "enum": [ + "MTTR", + "RECENT_MTTR", + "RESOLVED_ISSUES" + ] } }, { "name": "startDate", "in": "query", - "description": "The date and time to begin returning issue count history in ISO 8601 format (inclusive). The start date may not be more than 1 year in the past. ", + "description": "The inclusive start of the history range in ISO 8601 format.", "required": true, "schema": { "type": "string", @@ -2154,7 +2671,7 @@ { "name": "endDate", "in": "query", - "description": "The date and time to stop returning issue count history in ISO 8601 format (inclusive). If not specified, the current date and time at UTC offset 0 will be used. ", + "description": "The inclusive end of the history range. Defaults to the current date.", "required": false, "schema": { "type": [ @@ -2200,21 +2717,6 @@ } } }, - { - "name": "ruleKeys", - "in": "query", - "description": "Comma separated list of ruleKeys", - "required": false, - "schema": { - "type": [ - "array", - "null" - ], - "items": { - "type": "string" - } - } - }, { "name": "severities", "in": "query", @@ -2240,38 +2742,18 @@ { "name": "sliceBy", "in": "query", - "description": "Optionally group issue counts by a specific dimension. If not provided, issue counts will contain a single distribution with the count of all issues. ", + "description": "Optionally group issue resolution metrics by a dimension.", "required": false, "schema": { "type": [ "string", "null" - ] - } - }, - { - "name": "statuses", - "in": "query", - "description": "Limit results to issues with specific statuses (comma-separated). Reviewed security hotspots also support SAFE and FIXED.", - "required": false, - "schema": { - "type": [ - "array", - "null" ], - "items": { - "type": "string", - "enum": [ - "ACCEPTED", - "CONFIRMED", - "FALSE_POSITIVE", - "FIXED", - "OPEN", - "REVIEWED", - "SAFE", - "TO_REVIEW" - ] - } + "enum": [ + "SEVERITY", + "SOFTWARE_QUALITY", + "TYPE" + ] } } ], @@ -2281,7 +2763,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/IssueCountHistoryResponse" + "$ref": "#/components/schemas/IssueResolutionHistoryResponse" } } } @@ -2354,145 +2836,933 @@ ] } }, - "/dop-translation/project-bindings": { + "/history/issue-density-history": { "get": { "tags": [ - "Dop Translation" + "issue-density-history" ], - "summary": "Search across project bindings", - "operationId": "getProjectBindingByProjectId", + "summary": "Provides issue density history data for an entity", + "description": "Returns the history of issue density (issues per 1000 NCLOC) for an entity. The maximum resolution for history data is 1 day. All history is recorded in UTC timezone. Issue density values carry-forward for days with no changes. Density is calculated as (issue count / NCLOC) * 1000. ", + "operationId": "getIssueDensityHistory", "parameters": [ { - "name": "repository", + "name": "entityId", "in": "query", - "description": "Filter on the repository name.\nThis parameter performs an exact, case insensitive, match.\n", + "description": "The ID of the entity to retrieve issue density history for.", + "required": true, + "schema": { + "type": "string", + "maxLength": 36, + "minLength": 36 + } + }, + { + "name": "entityType", + "in": "query", + "description": "The entity's type.", + "required": true, + "schema": { + "type": "string", + "enum": [ + "PORTFOLIO", + "PROJECT_BRANCH", + "APPLICATION" + ] + } + }, + { + "name": "startDate", + "in": "query", + "description": "The date and time to begin returning issue density history in ISO 8601 format (inclusive). The start date may not be more than 1 year in the past. ", + "required": true, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "endDate", + "in": "query", + "description": "The date and time to stop returning issue density history in ISO 8601 format (inclusive). If not specified, the current date and time at UTC offset 0 will be used. ", "required": false, "schema": { "type": [ "string", "null" ], - "description": "Filter on the repository name.\nThis parameter performs an exact, case insensitive, match.\n" + "format": "date-time" } }, { - "name": "dopSettingId", + "name": "impacts", "in": "query", - "description": "Filter on the DevOps Platform setting id.", + "description": "Limit results to issues with specific impacts (comma-separated). An impact is a combination of software quality and severity. ", "required": false, "schema": { "type": [ - "string", + "array", "null" ], - "description": "Filter on the DevOps Platform setting id." + "items": { + "type": "string" + } } }, { - "name": "repositoryUrl", + "name": "issueTypes", "in": "query", - "description": "Filter on the repository URL.\nThis parameter can be in different formats, the traditional URL or the git remote URL (https or ssh).\n", + "description": "Limit results to issues with specific types (comma-separated).", "required": false, "schema": { "type": [ - "string", + "array", "null" ], - "description": "Filter on the repository URL.\nThis parameter can be in different formats, the traditional URL or the git remote URL (https or ssh).\n" + "items": { + "type": "string", + "enum": [ + "BUG", + "CODE_SMELL", + "SECURITY_HOTSPOT", + "VULNERABILITY" + ] + } } }, { - "name": "pageSize", + "name": "ruleKeys", "in": "query", - "description": "Number of results per page. A value of 0 will only return the pagination information.", + "description": "Comma separated list of ruleKeys", "required": false, "schema": { - "type": "integer", - "format": "int32", - "default": 50, - "description": "Number of results per page. A value of 0 will only return the pagination information.", - "maximum": 500, - "minimum": 0 + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } } }, { - "name": "pageIndex", + "name": "severities", "in": "query", - "description": "1-based page index", + "description": "Limit results to issues with specific severities (comma-separated).", "required": false, "schema": { - "type": "integer", - "format": "int32", - "default": 1, - "description": "1-based page index", - "exclusiveMinimum": 0, - "minimum": 1 + "type": [ + "array", + "null" + ], + "items": { + "type": "string", + "enum": [ + "BLOCKER", + "HIGH", + "INFO", + "LOW", + "MEDIUM" + ] + } + } + }, + { + "name": "sliceBy", + "in": "query", + "description": "Optionally group issue counts by a specific dimension. If not provided, issue counts will contain a single distribution with the count of all issues. ", + "required": false, + "schema": { + "type": [ + "string", + "null" + ], + "enum": [ + "RULE_KEY", + "SEVERITY", + "SOFTWARE_QUALITY", + "STATUS", + "TYPE" + ] + } + }, + { + "name": "statuses", + "in": "query", + "description": "Limit results to issues with specific statuses (comma-separated). Reviewed security hotspots also support SAFE and FIXED.", + "required": false, + "schema": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string", + "enum": [ + "ACCEPTED", + "CONFIRMED", + "FALSE_POSITIVE", + "FIXED", + "OPEN", + "REVIEWED", + "SAFE", + "TO_REVIEW" + ] + } } } ], "responses": { "200": { - "description": "OK", + "description": "Successful operation", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProjectBindingsSearchRestResponse" + "$ref": "#/components/schemas/IssueDensityHistoryResponse" } } } - } - }, - "x-sonar-internal": "true" - } - }, - "/dop-translation/project-bindings/{id}": { - "get": { - "tags": [ - "Dop Translation" - ], - "summary": "Fetch a single Project Binding", - "operationId": "getProjectBinding", - "parameters": [ - { - "name": "id", - "in": "path", - "description": "The id of the project-bindings to fetch.", - "required": true, - "schema": { - "type": "string" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } } - } - ], - "responses": { - "200": { - "description": "OK", + }, + "401": { + "description": "Unauthorized", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProjectBinding" + "$ref": "#/components/schemas/ErrorResponse" } } } - } - }, + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "TokenAuth": [] + } + ] + } + }, + "/history/issue-count-history": { + "get": { + "tags": [ + "issue-count-history" + ], + "summary": "Provides issue count history data for an entity", + "description": "Returns the history of issue counts for an entity. The maximum resolution for history data is 1 day. You cannot zoom in on a period less than 1 day (e.g. 1hour). All history is recorded in UTC timezone. Issue counts will carry-forward for days with no changes. ", + "operationId": "getIssueCountHistory", + "parameters": [ + { + "name": "entityId", + "in": "query", + "description": "The ID of the entity to retrieve issue count history for.", + "required": true, + "schema": { + "type": "string", + "maxLength": 36, + "minLength": 36 + } + }, + { + "name": "entityType", + "in": "query", + "description": "The entity's type.", + "required": true, + "schema": { + "type": "string", + "enum": [ + "PORTFOLIO", + "PROJECT_BRANCH", + "APPLICATION" + ] + } + }, + { + "name": "startDate", + "in": "query", + "description": "The date and time to begin returning issue count history in ISO 8601 format (inclusive). The start date may not be more than 1 year in the past. ", + "required": true, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "endDate", + "in": "query", + "description": "The date and time to stop returning issue count history in ISO 8601 format (inclusive). If not specified, the current date and time at UTC offset 0 will be used. ", + "required": false, + "schema": { + "type": [ + "string", + "null" + ], + "format": "date-time" + } + }, + { + "name": "impacts", + "in": "query", + "description": "Limit results to issues with specific impacts (comma-separated). An impact is a combination of software quality and severity. ", + "required": false, + "schema": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + } + }, + { + "name": "issueTypes", + "in": "query", + "description": "Limit results to issues with specific types (comma-separated).", + "required": false, + "schema": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string", + "enum": [ + "BUG", + "CODE_SMELL", + "SECURITY_HOTSPOT", + "VULNERABILITY" + ] + } + } + }, + { + "name": "ruleKeys", + "in": "query", + "description": "Comma separated list of ruleKeys", + "required": false, + "schema": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + } + }, + { + "name": "severities", + "in": "query", + "description": "Limit results to issues with specific severities (comma-separated).", + "required": false, + "schema": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string", + "enum": [ + "BLOCKER", + "HIGH", + "INFO", + "LOW", + "MEDIUM" + ] + } + } + }, + { + "name": "sliceBy", + "in": "query", + "description": "Optionally group issue counts by a specific dimension. If not provided, issue counts will contain a single distribution with the count of all issues. ", + "required": false, + "schema": { + "type": [ + "string", + "null" + ], + "enum": [ + "RULE_KEY", + "SEVERITY", + "SOFTWARE_QUALITY", + "STATUS", + "TYPE" + ] + } + }, + { + "name": "statuses", + "in": "query", + "description": "Limit results to issues with specific statuses (comma-separated). Reviewed security hotspots also support SAFE and FIXED.", + "required": false, + "schema": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string", + "enum": [ + "ACCEPTED", + "CONFIRMED", + "FALSE_POSITIVE", + "FIXED", + "OPEN", + "REVIEWED", + "SAFE", + "TO_REVIEW" + ] + } + } + } + ], + "responses": { + "200": { + "description": "Successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/IssueCountHistoryResponse" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "security": [ + { + "TokenAuth": [] + } + ] + } + }, + "/dop-translation/project-bindings": { + "get": { + "tags": [ + "Dop Translation" + ], + "summary": "Search across project bindings", + "operationId": "getProjectBindingByProjectId", + "parameters": [ + { + "name": "repository", + "in": "query", + "description": "Filter on the repository name.\nThis parameter performs an exact, case insensitive, match.\n", + "required": false, + "schema": { + "type": [ + "string", + "null" + ], + "description": "Filter on the repository name.\nThis parameter performs an exact, case insensitive, match.\n" + } + }, + { + "name": "dopSettingId", + "in": "query", + "description": "Filter on the DevOps Platform setting id.", + "required": false, + "schema": { + "type": [ + "string", + "null" + ], + "description": "Filter on the DevOps Platform setting id." + } + }, + { + "name": "repositoryUrl", + "in": "query", + "description": "Filter on the repository URL.\nThis parameter can be in different formats, the traditional URL or the git remote URL (https or ssh).\n", + "required": false, + "schema": { + "type": [ + "string", + "null" + ], + "description": "Filter on the repository URL.\nThis parameter can be in different formats, the traditional URL or the git remote URL (https or ssh).\n" + } + }, + { + "name": "pageSize", + "in": "query", + "description": "Number of results per page. A value of 0 will only return the pagination information.", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 50, + "description": "Number of results per page. A value of 0 will only return the pagination information.", + "maximum": 500, + "minimum": 0 + } + }, + { + "name": "pageIndex", + "in": "query", + "description": "1-based page index", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 1, + "description": "1-based page index", + "exclusiveMinimum": 0, + "minimum": 1 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProjectBindingsSearchRestResponse" + } + } + } + } + }, + "x-sonar-internal": "true" + } + }, + "/dop-translation/project-bindings/{id}": { + "get": { + "tags": [ + "Dop Translation" + ], + "summary": "Fetch a single Project Binding", + "operationId": "getProjectBinding", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The id of the project-bindings to fetch.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProjectBinding" + } + } + } + } + }, + "x-sonar-internal": "true" + } + }, + "/dop-translation/permission-checks": { + "get": { + "tags": [ + "Dop Translation" + ], + "summary": "Check DevOps Platform permissions for the Remediation Agent", + "description": "Validates whether the configured DevOps Platforms (GitHub, GitLab, Azure DevOps) grant the write permissions the\nSonarQube Remediation Agent needs to clone a repository, push a branch and open a pull/merge request. Without a\n'project' parameter it checks every configuration and requires the 'Administer System' permission or trusted\nprivileged-service authentication. With a 'project' parameter it checks the platform bound to that project and\nrequires 'Browse' permission on the project. Results are cached for a short time. Internal endpoint used by the\nRemediation Agent UI.\n", + "operationId": "checkDopPermissions", + "parameters": [ + { + "name": "project", + "in": "query", + "description": "Key of the project whose bound DevOps Platform should be checked", + "required": false, + "schema": { + "type": [ + "string", + "null" + ] + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PermissionChecksRestResponse" + } + } + } + } + }, "x-sonar-internal": "true" } }, - "/dop-translation/dop-settings": { + "/dop-translation/dop-settings": { + "get": { + "tags": [ + "Dop Translation" + ], + "summary": "List all DevOps Platform Integration settings", + "description": "Requires the 'Create Projects' permission ", + "operationId": "fetchAllDopSettings", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DopSettingsRestResponse" + } + } + } + } + } + } + }, + "/dashboards/built-ins": { + "get": { + "tags": [ + "built-in-dashboards" + ], + "summary": "List built-in dashboards", + "operationId": "listBuiltInDashboards", + "parameters": [ + { + "name": "q", + "in": "query", + "required": false, + "schema": { + "type": [ + "string", + "null" + ] + } + }, + { + "name": "resourceType", + "in": "query", + "required": false, + "schema": { + "type": [ + "string", + "null" + ], + "enum": [ + "project", + "portfolio" + ] + } + }, + { + "name": "pageIndex", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 1, + "minimum": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 50, + "maximum": 5000, + "minimum": 0 + } + } + ], + "responses": { + "200": { + "description": "Successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BuiltInDashboardsResponse" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "501": { + "description": "Not Implemented", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/dashboards/built-ins/{key}": { "get": { "tags": [ - "Dop Translation" + "built-in-dashboards" + ], + "summary": "Get a built-in dashboard", + "operationId": "getBuiltInDashboard", + "parameters": [ + { + "name": "key", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } ], - "summary": "List all DevOps Platform Integration settings", - "description": "Requires the 'Create Projects' permission ", - "operationId": "fetchAllDopSettings", "responses": { "200": { - "description": "OK", + "description": "Successful operation", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DopSettingsRestResponse" + "$ref": "#/components/schemas/BuiltInDashboardResponse" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "429": { + "description": "Too Many Requests", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "501": { + "description": "Not Implemented", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" } } } @@ -2649,7 +3919,137 @@ "description": "Project Key", "required": true, "schema": { - "type": "string" + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ActiveRule" + } + } + } + } + } + } + } + }, + "/agentic/jobs": { + "get": { + "tags": [ + "Agentic" + ], + "summary": "Search agentic jobs", + "description": "List agentic orchestration jobs (Hunter and Remediation agents), most recent first.\nRequires system administration permission.\n", + "operationId": "search_3", + "parameters": [ + { + "name": "id", + "in": "query", + "description": "Comma-separated list of job ids to filter on.", + "required": false, + "schema": { + "type": [ + "string", + "null" + ], + "description": "Comma-separated list of job ids to filter on." + } + }, + { + "name": "status", + "in": "query", + "description": "Comma-separated list of statuses to filter on.", + "required": false, + "schema": { + "type": [ + "string", + "null" + ], + "description": "Comma-separated list of statuses to filter on.", + "enum": [ + "PENDING", + "IN_PROGRESS", + "COMPLETED", + "FAILED" + ] + } + }, + { + "name": "type", + "in": "query", + "description": "Comma-separated list of agent types to filter on.", + "required": false, + "schema": { + "type": [ + "string", + "null" + ], + "description": "Comma-separated list of agent types to filter on.", + "enum": [ + "HUNTER", + "REMEDIATION" + ] + } + }, + { + "name": "createdAfter", + "in": "query", + "description": "Only return jobs created on or after this date (or date-time), e.g. 2026-01-01 or 2026-01-01T12:00:00+0000.", + "required": false, + "schema": { + "type": [ + "string", + "null" + ], + "description": "Only return jobs created on or after this date (or date-time), e.g. 2026-01-01 or 2026-01-01T12:00:00+0000." + } + }, + { + "name": "createdBefore", + "in": "query", + "description": "Only return jobs created on or before this date (or date-time), e.g. 2026-01-01 or 2026-01-01T12:00:00+0000.", + "required": false, + "schema": { + "type": [ + "string", + "null" + ], + "description": "Only return jobs created on or before this date (or date-time), e.g. 2026-01-01 or 2026-01-01T12:00:00+0000." + } + }, + { + "name": "pageSize", + "in": "query", + "description": "Number of results per page. A value of 0 will only return the pagination information.", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 50, + "description": "Number of results per page. A value of 0 will only return the pagination information.", + "maximum": 500, + "minimum": 0 + } + }, + { + "name": "pageIndex", + "in": "query", + "description": "1-based page index", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 1, + "description": "1-based page index", + "exclusiveMinimum": 0, + "minimum": 1 } } ], @@ -2659,10 +4059,7 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ActiveRule" - } + "$ref": "#/components/schemas/AgenticJobsSearchRestResponse" } } } @@ -3112,6 +4509,23 @@ } } }, + "ScmAccessTokenRestResponse": { + "type": "object", + "properties": { + "alm": { + "type": "string" + }, + "username": { + "type": "string" + }, + "secret": { + "type": "string" + }, + "expiresAt": { + "type": "string" + } + } + }, "GitlabConfigurationCreateRestRequest": { "type": "object", "properties": { @@ -3568,7 +4982,7 @@ ] }, "RuleDescriptionSectionContextRestResponse": { - "type": "null", + "type": "object", "properties": { "key": { "type": "string" @@ -4152,95 +5566,384 @@ "type": "integer", "format": "int32" } - } + } + }, + "UsersSearchRestResponse": { + "type": "object", + "properties": { + "users": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UserRestResponseForAdmins" + } + }, + "page": { + "$ref": "#/components/schemas/PageRestResponse" + } + } + }, + "DatabaseMigrationsResponse": { + "type": "object", + "properties": { + "status": { + "type": "string" + }, + "completedSteps": { + "type": [ + "integer", + "null" + ], + "format": "int32" + }, + "totalSteps": { + "type": [ + "integer", + "null" + ], + "format": "int32" + }, + "startedAt": { + "type": [ + "string", + "null" + ] + }, + "message": { + "type": [ + "string", + "null" + ] + }, + "expectedFinishTimestamp": { + "type": [ + "string", + "null" + ] + } + } + }, + "Health": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "GREEN", + "YELLOW", + "RED" + ] + }, + "causes": { + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } + } + }, + "EmailConfigurationSearchRestResponse": { + "type": "object", + "properties": { + "emailConfigurations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EmailConfigurationResource" + } + }, + "page": { + "$ref": "#/components/schemas/PageRestResponse" + } + } + }, + "DevopsPlatforms": { + "type": "object", + "properties": { + "total": { + "type": "integer", + "format": "int64" + }, + "shares": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PlatformShare" + } + } + }, + "required": [ + "shares", + "total" + ] + }, + "OnboardingStatisticsResponse": { + "type": "object", + "properties": { + "discoveredTotal": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "timeline": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TimelinePoint" + } + }, + "devopsPlatforms": { + "$ref": "#/components/schemas/DevopsPlatforms" + } + }, + "required": [ + "devopsPlatforms", + "timeline" + ] + }, + "PlatformShare": { + "type": "object", + "properties": { + "platform": { + "type": "string" + }, + "count": { + "type": "integer", + "format": "int64" + }, + "percentage": { + "type": "number", + "format": "double" + } + }, + "required": [ + "count", + "percentage", + "platform" + ] + }, + "TimelinePoint": { + "type": "object", + "properties": { + "date": { + "type": "string", + "format": "date-time" + }, + "repositoriesImported": { + "type": "integer", + "format": "int64" + }, + "projectsScanned": { + "type": "integer", + "format": "int64" + } + }, + "required": [ + "date", + "projectsScanned", + "repositoriesImported" + ] + }, + "OnboardingProjectRow": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "path": { + "type": [ + "string", + "null" + ] + }, + "alm": { + "type": [ + "string", + "null" + ] + }, + "scanStatus": { + "type": "string", + "enum": [ + "SCANNED", + "NOT_SCANNED" + ] + }, + "analysisMode": { + "type": "string", + "enum": [ + "CI", + "AUTOMATIC", + "NONE" + ] + }, + "lastScan": { + "type": [ + "integer", + "null" + ], + "format": "int64" + } + }, + "required": [ + "analysisMode", + "key", + "name", + "scanStatus" + ] + }, + "OnboardingProjectsPage": { + "type": "object", + "properties": { + "pageIndex": { + "type": "integer", + "format": "int32" + }, + "pageSize": { + "type": "integer", + "format": "int32" + }, + "total": { + "type": "integer", + "format": "int64" + } + }, + "required": [ + "pageIndex", + "pageSize", + "total" + ] + }, + "OnboardingProjectsResponse": { + "type": "object", + "properties": { + "page": { + "$ref": "#/components/schemas/OnboardingProjectsPage" + }, + "projects": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OnboardingProjectRow" + } + } + }, + "required": [ + "page", + "projects" + ] + }, + "DevopsPlatformsStep": { + "type": "object", + "properties": { + "configured": { + "type": "integer", + "format": "int64" + } + }, + "required": [ + "configured" + ] + }, + "OnboardingOverviewResponse": { + "type": "object", + "properties": { + "progressPct": { + "type": "number", + "format": "double" + }, + "steps": { + "$ref": "#/components/schemas/OnboardingSteps" + } + }, + "required": [ + "progressPct", + "steps" + ] }, - "UsersSearchRestResponse": { + "OnboardingSteps": { "type": "object", "properties": { - "users": { - "type": "array", - "items": { - "$ref": "#/components/schemas/UserRestResponseForAdmins" - } + "devopsPlatforms": { + "$ref": "#/components/schemas/DevopsPlatformsStep" }, - "page": { - "$ref": "#/components/schemas/PageRestResponse" + "repositories": { + "$ref": "#/components/schemas/RepositoriesStep" + }, + "projects": { + "$ref": "#/components/schemas/ProjectsStep" } - } + }, + "required": [ + "devopsPlatforms", + "projects", + "repositories" + ] }, - "DatabaseMigrationsResponse": { + "ProjectsStep": { "type": "object", "properties": { - "status": { - "type": "string" + "analyzed": { + "type": "integer", + "format": "int64" }, - "completedSteps": { + "notScanned": { + "type": "integer", + "format": "int64" + }, + "notImported": { "type": [ "integer", "null" ], - "format": "int32" + "format": "int64" }, - "totalSteps": { + "total": { "type": [ "integer", "null" ], - "format": "int32" - }, - "startedAt": { - "type": [ - "string", - "null" - ] - }, - "message": { - "type": [ - "string", - "null" - ] + "format": "int64" }, - "expectedFinishTimestamp": { + "percent": { "type": [ - "string", + "number", "null" - ] + ], + "format": "double" } - } + }, + "required": [ + "analyzed", + "notScanned" + ] }, - "Health": { + "RepositoriesStep": { "type": "object", "properties": { - "status": { - "type": "string", - "enum": [ - "GREEN", - "YELLOW", - "RED" - ] + "imported": { + "type": "integer", + "format": "int64" }, - "causes": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - } - } - }, - "EmailConfigurationSearchRestResponse": { - "type": "object", - "properties": { - "emailConfigurations": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EmailConfigurationResource" - } + "discovered": { + "type": [ + "integer", + "null" + ], + "format": "int64" }, - "page": { - "$ref": "#/components/schemas/PageRestResponse" + "percent": { + "type": [ + "number", + "null" + ], + "format": "double" } - } + }, + "required": [ + "imported" + ] }, "Pagination": { "type": "object", @@ -4361,13 +6064,80 @@ "type": "object", "properties": { "message": { + "type": "string" + } + }, + "required": [ + "message" + ] + }, + "ProjectIssueResolution": { + "type": "object", + "properties": { + "branchId": { + "type": "string" + }, + "projectName": { + "type": "string", + "example": "My Project" + }, + "projectKey": { + "type": "string", + "example": "my-project" + }, + "branchName": { "type": [ "string", "null" ], - "description": "Error message" + "example": "develop" + }, + "value": { + "type": "integer", + "format": "int64" + }, + "trendPercentage": { + "type": [ + "number", + "null" + ], + "format": "double", + "description": "Percentage change in value relative to trendSince." } - } + }, + "required": [ + "branchId", + "projectKey", + "projectName", + "value" + ] + }, + "ProjectIssueResolutionResponse": { + "type": "object", + "properties": { + "statistic": { + "type": "string", + "enum": [ + "MTTR", + "RECENT_MTTR", + "RESOLVED_ISSUES" + ] + }, + "projectIssueResolution": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProjectIssueResolution" + } + }, + "page": { + "$ref": "#/components/schemas/Pagination" + } + }, + "required": [ + "page", + "projectIssueResolution", + "statistic" + ] }, "ProjectIssueCount": { "type": "object", @@ -4433,36 +6203,145 @@ "projectIssueCounts" ] }, - "MeasureHistoryEntry": { + "MeasureHistoryEntry": { + "type": "object", + "properties": { + "metric": { + "type": [ + "string", + "null" + ], + "description": "Metric whose value is being recorded", + "example": "coverage" + }, + "type": { + "type": [ + "string", + "null" + ], + "description": "Type of value the measure contains", + "example": "PERCENT" + }, + "value": { + "type": [ + "string", + "null" + ], + "description": "Value of the measure", + "example": "81.2" + } + } + }, + "MeasureHistoryItem": { + "type": "object", + "properties": { + "date": { + "type": [ + "string", + "null" + ], + "format": "date-time", + "description": "The date the measures were taken" + }, + "measures": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MeasureHistoryEntry" + } + } + } + }, + "MeasuresHistoryResponse": { + "type": "object", + "properties": { + "measuresHistory": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MeasureHistoryItem" + } + } + } + }, + "IssueResolutionDistribution": { + "type": "object", + "properties": { + "key": { + "type": [ + "string", + "null" + ] + }, + "value": { + "type": [ + "integer", + "null" + ], + "format": "int32" + } + } + }, + "IssueResolutionHistory": { + "type": "object", + "properties": { + "date": { + "type": [ + "string", + "null" + ], + "format": "date-time", + "description": "Date and time in ISO 8601 format, including timezone offset", + "example": "2024-01-01T12:00Z" + }, + "distribution": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IssueResolutionDistribution" + } + } + } + }, + "IssueResolutionHistoryResponse": { + "type": "object", + "properties": { + "statistic": { + "type": "string", + "enum": [ + "MTTR", + "RECENT_MTTR", + "RESOLVED_ISSUES" + ] + }, + "issueResolutionHistory": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IssueResolutionHistory" + } + } + }, + "required": [ + "issueResolutionHistory", + "statistic" + ] + }, + "IssueDensityDistribution": { "type": "object", "properties": { - "metric": { - "type": [ - "string", - "null" - ], - "description": "Metric whose value is being recorded", - "example": "coverage" - }, - "type": { + "key": { "type": [ "string", "null" - ], - "description": "Type of value the measure contains", - "example": "PERCENT" + ] }, "value": { "type": [ - "string", + "number", "null" ], - "description": "Value of the measure", - "example": "81.2" + "format": "double" } } }, - "MeasureHistoryItem": { + "IssueDensityHistory": { "type": "object", "properties": { "date": { @@ -4471,23 +6350,24 @@ "null" ], "format": "date-time", - "description": "The date the measures were taken" + "description": "Date and time in ISO 8601 format, including timezone offset", + "example": "2024-01-01T12:00Z" }, - "measures": { + "distribution": { "type": "array", "items": { - "$ref": "#/components/schemas/MeasureHistoryEntry" + "$ref": "#/components/schemas/IssueDensityDistribution" } } } }, - "MeasuresHistoryResponse": { + "IssueDensityHistoryResponse": { "type": "object", "properties": { - "measuresHistory": { + "issueDensityHistory": { "type": "array", "items": { - "$ref": "#/components/schemas/MeasureHistoryItem" + "$ref": "#/components/schemas/IssueDensityHistory" } } } @@ -4590,6 +6470,47 @@ } } }, + "PermissionCheckResource": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "Key of the DevOps Platform configuration", + "readOnly": true + }, + "type": { + "type": "string", + "description": "DevOps Platform type of the configuration (github, gitlab, azure)" + }, + "status": { + "type": "string", + "description": "Whether the configuration grants the write permissions the Remediation Agent needs to clone and open pull requests", + "enum": [ + "SUFFICIENT", + "INSUFFICIENT", + "UNKNOWN", + "CHECK_FAILED" + ] + }, + "checkedAt": { + "type": "integer", + "format": "int64", + "description": "Epoch milliseconds when this status was last computed. May reflect a cached result.", + "readOnly": true + } + } + }, + "PermissionChecksRestResponse": { + "type": "object", + "properties": { + "permissionChecks": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PermissionCheckResource" + } + } + } + }, "GitlabConfigurationSearchRestResponse": { "type": "object", "properties": { @@ -4660,6 +6581,126 @@ } } }, + "BuiltInDashboardItem": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "name": { + "type": "string", + "maxLength": 300, + "minLength": 0 + }, + "description": { + "type": [ + "string", + "null" + ], + "maxLength": 500, + "minLength": 0 + }, + "updatedAt": { + "type": "integer", + "format": "int64" + }, + "resourceType": { + "type": "string", + "enum": [ + "project", + "portfolio" + ] + } + }, + "required": [ + "key", + "name", + "resourceType", + "updatedAt" + ] + }, + "BuiltInDashboardsResponse": { + "type": "object", + "properties": { + "dashboards": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BuiltInDashboardItem" + } + }, + "page": { + "$ref": "#/components/schemas/DashboardPage" + } + }, + "required": [ + "dashboards", + "page" + ] + }, + "DashboardPage": { + "type": "object", + "properties": { + "pageIndex": { + "type": "integer", + "format": "int32" + }, + "pageSize": { + "type": "integer", + "format": "int32" + }, + "total": { + "type": "integer", + "format": "int32" + } + }, + "required": [ + "pageIndex", + "pageSize", + "total" + ] + }, + "BuiltInDashboardResponse": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "name": { + "type": "string", + "maxLength": 300, + "minLength": 0 + }, + "layout": { + "type": "string" + }, + "description": { + "type": [ + "string", + "null" + ], + "maxLength": 500, + "minLength": 0 + }, + "updatedAt": { + "type": "integer", + "format": "int64" + }, + "resourceType": { + "type": "string", + "enum": [ + "project", + "portfolio" + ] + } + }, + "required": [ + "key", + "layout", + "name", + "resourceType", + "updatedAt" + ] + }, "GroupsSearchRestResponse": { "type": "object", "properties": { @@ -4806,6 +6847,142 @@ "type": "string" } } + }, + "AgenticJobRestResponse": { + "type": "object", + "properties": { + "id": { + "type": "string", + "readOnly": true + }, + "projectId": { + "type": "string", + "readOnly": true + }, + "projectKey": { + "type": [ + "string", + "null" + ], + "description": "Not set if the project has been deleted.", + "readOnly": true + }, + "projectName": { + "type": [ + "string", + "null" + ], + "description": "Not set if the project has been deleted.", + "readOnly": true + }, + "type": { + "type": "string", + "enum": [ + "HUNTER", + "REMEDIATION" + ], + "readOnly": true + }, + "analysisType": { + "type": "string", + "enum": [ + "FULL", + "INCREMENTAL" + ], + "readOnly": true + }, + "status": { + "type": "string", + "enum": [ + "PENDING", + "IN_PROGRESS", + "COMPLETED", + "FAILED" + ], + "readOnly": true + }, + "branch": { + "type": [ + "string", + "null" + ], + "readOnly": true + }, + "repositoryUrl": { + "type": "string", + "readOnly": true + }, + "revision": { + "type": [ + "string", + "null" + ], + "readOnly": true + }, + "workflowType": { + "type": [ + "string", + "null" + ], + "readOnly": true + }, + "findingsCount": { + "type": [ + "integer", + "null" + ], + "format": "int32", + "readOnly": true + }, + "createdAt": { + "type": "integer", + "format": "int64", + "readOnly": true + }, + "updatedAt": { + "type": "integer", + "format": "int64", + "readOnly": true + }, + "startedAt": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "readOnly": true + }, + "finishedAt": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "readOnly": true + }, + "failureReason": { + "type": [ + "string", + "null" + ], + "description": "Only set when status is FAILED.", + "readOnly": true + } + } + }, + "AgenticJobsSearchRestResponse": { + "type": "object", + "properties": { + "jobs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AgenticJobRestResponse" + } + }, + "page": { + "$ref": "#/components/schemas/PageRestResponse" + } + } } } } diff --git a/go.mod b/go.mod index a2b2c0e..0b22b1e 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.26.7 require ( github.com/google/go-querystring v1.2.0 - github.com/onsi/ginkgo/v2 v2.33.0 + github.com/onsi/ginkgo/v2 v2.33.1 github.com/onsi/gomega v1.44.0 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 diff --git a/go.sum b/go.sum index df56ed6..170a35c 100644 --- a/go.sum +++ b/go.sum @@ -32,8 +32,8 @@ github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= -github.com/onsi/ginkgo/v2 v2.33.0 h1:C8gBA6Uc2ZEubiV+SXiu5tZnMTwEmXHgkJwGozKtZf8= -github.com/onsi/ginkgo/v2 v2.33.0/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= +github.com/onsi/ginkgo/v2 v2.33.1 h1:62XfWJbEIjPSCSDYrsCPP/SHcmH7O0M8ne8zfqQ2iz4= +github.com/onsi/ginkgo/v2 v2.33.1/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= github.com/onsi/gomega v1.44.0 h1:eAiGl3Pw5jz5GQdDff0BcxYpAX1JxW8xD7mFUuwNfZQ= github.com/onsi/gomega v1.44.0/go.mod h1:e/C2HwaZ1DhvjzXXuFhcR7hY7Sh9pl7MmoWKEjzwcdA= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= diff --git a/integration_testing/community_26_9_services_test.go b/integration_testing/community_26_9_services_test.go new file mode 100644 index 0000000..7b01e77 --- /dev/null +++ b/integration_testing/community_26_9_services_test.go @@ -0,0 +1,226 @@ +package integration_testing_test + +import ( + "context" + "net/http" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/boxboxjason/sonarqube-client-go/v2/integration_testing/helpers" + "github.com/boxboxjason/sonarqube-client-go/v2/sonar" +) + +// Specs for the endpoints that SonarQube Community Build 26.9 gained from +// Enterprise Edition 2026.5. They assert the behavior shared by both editions, +// so they run unchanged against either one. +var _ = Describe("SonarQube 26.9 Community Build services", Ordered, func() { + var ( + client *sonar.Client + cleanup *helpers.CleanupManager + projectKey string + branchID string + startDate string + ) + + // A syntactically valid (but non-existent) 36-character entity UUID. + const dummyEntityID = "00000000-0000-0000-0000-000000000000" + + BeforeAll(func() { + var err error + client, err = helpers.NewDefaultClient() + Expect(err).NotTo(HaveOccurred()) + cleanup = helpers.NewCleanupManager(client) + + projectKey = helpers.UniqueResourceName("ce-26-9") + _, _, err = client.Projects.Create(context.Background(), &sonar.ProjectsCreateOptions{Name: projectKey, Project: projectKey}) + Expect(err).NotTo(HaveOccurred()) + cleanup.RegisterCleanup("project", projectKey, func() error { + _, err := client.Projects.Delete(context.Background(), &sonar.ProjectsDeleteOptions{Project: projectKey}) + return err + }) + + branches, _, err := client.ProjectBranches.List(context.Background(), &sonar.ProjectBranchesListOptions{Project: projectKey}) + Expect(err).NotTo(HaveOccurred()) + Expect(branches.Branches).NotTo(BeEmpty()) + branchID = branches.Branches[0].BranchID + + startDate = time.Now().UTC().AddDate(0, 0, -7).Format(time.RFC3339) + }) + + AfterAll(func() { + for _, err := range cleanup.Cleanup() { + GinkgoWriter.Printf("Cleanup error: %v\n", err) + } + }) + + // ========================================================================= + // Built-in dashboards + // ========================================================================= + It("should list built-in dashboards and get one", func() { + list, resp, err := client.V2.Dashboards.ListBuiltIn(context.Background(), &sonar.DashboardsListBuiltInOptions{ResourceType: "project"}) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + Expect(list.Dashboards).NotTo(BeEmpty()) + + builtIn, _, err := client.V2.Dashboards.GetBuiltIn(context.Background(), list.Dashboards[0].Key) + Expect(err).NotTo(HaveOccurred()) + Expect(builtIn.Key).To(Equal(list.Dashboards[0].Key)) + Expect(builtIn.Layout).NotTo(BeEmpty()) + }) + + // ========================================================================= + // History + // ========================================================================= + Describe("History", func() { + It("should return issue density history for a branch", func() { + result, resp, err := client.V2.History.GetIssueDensityHistory(context.Background(), &sonar.HistoryGetIssueDensityHistoryOptions{ + EntityId: branchID, EntityType: "PROJECT_BRANCH", StartDate: startDate, SliceBy: "SEVERITY", + }) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + Expect(result).NotTo(BeNil()) + }) + + It("should return issue resolution history for a branch", func() { + for _, statistic := range []string{"MTTR", "RECENT_MTTR", "RESOLVED_ISSUES"} { + result, resp, err := client.V2.History.GetIssueResolutionHistory(context.Background(), &sonar.HistoryGetIssueResolutionHistoryOptions{ + EntityId: branchID, EntityType: "PROJECT_BRANCH", Statistic: statistic, StartDate: startDate, + }) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + Expect(result.Statistic).To(Equal(statistic)) + } + }) + + It("should accept type severities on issue count history", func() { + result, resp, err := client.V2.History.GetIssueCountHistory(context.Background(), &sonar.HistoryIssueCountHistoryOptions{ + EntityID: branchID, EntityType: sonar.HistoryEntityTypeProjectBranch, StartDate: startDate, TypeSeverities: []string{"MAJOR"}, + }) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + Expect(result).NotTo(BeNil()) + }) + + It("should accept the APPLICATION entity type on measures history", func() { + _, resp, err := client.V2.History.GetMeasuresHistory(context.Background(), &sonar.HistoryMeasuresHistoryOptions{ + EntityType: sonar.HistoryEntityTypeApplication, EntityID: dummyEntityID, MetricKeys: []string{"ncloc"}, StartDate: startDate, + }) + Expect(err).To(HaveOccurred()) + Expect(resp).NotTo(BeNil()) + Expect(resp.StatusCode).To(Equal(http.StatusNotFound)) + }) + + It("should reject an unknown slice dimension client-side", func() { + result, resp, err := client.V2.History.GetIssueCountHistory(context.Background(), &sonar.HistoryIssueCountHistoryOptions{ + EntityID: branchID, EntityType: sonar.HistoryEntityTypeProjectBranch, StartDate: startDate, SliceBy: "issueType", + }) + Expect(err).To(HaveOccurred()) + Expect(resp).To(BeNil()) + Expect(result).To(BeNil()) + }) + + It("should return not found for the project issue resolution of an unknown portfolio", func() { + _, resp, err := client.V2.History.GetProjectIssueResolution(context.Background(), &sonar.HistoryGetProjectIssueResolutionOptions{ + Statistic: "MTTR", PortfolioId: helpers.UniqueResourceName("portfolio"), + }) + Expect(err).To(HaveOccurred()) + Expect(resp).NotTo(BeNil()) + Expect(resp.StatusCode).To(Equal(http.StatusNotFound)) + }) + }) + + // ========================================================================= + // Onboarding + // ========================================================================= + It("should return the onboarding overview, projects and statistics", func() { + overview, resp, err := client.V2.Onboarding.GetOverview(context.Background(), nil) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + Expect(overview).NotTo(BeNil()) + + projects, _, err := client.V2.Onboarding.GetProjects(context.Background(), &sonar.OnboardingGetProjectsOptions{Q: projectKey}) + Expect(err).NotTo(HaveOccurred()) + Expect(projects.Projects).To(HaveLen(1)) + Expect(projects.Projects[0].Key).To(Equal(projectKey)) + Expect(projects.Projects[0].ScanStatus).To(Equal("NOT_SCANNED")) + + stats, _, err := client.V2.Onboarding.GetStatistics(context.Background(), nil) + Expect(err).NotTo(HaveOccurred()) + Expect(stats.DevopsPlatforms.Total).To(BeNumerically(">", 0)) + }) + + // ========================================================================= + // Agentic jobs + // ========================================================================= + It("should search agentic jobs", func() { + result, resp, err := client.V2.Agentic.SearchJobs(context.Background(), &sonar.AgenticSearchJobsOptions{Type: "HUNTER"}) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + Expect(result).NotTo(BeNil()) + }) + + // ========================================================================= + // DevOps Platform permissions and SCM access tokens + // ========================================================================= + Describe("DevOps Platform permissions", func() { + It("should check DevOps Platform permissions", func() { + result, resp, err := client.V2.DopTranslation.CheckPermissions(context.Background(), nil) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + Expect(result).NotTo(BeNil()) + + result, _, err = client.V2.DopTranslation.CheckPermissions(context.Background(), &sonar.DopTranslationCheckPermissionsOptions{Project: projectKey}) + Expect(err).NotTo(HaveOccurred()) + Expect(result).NotTo(BeNil()) + }) + + It("should reject a refresh without a configuration client-side", func() { + result, resp, err := client.V2.DopTranslation.CheckPermissions(context.Background(), &sonar.DopTranslationCheckPermissionsOptions{Refresh: new(true)}) + Expect(err).To(HaveOccurred()) + Expect(resp).To(BeNil()) + Expect(result).To(BeNil()) + }) + + It("should not mint an SCM access token for an unbound project", func() { + _, resp, err := client.V2.DopTranslation.GenerateScmAccessToken(context.Background(), &sonar.DopTranslationGenerateScmAccessTokenOptions{Project: projectKey}) + Expect(err).To(HaveOccurred()) + Expect(resp).NotTo(BeNil()) + Expect(resp.StatusCode).To(Equal(http.StatusNotFound)) + }) + }) + + // ========================================================================= + // Hotspots migration + // ========================================================================= + Describe("Hotspots migration", func() { + It("should report the migration status of the instance", func() { + result, resp, err := client.Hotspots.MigrationStatus(context.Background(), nil) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + Expect(result).NotTo(BeNil()) + Expect(result.RemainingHotspots).To(BeNumerically(">=", 0)) + }) + + It("should report a complete migration for a project without hotspots", func() { + result, resp, err := client.Hotspots.MigrationStatus(context.Background(), &sonar.HotspotsMigrationStatusOptions{Project: projectKey}) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + Expect(result.RemainingHotspots).To(BeZero()) + Expect(result.Complete).To(BeTrue()) + }) + + It("should dry-run a migration without writing anything", func() { + result, resp, err := client.Hotspots.MigrateToIssues(context.Background(), &sonar.HotspotsMigrateToIssuesOptions{ + Project: projectKey, + DryRun: true, + }) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.StatusCode).To(Equal(http.StatusOK)) + Expect(result.DryRun).To(BeTrue()) + Expect(result.Skipped).To(BeZero()) + Expect(result.Projects).To(BeEmpty()) + }) + }) +}) diff --git a/integration_testing/enterprise/hotspots_notifications_test.go b/integration_testing/enterprise/notifications_groups_test.go similarity index 58% rename from integration_testing/enterprise/hotspots_notifications_test.go rename to integration_testing/enterprise/notifications_groups_test.go index abe0670..e5f4b94 100644 --- a/integration_testing/enterprise/hotspots_notifications_test.go +++ b/integration_testing/enterprise/notifications_groups_test.go @@ -11,63 +11,6 @@ import ( "github.com/boxboxjason/sonarqube-client-go/v2/sonar" ) -var _ = Describe("Hotspots migration", Ordered, func() { - var ( - client *sonar.Client - cleanup *helpers.CleanupManager - projectKey string - ) - - BeforeAll(func() { - var err error - client, err = helpers.NewDefaultClient() - Expect(err).NotTo(HaveOccurred()) - cleanup = helpers.NewCleanupManager(client) - - projectKey = helpers.UniqueResourceName("hotspots-migration") - _, _, err = client.Projects.Create(context.Background(), &sonar.ProjectsCreateOptions{Name: projectKey, Project: projectKey}) - Expect(err).NotTo(HaveOccurred()) - cleanup.RegisterCleanup("project", projectKey, func() error { - _, err := client.Projects.Delete(context.Background(), &sonar.ProjectsDeleteOptions{Project: projectKey}) - return err - }) - }) - - AfterAll(func() { - for _, err := range cleanup.Cleanup() { - GinkgoWriter.Printf("Cleanup error: %v\n", err) - } - }) - - It("should report the migration status of the instance", func() { - result, resp, err := client.Hotspots.MigrationStatus(context.Background(), nil) - Expect(err).NotTo(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(result).NotTo(BeNil()) - Expect(result.RemainingHotspots).To(BeNumerically(">=", 0)) - }) - - It("should report a complete migration for a project without hotspots", func() { - result, resp, err := client.Hotspots.MigrationStatus(context.Background(), &sonar.HotspotsMigrationStatusOptions{Project: projectKey}) - Expect(err).NotTo(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(result.RemainingHotspots).To(BeZero()) - Expect(result.Complete).To(BeTrue()) - }) - - It("should dry-run a migration without writing anything", func() { - result, resp, err := client.Hotspots.MigrateToIssues(context.Background(), &sonar.HotspotsMigrateToIssuesOptions{ - Project: projectKey, - DryRun: true, - }) - Expect(err).NotTo(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(result.DryRun).To(BeTrue()) - Expect(result.Skipped).To(BeZero()) - Expect(result.Projects).To(BeEmpty()) - }) -}) - var _ = Describe("Notifications group subscriptions", Ordered, func() { const notificationType = "security-alert-raised" diff --git a/integration_testing/enterprise/v2_2026_5_services_test.go b/integration_testing/enterprise/v2_2026_5_services_test.go index 9f11ee3..8061b6e 100644 --- a/integration_testing/enterprise/v2_2026_5_services_test.go +++ b/integration_testing/enterprise/v2_2026_5_services_test.go @@ -220,7 +220,7 @@ var _ = Describe("SonarQube 2026.5 V2 services", Ordered, func() { }) // ========================================================================= - // Software quality reports, onboarding, security alerts, agentic jobs + // Software quality reports, security alerts // ========================================================================= It("should return a WCAG compliance report with nested categories", func() { report, resp, err := client.V2.SoftwareQualityReports.GetComplianceReport(context.Background(), &sonar.SoftwareQualityReportsGetComplianceReportOptions{ @@ -232,23 +232,6 @@ var _ = Describe("SonarQube 2026.5 V2 services", Ordered, func() { Expect(report.Categories[0].Categories).NotTo(BeEmpty()) }) - It("should return the onboarding overview, projects and statistics", func() { - overview, resp, err := client.V2.Onboarding.GetOverview(context.Background(), nil) - Expect(err).NotTo(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(overview).NotTo(BeNil()) - - projects, _, err := client.V2.Onboarding.GetProjects(context.Background(), &sonar.OnboardingGetProjectsOptions{Q: projectKey}) - Expect(err).NotTo(HaveOccurred()) - Expect(projects.Projects).To(HaveLen(1)) - Expect(projects.Projects[0].Key).To(Equal(projectKey)) - Expect(projects.Projects[0].ScanStatus).To(Equal("NOT_SCANNED")) - - stats, _, err := client.V2.Onboarding.GetStatistics(context.Background(), nil) - Expect(err).NotTo(HaveOccurred()) - Expect(stats.DevopsPlatforms.Total).To(BeNumerically(">", 0)) - }) - It("should search security alerts", func() { result, resp, err := client.V2.SecurityAlerts.Search(context.Background(), &sonar.SecurityAlertsSearchOptions{ AlertTypes: []string{"DEPENDENCY_RISK"}, Statuses: []string{"OPEN"}, Sort: "FIRST_DETECTED_AT", Direction: "ASC", @@ -262,25 +245,6 @@ var _ = Describe("SonarQube 2026.5 V2 services", Ordered, func() { Expect(resp.StatusCode).To(Equal(http.StatusNotFound)) }) - It("should search agentic jobs", func() { - result, resp, err := client.V2.Agentic.SearchJobs(context.Background(), &sonar.AgenticSearchJobsOptions{Type: "HUNTER"}) - Expect(err).NotTo(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(result).NotTo(BeNil()) - }) - - It("should check DevOps Platform permissions", func() { - result, resp, err := client.V2.DopTranslation.CheckPermissions(context.Background(), &sonar.DopTranslationCheckPermissionsOptions{}) - Expect(err).NotTo(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusOK)) - Expect(result).NotTo(BeNil()) - - // The project is not bound to any DevOps Platform, so no token can be minted. - _, resp, err = client.V2.DopTranslation.GenerateScmAccessToken(context.Background(), &sonar.DopTranslationGenerateScmAccessTokenOptions{Project: projectKey}) - Expect(err).To(HaveOccurred()) - Expect(resp.StatusCode).To(Equal(http.StatusNotFound)) - }) - // ========================================================================= // LLM connectivity // ========================================================================= diff --git a/sonar/agentic_v2_service.go b/sonar/agentic_v2_service.go index a8f5461..06fa03b 100644 --- a/sonar/agentic_v2_service.go +++ b/sonar/agentic_v2_service.go @@ -8,7 +8,8 @@ import ( ) // AgenticService handles communication with the Agentic orchestration jobs (Hunter and Remediation agents) related methods of the -// SonarQube V2 API. This service is only available in Enterprise Edition. +// SonarQube V2 API. Job search is also available in Community Build since 26.9; +// downloading job logs is only available in Enterprise Edition. type AgenticService struct { // client is used to communicate with the SonarQube API. client *Client @@ -169,7 +170,7 @@ func (s *AgenticService) ValidateDownloadJobLogsOpt(opt *AgenticDownloadJobLogsO // agents), most recent first. Requires system administration permission. // // API endpoint: GET /api/v2/agentic/jobs. -// Enterprise Edition only. +// Available in Community Build since 26.9 and Enterprise Edition since 2026.5. func (s *AgenticService) SearchJobs(ctx context.Context, opt *AgenticSearchJobsOptions) (*AgenticAgenticJobsSearchRestResponse, *http.Response, error) { err := s.ValidateSearchJobsOpt(opt) if err != nil { diff --git a/sonar/alm_settings_service.go b/sonar/alm_settings_service.go index 4033e8e..0f72c1b 100644 --- a/sonar/alm_settings_service.go +++ b/sonar/alm_settings_service.go @@ -75,7 +75,8 @@ type AlmSettingsGetBinding struct { Repository string `json:"repository,omitempty"` // RepositoryURL is the URL to the repository (GitHub, GitLab, Azure since 2025.6). RepositoryURL string `json:"repositoryUrl,omitempty"` - // Slug is the repository slug (Bitbucket). + // Slug is the repository slug (Bitbucket), or the repository's full name or path + // (GitHub, GitLab since 2026.5). Slug string `json:"slug,omitempty"` // SummaryCommentEnabled indicates if summary comments are enabled. SummaryCommentEnabled bool `json:"summaryCommentEnabled,omitempty"` diff --git a/sonar/dashboards_v2_service.go b/sonar/dashboards_v2_service.go index 61c53c4..90767cc 100644 --- a/sonar/dashboards_v2_service.go +++ b/sonar/dashboards_v2_service.go @@ -8,7 +8,8 @@ import ( ) // DashboardsService handles communication with the custom and built-in dashboards related methods of the -// SonarQube V2 API. This service is only available in Enterprise Edition. +// SonarQube V2 API. Custom dashboards are only available in Enterprise Edition; +// the built-in dashboard endpoints are also available in Community Build since 26.9. type DashboardsService struct { // client is used to communicate with the SonarQube API. client *Client @@ -343,7 +344,7 @@ func (s *DashboardsService) List(ctx context.Context, opt *DashboardsListOptions // ListBuiltIn lists built-in dashboards. // // API endpoint: GET /api/v2/dashboards/built-ins. -// Enterprise Edition only. +// Available in Community Build since 26.9 and Enterprise Edition since 2026.5. func (s *DashboardsService) ListBuiltIn(ctx context.Context, opt *DashboardsListBuiltInOptions) (*DashboardsBuiltInDashboardsResponse, *http.Response, error) { err := s.ValidateListBuiltInOpt(opt) if err != nil { @@ -368,7 +369,7 @@ func (s *DashboardsService) ListBuiltIn(ctx context.Context, opt *DashboardsList // GetBuiltIn gets a built-in dashboard. // // API endpoint: GET /api/v2/dashboards/built-ins/{key}. -// Enterprise Edition only. +// Available in Community Build since 26.9 and Enterprise Edition since 2026.5. func (s *DashboardsService) GetBuiltIn(ctx context.Context, key string) (*DashboardsBuiltInDashboardResponse, *http.Response, error) { err := ValidateRequired(key, "key") if err != nil { diff --git a/sonar/dop_translation_v2_agents_service.go b/sonar/dop_translation_v2_agents_service.go index 54f2fc8..f110c19 100644 --- a/sonar/dop_translation_v2_agents_service.go +++ b/sonar/dop_translation_v2_agents_service.go @@ -127,6 +127,24 @@ type DopTranslationScmAccessTokenRestResponse struct { // Validation Functions // ----------------------------------------------------------------------------- +// ValidateCheckPermissionsOpt validates the options for the CheckPermissions method. A nil opt is +// valid and checks every configuration instance-wide. +func (s *DopTranslationService) ValidateCheckPermissionsOpt(opt *DopTranslationCheckPermissionsOptions) error { + if opt == nil { + return nil + } + + if opt.Project != "" && opt.Configuration != "" { + return NewValidationError("Configuration", "cannot be combined with Project", ErrInvalidValue) + } + + if opt.Refresh != nil && *opt.Refresh && opt.Configuration == "" { + return NewValidationError("Configuration", "is required when Refresh is true", ErrMissingRequired) + } + + return nil +} + // ValidateGenerateScmAccessTokenOpt validates the options for the GenerateScmAccessToken method. func (s *DopTranslationService) ValidateGenerateScmAccessTokenOpt(opt *DopTranslationGenerateScmAccessTokenOptions) error { if opt == nil { @@ -162,9 +180,15 @@ func (s *DopTranslationService) ValidateGenerateScmAccessTokenOpt(opt *DopTransl // response does not include the counts or list. // // API endpoint: GET /api/v2/dop-translation/permission-checks. -// Enterprise Edition only. Marked internal by SonarQube and subject to change +// Available in Community Build since 26.9 and Enterprise Edition since 2026.5. +// Marked internal by SonarQube and subject to change // without notice. func (s *DopTranslationService) CheckPermissions(ctx context.Context, opt *DopTranslationCheckPermissionsOptions) (*DopTranslationPermissionChecksRestResponse, *http.Response, error) { + err := s.ValidateCheckPermissionsOpt(opt) + if err != nil { + return nil, nil, err + } + req, err := s.client.NewSonarQubeV2APIRequest(ctx, http.MethodGet, "dop-translation/permission-checks", opt, nil) if err != nil { return nil, nil, fmt.Errorf("failed to create request: %w", err) @@ -188,7 +212,8 @@ func (s *DopTranslationService) CheckPermissions(ctx context.Context, opt *DopTr // authentication. // // API endpoint: POST /api/v2/dop-translation/scm-access-tokens. -// Enterprise Edition only. Marked internal by SonarQube and subject to change +// Available in Community Build since 26.9 and Enterprise Edition since 2026.5. +// Marked internal by SonarQube and subject to change // without notice. func (s *DopTranslationService) GenerateScmAccessToken(ctx context.Context, opt *DopTranslationGenerateScmAccessTokenOptions) (*DopTranslationScmAccessTokenRestResponse, *http.Response, error) { err := s.ValidateGenerateScmAccessTokenOpt(opt) diff --git a/sonar/dop_translation_v2_agents_service_test.go b/sonar/dop_translation_v2_agents_service_test.go index fba92d7..1032e36 100644 --- a/sonar/dop_translation_v2_agents_service_test.go +++ b/sonar/dop_translation_v2_agents_service_test.go @@ -19,6 +19,39 @@ func TestDopTranslationService_CheckPermissions(t *testing.T) { assert.NotNil(t, result) } +func TestDopTranslationService_CheckPermissions_NilOptions(t *testing.T) { + server := newTestServer(t, mockHandler(t, http.MethodGet, "/v2/dop-translation/permission-checks", http.StatusOK, map[string]any{})) + client := newTestClient(t, server.url()) + + result, _, err := client.V2.DopTranslation.CheckPermissions(context.Background(), nil) + require.NoError(t, err) + assert.NotNil(t, result) +} + +func TestDopTranslationService_CheckPermissions_ValidationError(t *testing.T) { + client := newLocalhostClient(t) + + tests := []struct { + opt *DopTranslationCheckPermissionsOptions + name string + }{ + {&DopTranslationCheckPermissionsOptions{Project: "p", Configuration: "c"}, "project with configuration"}, + {&DopTranslationCheckPermissionsOptions{Refresh: new(true)}, "refresh without configuration"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, resp, err := client.V2.DopTranslation.CheckPermissions(context.Background(), tt.opt) + require.Error(t, err) + assert.Nil(t, result) + assert.Nil(t, resp) + }) + } + + err := client.V2.DopTranslation.ValidateCheckPermissionsOpt(&DopTranslationCheckPermissionsOptions{Configuration: "c", Refresh: new(true)}) + require.NoError(t, err) +} + func TestDopTranslationService_GenerateScmAccessToken(t *testing.T) { server := newTestServer(t, mockHandler(t, http.MethodPost, "/v2/dop-translation/scm-access-tokens", http.StatusOK, map[string]any{})) client := newTestClient(t, server.url()) diff --git a/sonar/history_v2_resolution_service.go b/sonar/history_v2_resolution_service.go index 483809f..a065a77 100644 --- a/sonar/history_v2_resolution_service.go +++ b/sonar/history_v2_resolution_service.go @@ -98,8 +98,8 @@ type HistoryGetIssueDensityHistoryOptions struct { // Impacts limit results to issues with specific impacts (comma-separated). An impact is a // combination of software quality and severity. Impacts []string `json:"impacts,omitempty"` - // IssueTypes limit results to issues with specific types (comma-separated). Allowed values are - // the SonarQube issue types. + // IssueTypes limit results to issues with specific types (comma-separated). Allowed + // values: BUG, CODE_SMELL, VULNERABILITY, plus SECURITY_HOTSPOT on Community Build. IssueTypes []string `json:"issueTypes,omitempty"` // RuleKeys comma separated list of ruleKeys. RuleKeys []string `json:"ruleKeys,omitempty"` @@ -107,14 +107,17 @@ type HistoryGetIssueDensityHistoryOptions struct { // Allowed values: BLOCKER, HIGH, INFO, LOW, MEDIUM. Severities []string `json:"severities,omitempty"` // TypeSeverities limit results to issues with specific stored issue severities (comma- - // separated). Allowed values: INFO, MINOR, MAJOR, CRITICAL, BLOCKER. + // separated). Allowed values: INFO, MINOR, MAJOR, CRITICAL, BLOCKER. Ignored by Community + // Build. TypeSeverities []string `json:"typeSeverities,omitempty"` // SliceBy optionally group issue counts by a specific dimension. If not provided, issue counts // will contain a single distribution with the count of all issues. Allowed values: RULE_KEY, - // SEVERITY, TYPE_SEVERITY, SOFTWARE_QUALITY, STATUS, TYPE. + // SEVERITY, TYPE_SEVERITY, SOFTWARE_QUALITY, STATUS, TYPE. TYPE_SEVERITY is rejected by + // Community Build. SliceBy string `json:"sliceBy,omitempty"` // Statuses limit results to issues with specific statuses (comma-separated). Allowed values: - // ACCEPTED, CONFIRMED, FALSE_POSITIVE, FIXED, OPEN. + // ACCEPTED, CONFIRMED, FALSE_POSITIVE, FIXED, OPEN, plus REVIEWED, SAFE and TO_REVIEW on + // Community Build. Statuses []string `json:"statuses,omitempty"` } @@ -163,17 +166,18 @@ type HistoryGetIssueResolutionHistoryOptions struct { // Impacts limit results to issues with specific impacts (comma-separated). An impact is a // combination of software quality and severity. Impacts []string `json:"impacts,omitempty"` - // IssueTypes limit results to issues with specific types (comma-separated). Allowed values are - // the SonarQube issue types. + // IssueTypes limit results to issues with specific types (comma-separated). Allowed + // values: BUG, CODE_SMELL, VULNERABILITY, plus SECURITY_HOTSPOT on Community Build. IssueTypes []string `json:"issueTypes,omitempty"` // Severities limit results to issues with specific impact severities (comma-separated). // Allowed values: BLOCKER, HIGH, INFO, LOW, MEDIUM. Severities []string `json:"severities,omitempty"` // TypeSeverities limit results to issues with specific stored issue severities (comma- - // separated). Allowed values: INFO, MINOR, MAJOR, CRITICAL, BLOCKER. + // separated). Allowed values: INFO, MINOR, MAJOR, CRITICAL, BLOCKER. Ignored by Community + // Build. TypeSeverities []string `json:"typeSeverities,omitempty"` // SliceBy optionally group issue resolution metrics by a dimension. Allowed values: SEVERITY, - // TYPE_SEVERITY, SOFTWARE_QUALITY, TYPE. + // TYPE_SEVERITY, SOFTWARE_QUALITY, TYPE. TYPE_SEVERITY is rejected by Community Build. SliceBy string `json:"sliceBy,omitempty"` } @@ -225,10 +229,11 @@ type HistoryGetProjectIssueResolutionOptions struct { // Allowed values: BLOCKER, HIGH, INFO, LOW, MEDIUM. Severities []string `json:"severities,omitempty"` // TypeSeverities limit results to issues with specific stored issue severities (comma- - // separated). Allowed values: INFO, MINOR, MAJOR, CRITICAL, BLOCKER. + // separated). Allowed values: INFO, MINOR, MAJOR, CRITICAL, BLOCKER. Ignored by Community + // Build. TypeSeverities []string `json:"typeSeverities,omitempty"` - // IssueTypes limit results to issues with specific types (comma-separated). Allowed values are - // the SonarQube issue types. + // IssueTypes limit results to issues with specific types (comma-separated). Allowed + // values: BUG, CODE_SMELL, VULNERABILITY, plus SECURITY_HOTSPOT on Community Build. IssueTypes []string `json:"issueTypes,omitempty"` // Impacts limit results to issues with specific impacts (comma-separated). An impact is a // combination of software quality and severity. @@ -591,7 +596,7 @@ func (s *HistoryService) ValidateGetScaResolutionHistoryOpt(opt *HistoryGetScaRe // changes. Density is calculated as (issue count / NCLOC) * 1000. // // API endpoint: GET /api/v2/history/issue-density-history. -// Enterprise Edition only. +// Available in Community Build since 26.9 and Enterprise Edition since 2026.5. func (s *HistoryService) GetIssueDensityHistory(ctx context.Context, opt *HistoryGetIssueDensityHistoryOptions) (*HistoryIssueDensityHistoryResponse, *http.Response, error) { err := s.ValidateGetIssueDensityHistoryOpt(opt) if err != nil { @@ -619,7 +624,7 @@ func (s *HistoryService) GetIssueDensityHistory(ctx context.Context, opt *Histor // than one year under the approved history date-range policy. // // API endpoint: GET /api/v2/history/issue-resolution-history. -// Enterprise Edition only. +// Available in Community Build since 26.9 and Enterprise Edition since 2026.5. func (s *HistoryService) GetIssueResolutionHistory(ctx context.Context, opt *HistoryGetIssueResolutionHistoryOptions) (*HistoryIssueResolutionHistoryResponse, *http.Response, error) { err := s.ValidateGetIssueResolutionHistoryOpt(opt) if err != nil { @@ -647,7 +652,7 @@ func (s *HistoryService) GetIssueResolutionHistory(ctx context.Context, opt *His // mutually exclusive. // // API endpoint: GET /api/v2/history/project-issue-resolution. -// Enterprise Edition only. +// Available in Community Build since 26.9 and Enterprise Edition since 2026.5. func (s *HistoryService) GetProjectIssueResolution(ctx context.Context, opt *HistoryGetProjectIssueResolutionOptions) (*HistoryProjectIssueResolutionResponse, *http.Response, error) { err := s.ValidateGetProjectIssueResolutionOpt(opt) if err != nil { diff --git a/sonar/history_v2_service.go b/sonar/history_v2_service.go index a4090a4..41bce44 100644 --- a/sonar/history_v2_service.go +++ b/sonar/history_v2_service.go @@ -11,6 +11,8 @@ const ( HistoryEntityTypePortfolio = "PORTFOLIO" // HistoryEntityTypeProjectBranch identifies a project branch entity. HistoryEntityTypeProjectBranch = "PROJECT_BRANCH" + // HistoryEntityTypeApplication identifies an application entity. + HistoryEntityTypeApplication = "APPLICATION" ) // HistoryService handles communication with the history related methods of the @@ -22,11 +24,32 @@ type HistoryService struct { client *Client } -//nolint:gochecknoglobals // constant set of allowed values -var allowedHistoryMeasuresEntityTypes = map[string]struct{}{ - HistoryEntityTypePortfolio: {}, - HistoryEntityTypeProjectBranch: {}, -} +//nolint:gochecknoglobals,goconst // constant sets of allowed values +var ( + // allowedHistoryEntityTypes is the set of entity types accepted by the + // entity-scoped history endpoints (measures and issue count history). + allowedHistoryEntityTypes = map[string]struct{}{ + HistoryEntityTypePortfolio: {}, + HistoryEntityTypeProjectBranch: {}, + HistoryEntityTypeApplication: {}, + } + // allowedHistorySelectionEntityTypes is the set of entity types accepted by + // the flattened per-project history endpoints. + allowedHistorySelectionEntityTypes = map[string]struct{}{ + HistoryEntityTypePortfolio: {}, + HistoryEntityTypeApplication: {}, + } + // allowedHistoryIssueCountSliceBy is the set of allowed values for + // HistoryIssueCountHistoryOptions.SliceBy. + allowedHistoryIssueCountSliceBy = map[string]struct{}{ + "RULE_KEY": {}, + "SEVERITY": {}, + "TYPE_SEVERITY": {}, + "SOFTWARE_QUALITY": {}, + "STATUS": {}, + "TYPE": {}, + } +) // ----------------------------------------------------------------------------- // Response Types @@ -168,6 +191,7 @@ type HistoryIssueCountHistoryOptions struct { // This field is required. EntityID string `json:"entityId"` // EntityType is the entity's type. This field is required. + // Allowed values: PORTFOLIO, PROJECT_BRANCH, APPLICATION. EntityType string `json:"entityType"` // StartDate is the inclusive start of the date range in ISO 8601 format. // This field is required and may not be more than 1 year in the past. @@ -177,15 +201,26 @@ type HistoryIssueCountHistoryOptions struct { EndDate string `json:"endDate,omitempty"` // Impacts limits results to issues with specific impacts. Impacts []string `json:"impacts,omitempty"` - // IssueTypes limits results to issues with specific types. + // IssueTypes limits results to issues with specific types. Allowed + // values: BUG, CODE_SMELL, VULNERABILITY, plus SECURITY_HOTSPOT on + // Community Build. IssueTypes []string `json:"issueTypes,omitempty"` // RuleKeys limits results to issues raised by specific rules. RuleKeys []string `json:"ruleKeys,omitempty"` - // Severities limits results to issues with specific severities. + // Severities limits results to issues with specific impact severities. + // Allowed values: BLOCKER, HIGH, INFO, LOW, MEDIUM. Severities []string `json:"severities,omitempty"` - // SliceBy optionally groups issue counts by a specific dimension. + // TypeSeverities limits results to issues with specific stored issue + // severities. Allowed values: INFO, MINOR, MAJOR, CRITICAL, BLOCKER. + // Ignored by Community Build. + TypeSeverities []string `json:"typeSeverities,omitempty"` + // SliceBy optionally groups issue counts by a specific dimension. Allowed + // values: RULE_KEY, SEVERITY, TYPE_SEVERITY, SOFTWARE_QUALITY, STATUS, TYPE. + // TYPE_SEVERITY is rejected by Community Build. SliceBy string `json:"sliceBy,omitempty"` - // Statuses limits results to issues with specific statuses. + // Statuses limits results to issues with specific statuses. Allowed values: + // ACCEPTED, CONFIRMED, FALSE_POSITIVE, FIXED, OPEN, plus REVIEWED, SAFE and + // TO_REVIEW on Community Build. Statuses []string `json:"statuses,omitempty"` } @@ -194,7 +229,8 @@ type HistoryIssueCountHistoryOptions struct { //nolint:govet // Field alignment less important than maintaining consistent field order for readability type HistoryMeasuresHistoryOptions struct { // EntityType is the type of entity to retrieve measures history for. - // This field is required. Allowed values: PORTFOLIO, PROJECT_BRANCH. + // This field is required. Allowed values: PORTFOLIO, PROJECT_BRANCH, + // APPLICATION. EntityType string `json:"entityType"` // EntityID is the UUID of the entity. This field is required. EntityID string `json:"entityId"` @@ -219,18 +255,28 @@ type HistoryProjectIssueCountsOptions struct { // PortfolioID is the legacy portfolio ID. PortfolioID string `json:"portfolioId,omitempty"` // EntityType is the type of the selection whose projects are returned as a - // flattened list. Must be provided together with EntityID. + // flattened list. Must be provided together with EntityID. Allowed values: + // PORTFOLIO, APPLICATION. EntityType string `json:"entityType,omitempty"` // EntityID is the portfolio ID or application branch ID, according to // EntityType. Must be provided together with EntityType. EntityID string `json:"entityId,omitempty"` // RuleKeys limits results to issues raised by specific rules. RuleKeys []string `json:"ruleKeys,omitempty"` - // Severities limits results to issues with specific severities. + // Severities limits results to issues with specific impact severities. + // Allowed values: BLOCKER, HIGH, INFO, LOW, MEDIUM. Severities []string `json:"severities,omitempty"` - // IssueTypes limits results to issues with specific types. + // TypeSeverities limits results to issues with specific stored issue + // severities. Allowed values: INFO, MINOR, MAJOR, CRITICAL, BLOCKER. + // Ignored by Community Build. + TypeSeverities []string `json:"typeSeverities,omitempty"` + // IssueTypes limits results to issues with specific types. Allowed + // values: BUG, CODE_SMELL, VULNERABILITY, plus SECURITY_HOTSPOT on + // Community Build. IssueTypes []string `json:"issueTypes,omitempty"` - // Statuses limits results to issues with specific statuses. + // Statuses limits results to issues with specific statuses. Allowed values: + // ACCEPTED, CONFIRMED, FALSE_POSITIVE, FIXED, OPEN, plus REVIEWED, SAFE and + // TO_REVIEW on Community Build. Statuses []string `json:"statuses,omitempty"` // Impacts limits results to issues with specific impacts. Impacts []string `json:"impacts,omitempty"` @@ -272,7 +318,8 @@ type HistoryProjectMeasuresOptions struct { // PortfolioID is the legacy portfolio ID. PortfolioID string `json:"portfolioId,omitempty"` // EntityType is the type of the selection whose projects are returned as a - // flattened list. Must be provided together with EntityID. + // flattened list. Must be provided together with EntityID. Allowed values: + // PORTFOLIO, APPLICATION. EntityType string `json:"entityType,omitempty"` // EntityID is the portfolio ID or application branch ID, according to // EntityType. Must be provided together with EntityType. @@ -307,7 +354,17 @@ func (s *HistoryService) ValidateIssueCountHistoryOpt(opt *HistoryIssueCountHist return err } - return ValidateRequired(opt.StartDate, "StartDate") + err = IsValueAuthorized(opt.EntityType, allowedHistoryEntityTypes, "EntityType") + if err != nil { + return err + } + + err = ValidateRequired(opt.StartDate, "StartDate") + if err != nil { + return err + } + + return IsValueAuthorized(opt.SliceBy, allowedHistoryIssueCountSliceBy, "SliceBy") } // ValidateMeasuresHistoryOpt validates the options for GetMeasuresHistory. @@ -321,7 +378,7 @@ func (s *HistoryService) ValidateMeasuresHistoryOpt(opt *HistoryMeasuresHistoryO return err } - err = IsValueAuthorized(opt.EntityType, allowedHistoryMeasuresEntityTypes, "EntityType") + err = IsValueAuthorized(opt.EntityType, allowedHistoryEntityTypes, "EntityType") if err != nil { return err } @@ -355,7 +412,7 @@ func validateHistorySelector(portfolioID, entityType, entityID string) error { return NewValidationError("EntityType", "EntityType and EntityID must be provided together", ErrMissingRequired) } - return nil + return IsValueAuthorized(entityType, allowedHistorySelectionEntityTypes, "EntityType") } // ValidateProjectIssueCountsOpt validates the options for GetProjectIssueCounts. diff --git a/sonar/history_v2_service_test.go b/sonar/history_v2_service_test.go index 7ebaaf3..71484c1 100644 --- a/sonar/history_v2_service_test.go +++ b/sonar/history_v2_service_test.go @@ -29,8 +29,9 @@ func TestHistoryService_GetIssueCountHistory(t *testing.T) { map[string]string{ "entityId": "00000000-0000-0000-0000-000000000000", "entityType": "PROJECT_BRANCH", - "startDate": "2026-01-01T00:00:00Z", - "sliceBy": "issueType", + "startDate": "2026-01-01T00:00:00Z", + "sliceBy": "TYPE", + "typeSeverities": "MAJOR", }, response)) client := newTestClient(t, server.url()) @@ -38,8 +39,9 @@ func TestHistoryService_GetIssueCountHistory(t *testing.T) { EntityID: "00000000-0000-0000-0000-000000000000", EntityType: "PROJECT_BRANCH", StartDate: "2026-01-01T00:00:00Z", - SliceBy: "issueType", - IssueTypes: []string{"BUG", "CODE_SMELL"}, + SliceBy: "TYPE", + IssueTypes: []string{"BUG", "CODE_SMELL"}, + TypeSeverities: []string{"MAJOR"}, }) require.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) @@ -61,6 +63,8 @@ func TestHistoryService_GetIssueCountHistory_ValidationError(t *testing.T) { {&HistoryIssueCountHistoryOptions{EntityType: "PROJECT_BRANCH", StartDate: "2026-01-01T00:00:00Z"}, "missing EntityID"}, {&HistoryIssueCountHistoryOptions{EntityID: "id", StartDate: "2026-01-01T00:00:00Z"}, "missing EntityType"}, {&HistoryIssueCountHistoryOptions{EntityID: "id", EntityType: "PROJECT_BRANCH"}, "missing StartDate"}, + {&HistoryIssueCountHistoryOptions{EntityID: "id", EntityType: "BRANCH", StartDate: "d"}, "invalid EntityType"}, + {&HistoryIssueCountHistoryOptions{EntityID: "id", EntityType: "PROJECT_BRANCH", StartDate: "d", SliceBy: "issueType"}, "invalid SliceBy"}, } for _, tt := range tests { @@ -110,6 +114,25 @@ func TestHistoryService_GetMeasuresHistory(t *testing.T) { assert.Equal(t, "81.2", result.MeasuresHistory[0].Measures[0].Value) } +func TestHistoryService_GetMeasuresHistory_Application(t *testing.T) { + server := newTestServer(t, mockHandlerWithParams(t, http.MethodGet, "/v2/history/measures-history", http.StatusOK, + map[string]string{ + "entityType": HistoryEntityTypeApplication, + "entityId": "00000000-0000-0000-0000-000000000000", + }, HistoryMeasuresHistoryResponse{})) + client := newTestClient(t, server.url()) + + result, resp, err := client.V2.History.GetMeasuresHistory(context.Background(), &HistoryMeasuresHistoryOptions{ + EntityType: HistoryEntityTypeApplication, + EntityID: "00000000-0000-0000-0000-000000000000", + MetricKeys: []string{"ncloc"}, + StartDate: "2026-01-01T00:00:00Z", + }) + require.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.NotNil(t, result) +} + func TestHistoryService_GetMeasuresHistory_ValidationError(t *testing.T) { client := newLocalhostClient(t) svc := &HistoryService{client: client} @@ -192,6 +215,7 @@ func TestHistoryService_GetProjectIssueCounts_ValidationError(t *testing.T) { {&HistoryProjectIssueCountsOptions{PortfolioID: "p", EntityType: "PORTFOLIO", EntityID: "e"}, "both selector forms"}, {&HistoryProjectIssueCountsOptions{EntityType: "PORTFOLIO"}, "entityType without entityId"}, {&HistoryProjectIssueCountsOptions{EntityID: "e"}, "entityId without entityType"}, + {&HistoryProjectIssueCountsOptions{EntityType: "PROJECT_BRANCH", EntityID: "e"}, "invalid EntityType"}, } for _, tt := range tests { @@ -261,6 +285,7 @@ func TestHistoryService_GetProjectMeasures_ValidationError(t *testing.T) { {&HistoryProjectMeasuresOptions{MetricKey: "coverage"}, "no selector"}, {&HistoryProjectMeasuresOptions{MetricKey: "coverage", PortfolioID: "p", EntityID: "e"}, "both selector forms"}, {&HistoryProjectMeasuresOptions{MetricKey: "coverage", EntityType: "PORTFOLIO"}, "entityType without entityId"}, + {&HistoryProjectMeasuresOptions{MetricKey: "coverage", EntityType: "PROJECT_BRANCH", EntityID: "e"}, "invalid EntityType"}, } for _, tt := range tests { diff --git a/sonar/issues_service.go b/sonar/issues_service.go index 8ba1206..e560586 100644 --- a/sonar/issues_service.go +++ b/sonar/issues_service.go @@ -138,6 +138,10 @@ type Issue struct { QuickFixAvailable bool `json:"quickFixAvailable,omitempty"` // PrioritizedRule indicates if the rule is prioritized. PrioritizedRule bool `json:"prioritizedRule,omitempty"` + // Scope is the scope of the file the issue is on (MAIN or TEST). + Scope string `json:"scope,omitempty"` + // FromSonarQubeUpdate indicates if the issue was detected because of a SonarQube update. + FromSonarQubeUpdate bool `json:"fromSonarQubeUpdate,omitempty"` } // IssueComment represents a comment on an issue. @@ -174,6 +178,8 @@ type IssueFlow struct { // //nolint:govet // Field alignment less important than maintaining consistent field order for readability type FlowLocation struct { + // Component is the key of the component this location is in. + Component string `json:"component,omitempty"` // Msg is the message for this location. Msg string `json:"msg,omitempty"` // TextRange is the text range of this location. @@ -315,8 +321,6 @@ type ComponentTag struct { // ============================================================================= // IssuesAddComment represents the response from adding a comment to an issue. -// -//nolint:govet // Field alignment less important than maintaining consistent field order for readability type IssuesAddComment struct { Components []IssueComponent `json:"components,omitempty"` Issue Issue `json:"issue,omitzero"` @@ -325,8 +329,6 @@ type IssuesAddComment struct { } // IssuesAssign represents the response from assigning an issue. -// -//nolint:govet // Field alignment less important than maintaining consistent field order for readability type IssuesAssign struct { Components []IssueComponent `json:"components,omitempty"` Issue Issue `json:"issue,omitzero"` @@ -365,8 +367,6 @@ type IssuesComponentTags struct { } // IssuesDeleteComment represents the response from deleting a comment. -// -//nolint:govet // Field alignment less important than maintaining consistent field order for readability type IssuesDeleteComment struct { Components []IssueComponent `json:"components,omitempty"` Issue Issue `json:"issue,omitzero"` @@ -375,8 +375,6 @@ type IssuesDeleteComment struct { } // IssuesDoTransition represents the response from performing a transition. -// -//nolint:govet // Field alignment less important than maintaining consistent field order for readability type IssuesDoTransition struct { Components []IssueComponent `json:"components,omitempty"` Issue Issue `json:"issue,omitzero"` @@ -385,8 +383,6 @@ type IssuesDoTransition struct { } // IssuesEditComment represents the response from editing a comment. -// -//nolint:govet // Field alignment less important than maintaining consistent field order for readability type IssuesEditComment struct { Components []IssueComponent `json:"components,omitempty"` Issue Issue `json:"issue,omitzero"` @@ -420,8 +416,6 @@ type IssuesSearch struct { } // IssuesSetSeverity represents the response from setting severity. -// -//nolint:govet // Field alignment less important than maintaining consistent field order for readability type IssuesSetSeverity struct { Components []IssueComponent `json:"components,omitempty"` Issue Issue `json:"issue,omitzero"` @@ -430,8 +424,6 @@ type IssuesSetSeverity struct { } // IssuesSetTags represents the response from setting tags. -// -//nolint:govet // Field alignment less important than maintaining consistent field order for readability type IssuesSetTags struct { Components []IssueComponent `json:"components,omitempty"` Issue Issue `json:"issue,omitzero"` @@ -440,8 +432,6 @@ type IssuesSetTags struct { } // IssuesSetType represents the response from setting type. -// -//nolint:govet // Field alignment less important than maintaining consistent field order for readability type IssuesSetType struct { Components []IssueComponent `json:"components,omitempty"` Issue Issue `json:"issue,omitzero"` diff --git a/sonar/issues_service_test.go b/sonar/issues_service_test.go index 9438f1c..2c74215 100644 --- a/sonar/issues_service_test.go +++ b/sonar/issues_service_test.go @@ -444,6 +444,47 @@ func TestIssues_Search(t *testing.T) { assert.Len(t, result.Issues, 1) } +func TestIssues_Search_LiveIssueShape(t *testing.T) { + // Issue captured from a SonarQube Community Build 26.9 analysis. + body := `{ + "issues": [{ + "key": "f7c0d371-c177-4599-9237-3605600bdace", "rule": "python:S1764", "severity": "MAJOR", + "component": "probe-ce:src/app.py", "project": "probe-ce", "line": 24, + "hash": "603b3b3324f162551fb61a3bf932bb76", + "textRange": {"startLine": 24, "endLine": 24, "startOffset": 12, "endOffset": 13}, + "flows": [{"locations": [{"component": "probe-ce:src/app.py", + "textRange": {"startLine": 24, "endLine": 24, "startOffset": 7, "endOffset": 8}, "msgFormattings": []}]}], + "status": "OPEN", "message": "Correct one of the identical sub-expressions.", "effort": "2min", "debt": "2min", + "author": "", "tags": ["suspicious"], "creationDate": "2026-10-20T19:59:39+0000", + "updateDate": "2026-10-20T19:59:39+0000", "type": "BUG", "scope": "MAIN", "quickFixAvailable": false, + "messageFormattings": [], "codeVariants": [], "cleanCodeAttribute": "LOGICAL", + "cleanCodeAttributeCategory": "INTENTIONAL", "impacts": [{"softwareQuality": "RELIABILITY", "severity": "MEDIUM"}], + "issueStatus": "OPEN", "prioritizedRule": false, "fromSonarQubeUpdate": true, "internalTags": [], + "linkedTicketStatus": "NOT_LINKED" + }], + "paging": {"pageIndex": 1, "pageSize": 100, "total": 1} + }` + + mismatches, err := CheckSchema("GET /issues/search", []byte(body), &IssuesSearch{}) + require.NoError(t, err) + assert.Empty(t, mismatches) + + server := newTestServer(t, func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(body)) + }) + client := newTestClient(t, server.URL) + + result, _, err := client.Issues.Search(context.Background(), &IssuesSearchOptions{Components: []string{"probe-ce"}}) + require.NoError(t, err) + require.Len(t, result.Issues, 1) + assert.Equal(t, "MAIN", result.Issues[0].Scope) + assert.True(t, result.Issues[0].FromSonarQubeUpdate) + require.Len(t, result.Issues[0].Flows, 1) + require.Len(t, result.Issues[0].Flows[0].Locations, 1) + assert.Equal(t, "probe-ce:src/app.py", result.Issues[0].Flows[0].Locations[0].Component) +} + func TestIssues_Search_ValidationError(t *testing.T) { client := newLocalhostClient(t) diff --git a/sonar/onboarding_v2_service.go b/sonar/onboarding_v2_service.go index e4b96b8..9cbb055 100644 --- a/sonar/onboarding_v2_service.go +++ b/sonar/onboarding_v2_service.go @@ -7,7 +7,8 @@ import ( ) // OnboardingService handles communication with the onboarding related methods of the -// SonarQube V2 API. This service is only available in Enterprise Edition. +// SonarQube V2 API. This service is available in Community Build since 26.9 and +// in Enterprise Edition since 2026.5. type OnboardingService struct { // client is used to communicate with the SonarQube API. client *Client @@ -226,7 +227,7 @@ func (s *OnboardingService) ValidateGetProjectsOpt(opt *OnboardingGetProjectsOpt // GetOverview calls GET /api/v2/onboarding/overview. // // API endpoint: GET /api/v2/onboarding/overview. -// Enterprise Edition only. +// Available in Community Build since 26.9 and Enterprise Edition since 2026.5. func (s *OnboardingService) GetOverview(ctx context.Context, opt *OnboardingGetOverviewOptions) (*OnboardingOnboardingOverviewResponse, *http.Response, error) { req, err := s.client.NewSonarQubeV2APIRequest(ctx, http.MethodGet, "onboarding/overview", opt, nil) if err != nil { @@ -246,7 +247,7 @@ func (s *OnboardingService) GetOverview(ctx context.Context, opt *OnboardingGetO // GetProjects calls GET /api/v2/onboarding/projects. // // API endpoint: GET /api/v2/onboarding/projects. -// Enterprise Edition only. +// Available in Community Build since 26.9 and Enterprise Edition since 2026.5. func (s *OnboardingService) GetProjects(ctx context.Context, opt *OnboardingGetProjectsOptions) (*OnboardingOnboardingProjectsResponse, *http.Response, error) { err := s.ValidateGetProjectsOpt(opt) if err != nil { @@ -271,7 +272,7 @@ func (s *OnboardingService) GetProjects(ctx context.Context, opt *OnboardingGetP // GetStatistics calls GET /api/v2/onboarding/statistics. // // API endpoint: GET /api/v2/onboarding/statistics. -// Enterprise Edition only. +// Available in Community Build since 26.9 and Enterprise Edition since 2026.5. func (s *OnboardingService) GetStatistics(ctx context.Context, opt *OnboardingGetStatisticsOptions) (*OnboardingOnboardingStatisticsResponse, *http.Response, error) { req, err := s.client.NewSonarQubeV2APIRequest(ctx, http.MethodGet, "onboarding/statistics", opt, nil) if err != nil {