Skip to content

Commit ab403ea

Browse files
committed
control Show PA API switch from query param
1 parent f5c81b0 commit ab403ea

File tree

9 files changed

+93
-66
lines changed

9 files changed

+93
-66
lines changed

newswires/app/controllers/QueryController.scala

Lines changed: 53 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -54,55 +54,60 @@ class QueryController(
5454
hasDataFormatting: Option[Boolean],
5555
guSourceFeeds: List[String],
5656
guSourceFeedsExcl: List[String]
57-
): Action[AnyContent] = apiAuthAction { request: UserRequest[AnyContent] =>
58-
val baseParams = BaseRequestParams(
59-
maybeFreeTextQuery = maybeFreeTextQuery,
60-
keywords = keywords,
61-
suppliers = suppliers,
62-
categoryCode = categoryCode,
63-
categoryCodeExcl = categoryCodeExcl,
64-
maybeCollectionId = maybeCollectionId,
65-
maybeStart = maybeStart,
66-
maybeEnd = maybeEnd,
67-
maybeBeforeTimeStamp = maybeBeforeTimeStamp,
68-
maybeAfterTimeStamp = maybeAfterTimeStamp,
69-
hasDataFormatting = hasDataFormatting,
70-
guSourceFeeds = guSourceFeeds,
71-
guSourceFeedsExcl = guSourceFeedsExcl
72-
)
73-
val searchParams =
74-
SearchParams.build(request.queryString, baseParams, featureSwitchProvider)
75-
76-
val searchPreset = request
77-
.getQueryString("preset")
78-
.flatMap(SearchPresets.get)
79-
80-
val timeStampColumn = maybeCollectionId match {
81-
case Some(id) => AddedToCollectionAtTime(id)
82-
case None => IngestedAtTime
83-
}
84-
val queryParams = QueryParams(
85-
searchParams = searchParams,
86-
searchPreset = searchPreset,
87-
maybeSearchTerm = baseParams.textForHighlighting,
88-
queryCursor = QueryCursor(
57+
): Action[AnyContent] = apiAuthAction {
58+
implicit request: UserRequest[AnyContent] =>
59+
val baseParams = BaseRequestParams(
60+
maybeFreeTextQuery = maybeFreeTextQuery,
61+
keywords = keywords,
62+
suppliers = suppliers,
63+
categoryCode = categoryCode,
64+
categoryCodeExcl = categoryCodeExcl,
65+
maybeCollectionId = maybeCollectionId,
66+
maybeStart = maybeStart,
67+
maybeEnd = maybeEnd,
8968
maybeBeforeTimeStamp = maybeBeforeTimeStamp,
90-
maybeAfterTimeStamp = maybeAfterTimeStamp.map(NextPage(_))
91-
),
92-
pageSize = 30,
93-
timeStampColumn = timeStampColumn
94-
)
95-
96-
val queryResponse = FingerpostWireEntry.query(
97-
queryParams
98-
)
99-
100-
Ok(
101-
QueryResponse
102-
.display(queryResponse, request.user.username, timeStampColumn)
103-
.asJson
104-
.spaces2
105-
)
69+
maybeAfterTimeStamp = maybeAfterTimeStamp,
70+
hasDataFormatting = hasDataFormatting,
71+
guSourceFeeds = guSourceFeeds,
72+
guSourceFeedsExcl = guSourceFeedsExcl
73+
)
74+
val searchParams =
75+
SearchParams.build(
76+
request.queryString,
77+
baseParams,
78+
featureSwitchProvider
79+
)
80+
81+
val searchPreset = request
82+
.getQueryString("preset")
83+
.flatMap(SearchPresets.get)
84+
85+
val timeStampColumn = maybeCollectionId match {
86+
case Some(id) => AddedToCollectionAtTime(id)
87+
case None => IngestedAtTime
88+
}
89+
val queryParams = QueryParams(
90+
searchParams = searchParams,
91+
searchPreset = searchPreset,
92+
maybeSearchTerm = baseParams.textForHighlighting,
93+
queryCursor = QueryCursor(
94+
maybeBeforeTimeStamp = maybeBeforeTimeStamp,
95+
maybeAfterTimeStamp = maybeAfterTimeStamp.map(NextPage(_))
96+
),
97+
pageSize = 30,
98+
timeStampColumn = timeStampColumn
99+
)
100+
101+
val queryResponse = FingerpostWireEntry.query(
102+
queryParams
103+
)
104+
105+
Ok(
106+
QueryResponse
107+
.display(queryResponse, request.user.username, timeStampColumn)
108+
.asJson
109+
.spaces2
110+
)
106111
}
107112

