diff --git a/src/main/java/com/openelements/issues/ApiEndpoint.java b/src/main/java/com/openelements/issues/ApiEndpoint.java index c93ce7f..fdbc465 100644 --- a/src/main/java/com/openelements/issues/ApiEndpoint.java +++ b/src/main/java/com/openelements/issues/ApiEndpoint.java @@ -3,6 +3,10 @@ import com.openelements.issues.data.Contributor; import com.openelements.issues.data.Issue; import com.openelements.issues.services.GitHubCache; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -13,6 +17,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController +@Tag(name = "Good First Issue API", description = "Endpoints for querying good first issues, contributors, and repository statistics") public class ApiEndpoint { private final static Logger log = org.slf4j.LoggerFactory.getLogger(ApiEndpoint.class); @@ -23,25 +28,39 @@ public ApiEndpoint(@NonNull final GitHubCache issueCache) { this.issueCache = Objects.requireNonNull(issueCache, "issueCache must not be null"); } - + @Operation(summary = "Get all contributors", + description = "Returns a set of all contributors across the configured repositories") + @ApiResponse(responseCode = "200", description = "Successfully retrieved the list of contributors") @GetMapping("/api/v2/contributors") public Set getContributors() { log.info("Getting contributors"); return issueCache.getContributors(); } + @Operation(summary = "Get contributor count", + description = "Returns the total number of contributors across all configured repositories") + @ApiResponse(responseCode = "200", description = "Successfully retrieved the contributor count") @GetMapping("/api/v2/contributors/count") public long getContributorCount() { log.info("Getting contributors count"); return issueCache.getContributors().size(); } + @Operation(summary = "Get issues", + description = "Returns a filtered set of issues from configured repositories. " + + "Supports filtering by assignment status, closed status, labels, and programming languages.") + @ApiResponse(responseCode = "200", description = "Successfully retrieved the list of issues") @GetMapping("/api/v2/issues") public Set getIssues( + @Parameter(description = "Filter by assignment status (true = assigned, false = unassigned)") @RequestParam(name = "isAssigned", required = false) Boolean isAssigned, + @Parameter(description = "Filter by closed status (true = closed, false = open)") @RequestParam(name = "isClosed", required = false) Boolean isClosed, + @Parameter(description = "Filter issues that contain all of the specified labels") @RequestParam(name = "filteredLabels", required = false) Set filteredLabels, + @Parameter(description = "Exclude issues that contain any of the specified labels") @RequestParam(name = "excludedLabels", required = false) Set excludedLabels, + @Parameter(description = "Filter issues by repository programming languages") @RequestParam(name = "filteredLanguages", required = false) Set filteredLanguages) { log.info( "Getting issues with filters - isAssigned: {}, isClosed: {}, filteredLabels: {}, excludedLabels: {}, filteredLanguages: {}", @@ -58,21 +77,35 @@ public Set getIssues( .collect(Collectors.toUnmodifiableSet()); } + @Operation(summary = "Get issue count", + description = "Returns the total count of issues matching the specified filters") + @ApiResponse(responseCode = "200", description = "Successfully retrieved the issue count") @GetMapping("/api/v2/issues/count") public long getIssuesCount( + @Parameter(description = "Filter by assignment status (true = assigned, false = unassigned)") @RequestParam(name = "isAssigned", required = false) Boolean isAssigned, + @Parameter(description = "Filter by closed status (true = closed, false = open)") @RequestParam(name = "isClosed", required = false) Boolean isClosed, + @Parameter(description = "Filter issues that contain all of the specified labels") @RequestParam(name = "filteredLabels", required = false) Set filteredLabels, + @Parameter(description = "Exclude issues that contain any of the specified labels") @RequestParam(name = "excludedLabels", required = false) Set excludedLabels, + @Parameter(description = "Filter issues by repository programming languages") @RequestParam(name = "filteredLanguages", required = false) Set filteredLanguages) { return getIssues(isAssigned, isClosed, filteredLabels, excludedLabels, filteredLanguages).size(); } + @Operation(summary = "Get total stars count", + description = "Returns the sum of stars across all configured repositories") + @ApiResponse(responseCode = "200", description = "Successfully retrieved the total stars count") @GetMapping("/api/v2/repositories/stars") public long getStarsCount() { return issueCache.getRepositories().stream().mapToLong(repo -> repo.stars()).sum(); } + @Operation(summary = "Get repository count", + description = "Returns the total number of configured repositories") + @ApiResponse(responseCode = "200", description = "Successfully retrieved the repository count") @GetMapping("/api/v2/repositories/count") public long getRepositoryCount() { return issueCache.getRepositories().size(); diff --git a/src/main/java/com/openelements/issues/config/OpenApiConfig.java b/src/main/java/com/openelements/issues/config/OpenApiConfig.java new file mode 100644 index 0000000..c2c9144 --- /dev/null +++ b/src/main/java/com/openelements/issues/config/OpenApiConfig.java @@ -0,0 +1,29 @@ +package com.openelements.issues.config; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class OpenApiConfig { + + @Bean + public OpenAPI goodFirstIssueOpenAPI() { + return new OpenAPI() + .info(new Info() + .title("Good First Issue Provider API") + .description("REST API for discovering good first issues from open-source projects on GitHub. " + + "This service aggregates issues labeled as 'good first issue' from configured repositories " + + "and provides endpoints to query issues, contributors, and repository statistics.") + .version("2.0.0") + .contact(new Contact() + .name("Open Elements") + .url("https://github.com/OpenElements/good-first-issue-provider")) + .license(new License() + .name("Apache 2.0") + .url("https://www.apache.org/licenses/LICENSE-2.0"))); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 86da9ff..f725ec6 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -16,4 +16,10 @@ management: exposure: include: - health - - prometheus \ No newline at end of file + - prometheus +springdoc: + swagger-ui: + path: /swagger-ui.html + enabled: true + api-docs: + path: /v3/api-docs