Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ESWE-1080] publish expression of interest api #581

Closed
wants to merge 10 commits into from
16 changes: 9 additions & 7 deletions docs/adr/0011-openapi-revision.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Proposed

## Context

In [#0003](./0003-manually-manage-openapi-file.md), the team decided to manually manage the OpenAPI specification file, as opposed to generating them from code.
In [#0003](./0003-manually-manage-openapi-file.md), the team decided to manually manage the OpenAPI specification file, as opposed to generating them from code.
This had the benefit of allowing the team to write documentation for endpoints before they have been built.
However, in practice this has resulted in an out-of-date specification due to code changes not being reflected.

Expand All @@ -19,11 +19,13 @@ When the code or annotations change, the OpenAPI specification will be generated
Additional documentation can be added for endpoints that have not yet been implemented, by adding a separate "draft" OpenAPI specification file.

### Rationale
* Auto-generating OpenAPI specifications from annotations in code is consistent with the wider HMPPS approach
* Keeping documentation as close to the relevant code as possible ensures it is kept up-to-date
* Pre-existing documentation is maintained
* Additional documentation that doesn't correspond to code can be drafted separately

- Auto-generating OpenAPI specifications from annotations in code is consistent with the wider HMPPS approach
- Keeping documentation as close to the relevant code as possible ensures it is kept up-to-date
- Pre-existing documentation is maintained
- Additional documentation that doesn't correspond to code can be drafted separately

## Consequences
* The existing OpenAPI yaml file will be converted into code annotations, and will be published automatically via GitHub pages.
* The tech-docs pages will be updated to reference the new location.

- The existing OpenAPI yaml file will be converted into code annotations, and will be published automatically via GitHub pages.
- The tech-docs pages will be updated to reference the new location.
20 changes: 10 additions & 10 deletions docs/adr/0012-endpoint-naming.md
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we skip formatting .md files?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you rollback this whole file?

Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,25 @@ Name HTTP URLs according to the following rules:

1. Follow the [ADR documented](./0007-version-through-url-path.md) strategy for versioning in the URL path
2. For client-specific endpoints use the URL path part immediately after the version part to indicate the specific client
2. Use subsequent URL path parts to either:
3. Use subsequent URL path parts to either:
1. Indicate the HMPPS domain that the endpoint relates
2. **OR** indicate the intention of a client-specific endpoints

### Rationale

* The URL path should indicate the overall intention of the HTTP endpoint
* The URL path should indicate if the endpoint is intended for a specific client immediately after the version path part, supporting grouping of domain endpoints and client-specific endpoints in the documentation
- The URL path should indicate the overall intention of the HTTP endpoint
- The URL path should indicate if the endpoint is intended for a specific client immediately after the version path part, supporting grouping of domain endpoints and client-specific endpoints in the documentation

## Consequences

* Developers of future endpoints will have a clear guidance on naming HTTP URLs to reflect the intention of the endpoint
* General domain-based endpoints will be named in a clear and consistent manner
* Client-specific endpoints will be named in a clear and consistent manner, indicating the client the endpoint has been designed for
- Developers of future endpoints will have a clear guidance on naming HTTP URLs to reflect the intention of the endpoint
- General domain-based endpoints will be named in a clear and consistent manner
- Client-specific endpoints will be named in a clear and consistent manner, indicating the client the endpoint has been designed for

## Examples

| Type | Description | URL Path |
|-----------------|-------------------------------------------------------------------------------------------------|--------------------------------------------------|
| HMPPS Domain | An endpoint that provides a persons cell location | `/v1/persons/{encodedHmppsId}/cell-location` |
| HMPPS Domain | An endpoint that provides risk assessment actuarial scores | `/v1/persons/{encodedHmppsId}/risks/scores` |
| Type | Description | URL Path |
| --------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------- |
| HMPPS Domain | An endpoint that provides a persons cell location | `/v1/persons/{encodedHmppsId}/cell-location` |
| HMPPS Domain | An endpoint that provides risk assessment actuarial scores | `/v1/persons/{encodedHmppsId}/risks/scores` |
| Client-Specific | An endpoint to provide consolidated person details to support the EPF digital service | `/v1/epf/person-details/{encodedHmppsId}/{eventNumber}` |
22 changes: 14 additions & 8 deletions docs/adr/README.md
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we skip formatting .md files?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you rollback this whole file?

Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
# Architecture Decision Records (ADRs)

| Status | ADR No. | Title |
| ---------- | ------- | ---------------------------------------------------------------------------------------------------------- |
| ✅ Accepted | 0001 | [Use CircleCI](./0001-use-circleci.md) |
| ✅ Accepted | 0002 | [Use PlantUML to create diagrams](./0002-plantuml-diagrams-as-code.md) |
| ✅ Accepted | 0003 | [Manually manage OpenAPI specification file](./0003-manually-manage-openapi-file.md) |
| ✅ Accepted | 0004 | [Always return a JSON object for JSON responses](./0004-always-return-a-json-object-for-json-responses.md) |
| ✅ Accepted | 0005 | [Use Data Transfer Object Pattern](./0005-use-dto-pattern.md) |
| ✅ Accepted | 0006 | [URL-encode path parameters that contain a forward slash](./0006-url-encode-path-parameters.md) |
| Status | ADR No. | Title |
| ------------- | ------- | ---------------------------------------------------------------------------------------------------------- |
| ✅ Accepted | 0001 | [Use CircleCI](./0001-use-circleci.md) |
| ✅ Accepted | 0002 | [Use PlantUML to create diagrams](./0002-plantuml-diagrams-as-code.md) |
| ⏭️ Superseded | 0003 | [Manually manage OpenAPI specification file](./0003-manually-manage-openapi-file.md) |
| ✅ Accepted | 0004 | [Always return a JSON object for JSON responses](./0004-always-return-a-json-object-for-json-responses.md) |
| ✅ Accepted | 0005 | [Use Data Transfer Object Pattern](./0005-use-dto-pattern.md) |
| ✅ Accepted | 0007 | [Version through URL path](./0007-version-through-url-path.md) |
| ✅ Accepted | 0008 | [Use certificates in production](./0008-certificates-in-production.md) |
| ✅ Accepted | 0009 | [Authorisation at application level](./0009-authorisation-at-application-level.md) |
| ✅ Accepted | 0010 | [Certificates expiration](./0010-certificates-expiration.md) |
| ✅ Accepted | 0011 | [Revert decision 0003 and auto-generate OpenAPI specification](./0011-openapi-revision.md) |
| ✅ Accepted | 0012 | [Endpoint Naming Strategy](./0012-endpoint-naming.md) |
| ✅ Accepted | 0013 | [Filtering data from an endpoint](./0013-filters.md) |
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package uk.gov.justice.digital.hmpps.hmppsintegrationapi.controllers.v1.person

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 org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.expressionOfInterest.ExpressionInterest
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.NomisNumber
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.Response
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps.UpstreamApiError
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.services.ExpressionInterestService
import uk.gov.justice.digital.hmpps.hmppsintegrationapi.services.GetPersonService

@RestController
@RequestMapping("/v1/persons")
@Tag(name = "persons")
class ExpressionInterestController(
@Autowired val getPersonService: GetPersonService,
@Autowired val expressionInterestService: ExpressionInterestService,
) {
val logger: Logger = LoggerFactory.getLogger(this::class.java)

@PutMapping("{hmppsId}/expression-of-interest/jobs/{jobid}")
@Operation(
summary = "Returns completed response",
responses = [
ApiResponse(responseCode = "200", useReturnTypeSchema = true, description = "Successfully submitted an expression of interest"),
ApiResponse(responseCode = "403", useReturnTypeSchema = true, description = "Access is forbidden"),
ApiResponse(responseCode = "404", useReturnTypeSchema = true, description = "Not found"),
],
)
fun submitExpressionOfInterest(
@Parameter(description = "A URL-encoded HMPPS identifier", example = "2008%2F0545166T") @PathVariable hmppsId: String,
@Parameter(description = "A job identifier") @PathVariable jobid: String,
): ResponseEntity<Void> {
try {
val hmppsIdCheck = getPersonService.getNomisNumber(hmppsId)

if (hmppsIdCheck.hasError(UpstreamApiError.Type.ENTITY_NOT_FOUND)) {
logger.info("Could not find nomis number for hmppsId: $hmppsId")
return ResponseEntity.badRequest().build()
}
Comment on lines +48 to +51
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we return 404 instead of 400?


if (hmppsIdCheck.hasError(UpstreamApiError.Type.BAD_REQUEST)) {
logger.info("Invalid hmppsId: $hmppsId")
return ResponseEntity.badRequest().build()
Comment on lines +53 to +55
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar question... is this 404 or 400?
if the hmpps ID isn't found

}

val verifiedNomisNumber = getVerifiedNomisNumber(hmppsIdCheck) ?: return ResponseEntity.badRequest().build()
expressionInterestService.sendExpressionOfInterest(ExpressionInterest(jobid, verifiedNomisNumber))

return ResponseEntity.ok().build()
} catch (e: Exception) {
logger.info("ExpressionInterestController: Unable to send message: ${e.message}")
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build<Void>()
}
}

fun getVerifiedNomisNumber(nomisNumberResponse: Response<NomisNumber?>): String? {
return nomisNumberResponse.data?.nomisNumber
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package uk.gov.justice.digital.hmpps.hmppsintegrationapi.exception

class MessageFailedException(msg: String) : RuntimeException(msg)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest to use our domain area or product name for naming the parent folder, instead of expressionOfInterest
I saw PLP and many others just put inside models.hmpps. Is this okay?

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.expressionOfInterest

data class ExpressionInterest(
val jobId: String,
val hmppsId: String?,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package uk.gov.justice.digital.hmpps.hmppsintegrationapi.models.hmpps

data class ExpressionOfInterestMessage(
val jobId: String,
val verifiedHmppsId: String?,
val eventType: String = "ExpressionOfInterest",
)
Loading
Loading