Skip to content

Commit f8356a1

Browse files
committed
subs endpoint first pass
1 parent d271cd3 commit f8356a1

File tree

7 files changed

+193
-4
lines changed

7 files changed

+193
-4
lines changed

app/uk/gov/hmrc/senioraccountingofficer/config/AppConfig.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,12 @@
1717
package uk.gov.hmrc.senioraccountingofficer.config
1818

1919
import play.api.Configuration
20+
import uk.gov.hmrc.play.bootstrap.config.ServicesConfig
2021

21-
import javax.inject.{Inject, Singleton}
22+
import javax.inject.Inject
2223

23-
@Singleton
24-
class AppConfig @Inject() (config: Configuration) {
24+
class AppConfig @Inject() (servicesConfig: ServicesConfig, config: Configuration) {
2525

2626
val appName: String = config.get[String]("appName")
27+
val stubsBaseUrl: String = servicesConfig.baseUrl("senior-accounting-officer-stubs")
2728
}

app/uk/gov/hmrc/senioraccountingofficer/config/Module.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717
package uk.gov.hmrc.senioraccountingofficer.config
1818

1919
import com.google.inject.AbstractModule
20+
import uk.gov.hmrc.senioraccountingofficer.connectors.{DefaultSubscriptionsConnector, SubscriptionsConnector}
2021

2122
class Module extends AbstractModule {
2223

2324
override def configure(): Unit = {
2425

2526
bind(classOf[AppConfig]).asEagerSingleton()
27+
bind(classOf[SubscriptionsConnector]).to(classOf[DefaultSubscriptionsConnector])
2628
}
2729
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2026 HM Revenue & Customs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package uk.gov.hmrc.senioraccountingofficer.connectors
18+
19+
import play.api.http.MimeTypes
20+
import play.api.libs.ws.writeableOf_String
21+
import uk.gov.hmrc.http.HttpReads.Implicits.*
22+
import uk.gov.hmrc.http.client.HttpClientV2
23+
import uk.gov.hmrc.http.{HeaderCarrier, HttpResponse, StringContextOps}
24+
import uk.gov.hmrc.senioraccountingofficer.config.AppConfig
25+
26+
import scala.concurrent.{ExecutionContext, Future}
27+
28+
import javax.inject.Inject
29+
30+
trait SubscriptionsConnector {
31+
def putSubscription(body: String)(using HeaderCarrier): Future[HttpResponse]
32+
}
33+
34+
class DefaultSubscriptionsConnector @Inject() (httpClient: HttpClientV2, appConfig: AppConfig)(using
35+
ExecutionContext
36+
) extends SubscriptionsConnector {
37+
38+
override def putSubscription(body: String)(using HeaderCarrier): Future[HttpResponse] =
39+
httpClient
40+
.put(url"${appConfig.stubsBaseUrl}/subscriptions")
41+
.setHeader("Content-Type" -> MimeTypes.JSON)
42+
.withBody(body)
43+
.execute[HttpResponse]
44+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2026 HM Revenue & Customs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package uk.gov.hmrc.senioraccountingofficer.controllers
18+
19+
import play.api.http.MimeTypes
20+
import play.api.mvc.{Action, ControllerComponents}
21+
import uk.gov.hmrc.http.HeaderCarrier
22+
import uk.gov.hmrc.play.bootstrap.backend.controller.BackendController
23+
import uk.gov.hmrc.play.http.HeaderCarrierConverter
24+
import uk.gov.hmrc.senioraccountingofficer.connectors.SubscriptionsConnector
25+
26+
import scala.concurrent.ExecutionContext
27+
28+
import javax.inject.Inject
29+
30+
class SubscriptionsController @Inject() (
31+
cc: ControllerComponents,
32+
subscriptionsConnector: SubscriptionsConnector
33+
)(using ec: ExecutionContext)
34+
extends BackendController(cc) {
35+
36+
def putSubscription: Action[String] = Action.async(parse.tolerantText) { implicit request =>
37+
given HeaderCarrier = HeaderCarrierConverter.fromRequest(request)
38+
39+
subscriptionsConnector.putSubscription(request.body).map { response =>
40+
if response.body.isBlank then Status(response.status)
41+
else Status(response.status)(response.body).as(MimeTypes.JSON)
42+
}
43+
}
44+
}

conf/app.routes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
# microservice specific routes
22

33
GET /hello-world uk.gov.hmrc.senioraccountingofficer.controllers.MicroserviceHelloWorldController.hello()
4+
PUT /subscriptions uk.gov.hmrc.senioraccountingofficer.controllers.SubscriptionsController.putSubscription

conf/application.conf

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ microservice {
6565
host = localhost
6666
port = 8500
6767
}
68+
senior-accounting-officer-stubs {
69+
host = localhost
70+
port = 10061
71+
}
6872
}
6973
}
70-
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright 2026 HM Revenue & Customs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package uk.gov.hmrc.senioraccountingofficer.controllers
18+
19+
import org.scalatest.matchers.should.Matchers
20+
import org.scalatest.wordspec.AnyWordSpec
21+
import org.scalatestplus.play.guice.GuiceOneAppPerSuite
22+
import org.apache.pekko.stream.Materializer
23+
import play.api.http.HeaderNames.CONTENT_TYPE
24+
import play.api.http.Status
25+
import play.api.test.Helpers.{contentAsString, defaultAwaitTimeout, status, stubControllerComponents}
26+
import play.api.test.FakeRequest
27+
import uk.gov.hmrc.http.{HeaderCarrier, HttpResponse}
28+
import uk.gov.hmrc.senioraccountingofficer.connectors.SubscriptionsConnector
29+
30+
import scala.concurrent.{ExecutionContext, Future}
31+
32+
class SubscriptionsControllerSpec extends AnyWordSpec with Matchers with GuiceOneAppPerSuite {
33+
34+
given ExecutionContext = ExecutionContext.global
35+
given Materializer = app.materializer
36+
37+
private val validPayload =
38+
"""{
39+
| "safeId": "XE000123456789",
40+
| "company": {
41+
| "companyName": "Acme Manufacturing Ltd",
42+
| "uniqueTaxReference": "1234567890",
43+
| "companyRegistrationNumber": "OC123456"
44+
| },
45+
| "contacts": [
46+
| {
47+
| "name": "Jane Doe",
48+
| "email": "jane.doe@example.com"
49+
| }
50+
| ]
51+
|}""".stripMargin
52+
53+
"SubscriptionsController" should {
54+
"return the downstream status when the stub responds without a body" in {
55+
val controller = new SubscriptionsController(
56+
stubControllerComponents(),
57+
new StubSubscriptionsConnector(Future.successful(HttpResponse(status = Status.NO_CONTENT)))
58+
)
59+
60+
val result = controller.putSubscription(
61+
FakeRequest("PUT", "/subscriptions")
62+
.withHeaders(CONTENT_TYPE -> "application/json")
63+
.withTextBody(validPayload)
64+
)
65+
66+
status(result) shouldBe Status.NO_CONTENT
67+
}
68+
69+
"return the downstream JSON body for validation errors" in {
70+
val downstreamBody = """[{"path":"safeId","reason":"INVALID_FORMAT"}]"""
71+
val controller = new SubscriptionsController(
72+
stubControllerComponents(),
73+
new StubSubscriptionsConnector(Future.successful(HttpResponse(status = Status.BAD_REQUEST, body = downstreamBody)))
74+
)
75+
76+
val result = controller.putSubscription(
77+
FakeRequest("PUT", "/subscriptions")
78+
.withHeaders(CONTENT_TYPE -> "application/json")
79+
.withTextBody(validPayload)
80+
)
81+
82+
status(result) shouldBe Status.BAD_REQUEST
83+
contentAsString(result) shouldBe downstreamBody
84+
}
85+
86+
}
87+
88+
private class StubSubscriptionsConnector(
89+
result: Future[HttpResponse]
90+
) extends SubscriptionsConnector {
91+
override def putSubscription(body: String)(using HeaderCarrier): Future[HttpResponse] =
92+
result
93+
}
94+
}

0 commit comments

Comments
 (0)