108113
def keywords(

newswires/app/controllers/ViteController.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class ViteController(
7575
val config =
7676
views.html.fragments.clientConfig(
7777
ClientConfig(
78-
featureSwitchProvider.clientSideSwitchStates,
78+
featureSwitchProvider.clientSideSwitchStates(request),
7979
stage = configuration.get[String]("stage"),
8080
sendTelemetryAsDev =
8181
devEmails.exists(request.user.email.startsWith),

newswires/app/models/SearchParams.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package models
22

33
import conf.SearchTerms
4+
import play.api.mvc.Request
45
import service.FeatureSwitchProvider
56

67
case class FilterParams(
@@ -33,7 +34,7 @@ object SearchParams {
3334
query: Map[String, Seq[String]],
3435
baseParams: BaseRequestParams,
3536
featureSwitch: FeatureSwitchProvider
36-
) = {
37+
)(implicit req: Request[_]) = {
3738
SearchParams(
3839
FilterParams(
3940
searchTerms = baseParams.textSearchTerms,
@@ -42,7 +43,7 @@ object SearchParams {
4243
suppliersIncl = baseParams.suppliers,
4344
suppliersExcl = computeSupplierExcl(
4445
query,
45-
featureSwitch.ShowGuSuppliers.isOn(),
46+
featureSwitch.ShowGuSuppliers.isOn(req),
4647
baseParams.suppliers
4748
),
4849
categoryCodesIncl = baseParams.categoryCode,
@@ -53,7 +54,7 @@ object SearchParams {
5354
collectionId = baseParams.maybeCollectionId,
5455
guSourceFeeds = baseParams.guSourceFeeds,
5556
guSourceFeedsExcl = computeGuSourceFeedExcl(
56-
showPAAPI = featureSwitch.ShowPAAPI.isOn(),
57+
showPAAPI = featureSwitch.ShowPAAPI.isOn(req),
5758
guSourceFeeds = baseParams.guSourceFeeds,
5859
guSourceFeedsExcl = baseParams.guSourceFeedsExcl
5960
)
Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package service
22

3-
import play.api.libs.json.{Json, OFormat}
3+
import play.api.mvc.Request
44

55
sealed trait SwitchState
66
case object On extends SwitchState
@@ -10,7 +10,7 @@ case class FeatureSwitch(
1010
description: String,
1111
exposeToClient: Boolean = false,
1212
private val safeState: SwitchState,
13-
isOn: () => Boolean
13+
isOn: Request[_] => Boolean
1414
)
1515
class FeatureSwitchProvider(stage: String) {
1616

@@ -20,7 +20,7 @@ class FeatureSwitchProvider(stage: String) {
2020
safeState = Off,
2121
description = "Show suppliers from the Guardian",
2222
exposeToClient = true,
23-
isOn = () => stage.toUpperCase() != "PROD"
23+
isOn = _ => stage.toUpperCase() != "PROD"
2424
)
2525

2626
val ShowPAAPI: FeatureSwitch =
@@ -29,12 +29,14 @@ class FeatureSwitchProvider(stage: String) {
2929
safeState = Off,
3030
description = "Show new PA API in the feed",
3131
exposeToClient = true,
32-
isOn = () => stage.toUpperCase() != "PROD"
32+
isOn = _.getQueryString("previewPaApi")
33+
.flatMap(_.toBooleanOption)
34+
.getOrElse(false)
3335
)
3436
private val switches = List(
3537
ShowGuSuppliers,
3638
ShowPAAPI
3739
)
38-
def clientSideSwitchStates: Map[String, Boolean] =
39-
switches.filter(_.exposeToClient).map(s => s.name -> s.isOn()).toMap
40+
def clientSideSwitchStates(request: Request[_]): Map[String, Boolean] =
41+
switches.filter(_.exposeToClient).map(s => s.name -> s.isOn(request)).toMap
4042
}

newswires/client/src/SearchSummary.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const SearchTermBadgeLabelLookup: Record<DeselectableQueryKey, string> = {
2929
categoryCode: 'Category',
3030
categoryCodeExcl: '(NOT) Category',
3131
hasDataFormatting: 'Has data formatting',
32+
previewPaApi: 'Previewing PA API switchover',
3233
keyword: 'Keyword',
3334
keywordExcl: '(NOT) Keyword',
3435
guSourceFeed: 'Source feed',
@@ -98,6 +99,7 @@ const Summary = ({
9899
categoryCode,
99100
categoryCodeExcl,
100101
hasDataFormatting,
102+
previewPaApi,
101103
collectionId,
102104
} = query;
103105

@@ -121,6 +123,7 @@ const Summary = ({
121123
displayGuSourceFeeds ||
122124
displayExcludedGuSourceFeeds ||
123125
hasDataFormatting !== undefined ||
126+
previewPaApi !== undefined ||
124127
collectionId !== undefined;
125128

126129
const isDefaultDateRange =
@@ -235,6 +238,14 @@ const Summary = ({
235238
}}
236239
/>
237240
)}
241+
{previewPaApi !== undefined && (
242+
<SummaryBadge
243+
keyValuePair={{
244+
key: 'previewPaApi',
245+
value: previewPaApi ? 'true' : 'false',
246+
}}
247+
/>
248+
)}
238249
</>
239250
);
240251
};

newswires/client/src/queryHelpers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export const queryAfterDeselection = (
5353
'keywordExcl',
5454
'guSourceFeed',
5555
'guSourceFeedExcl',
56+
'previewPaApi',
5657
].includes(key)
5758
) {
5859
const current = query[key] as string[] | undefined;

newswires/client/src/sharedTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ export const BaseQuerySchema = z.object({
165165
start: EuiDateStringSchema.optional(),
166166
end: EuiDateStringSchema.optional(),
167167
hasDataFormatting: z.boolean().optional(),
168+
previewPaApi: z.boolean().optional(),
168169
});
169170
export type BaseQuery = z.infer<typeof BaseQuerySchema>;
170171

newswires/client/src/urlState.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export const defaultQuery: Query = {
1818
start: DEFAULT_DATE_RANGE.start,
1919
end: DEFAULT_DATE_RANGE.end,
2020
hasDataFormatting: undefined,
21+
previewPaApi: undefined,
2122
collectionId: undefined,
2223
};
2324

@@ -70,6 +71,9 @@ function searchParamsToQuery(params: URLSearchParams): Query {
7071
const hasDataFormatting = maybeStringToBooleanOrUndefined(
7172
params.get('hasDataFormatting'),
7273
);
74+
const previewPaApi = maybeStringToBooleanOrUndefined(
75+
params.get('previewPaApi'),
76+
);
7377
let collectionId: number | undefined = undefined;
7478

7579
try {
@@ -101,6 +105,7 @@ function searchParamsToQuery(params: URLSearchParams): Query {
101105
start,
102106
end,
103107
hasDataFormatting,
108+
previewPaApi,
104109
};
105110

106111
// we're treating preset and collectionId as mutually exclusive - if both are present, preset takes precedence and collectionId is ignored

newswires/test/models/SearchParamsSpec.scala

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import org.mockito.Mockito.when
77
import org.scalatest.flatspec.AnyFlatSpec
88
import org.scalatest.matchers.should.Matchers.convertToAnyShouldWrapper
99
import org.scalatestplus.mockito.MockitoSugar.mock
10+
import play.api.test.FakeRequest
1011
import service.{FeatureSwitch, FeatureSwitchProvider, Off}
1112

1213
class SearchParamsSpec extends AnyFlatSpec with models {
@@ -18,7 +19,7 @@ class SearchParamsSpec extends AnyFlatSpec with models {
1819
emptyQueryString,
1920
emptyBaseParams,
2021
featureSwitchesOn
21-
) shouldEqual emptySearchParams.copy(filters =
22+
)(FakeRequest()) shouldEqual emptySearchParams.copy(filters =
2223
emptyFilterParams.copy(suppliersExcl = List("UNAUTHED_EMAIL_FEED"))
2324
)
2425
}
@@ -28,7 +29,7 @@ class SearchParamsSpec extends AnyFlatSpec with models {
2829
emptyQueryString,
2930
baseParams,
3031
featureSwitchesOn
31-
)
32+
)(FakeRequest())
3233
result.filters.searchTerms shouldEqual Some(
3334
ComboTerm(
3435
List(SearchTerm.English("query"), SearchTerm.Simple("query", Slug)),
@@ -42,7 +43,7 @@ class SearchParamsSpec extends AnyFlatSpec with models {
4243
Map("keywordExcl" -> Seq("a", "b")),
4344
emptyBaseParams,
4445
featureSwitchesOn
45-
)
46+
)(FakeRequest())
4647
result.filters.keywordExcl shouldEqual List("a", "b")
4748
}
4849

@@ -64,7 +65,7 @@ class SearchParamsSpec extends AnyFlatSpec with models {
6465
emptyQueryString,
6566
baseParams,
6667
featureSwitchesOn
67-
)
68+
)(FakeRequest())
6869
result shouldEqual SearchParams(
6970
FilterParams(
7071
searchTerms = Some(
@@ -99,7 +100,7 @@ class SearchParamsSpec extends AnyFlatSpec with models {
99100
emptyQueryString,
100101
emptyBaseParams,
101102
showGuSuppliersFeatureOff
102-
)
103+
)(FakeRequest())
103104
result.filters.suppliersExcl shouldEqual List(
104105
"UNAUTHED_EMAIL_FEED",
105106
"GuReuters",
@@ -210,12 +211,12 @@ trait searchParamsMocks {
210211
safeState = Off,
211212
description = "",
212213
exposeToClient = true,
213-
isOn = () => true
214+
isOn = _ => true
214215
)
215216
when(featureSwitchesOn.ShowGuSuppliers).thenReturn(featureSwitch)
216217
when(featureSwitchesOn.ShowPAAPI).thenReturn(featureSwitch)
217218
when(showGuSuppliersFeatureOff.ShowGuSuppliers).thenReturn(
218-
featureSwitch.copy(isOn = () => false)
219+
featureSwitch.copy(isOn = _ => false)
219220
)
220221
when(showGuSuppliersFeatureOff.ShowPAAPI).thenReturn(
221222
featureSwitch
@@ -224,6 +225,6 @@ trait searchParamsMocks {
224225
featureSwitch
225226
)
226227
when(showPAAPIFeatureOff.ShowPAAPI).thenReturn(
227-
featureSwitch.copy(isOn = () => false)
228+
featureSwitch.copy(isOn = _ => false)
228229
)
229230
}

0 commit comments

Comments
 (0)