Skip to content

Commit 3fd8fa6

Browse files
authored
feat: #2240 fetch codes by activity name (#2270)
1 parent 4297479 commit 3fd8fa6

File tree

13 files changed

+260
-77
lines changed

13 files changed

+260
-77
lines changed

frontend/src/api-service/ApiConfig.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ const ApiConfig = {
6363

6464
purityTest: `${oracleServerHost}/api/purity-tests`,
6565

66-
searchTestActivities: `${oracleServerHost}/api/testing-activities`
66+
searchTestActivities: `${oracleServerHost}/api/testing-activities`,
67+
68+
testCodes: `${oracleServerHost}/api/test-codes`
6769
};
6870

6971
export default ApiConfig;

frontend/src/api-service/consep/searchTestingActivitiesAPI.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import ApiConfig from '../ApiConfig';
22
import api from '../api';
33

44
import { ActivitySearchRequest } from '../../views/CONSEP/TestingActivities/TestSearch/definitions';
5-
import { PaginatedTestingSearchResponseType, TestCodeType } from '../../types/consep/TestingSearchType';
5+
import { PaginatedTestingSearchResponseType } from '../../types/consep/TestingSearchType';
66

77
export const searchTestingActivities = (
88
filter: ActivitySearchRequest,
@@ -21,13 +21,3 @@ export const searchTestingActivities = (
2121
return api.post(url, filter)
2222
.then((res): PaginatedTestingSearchResponseType => res.data);
2323
};
24-
25-
export const getTestTypeCodes = () => {
26-
const url = `${ApiConfig.searchTestActivities}/type-codes`;
27-
return api.get(url).then((res): TestCodeType[] => res.data);
28-
};
29-
30-
export const getTestCategoryCodes = () => {
31-
const url = `${ApiConfig.searchTestActivities}/category-codes`;
32-
return api.get(url).then((res): TestCodeType[] => res.data);
33-
};
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import ApiConfig from '../ApiConfig';
2+
import api from '../api';
3+
4+
import { TestCodeType } from '../../types/consep/TestingSearchType';
5+
6+
export const getTestTypeCodes = () => {
7+
const url = `${ApiConfig.testCodes}/type`;
8+
return api.get(url).then((res): TestCodeType[] => res.data);
9+
};
10+
11+
export const getTestCategoryCodes = () => {
12+
const url = `${ApiConfig.testCodes}/category`;
13+
return api.get(url).then((res): TestCodeType[] => res.data);
14+
};
15+
16+
export const getCodesByActivity = (activity: string) => {
17+
const url = `${ApiConfig.testCodes}/by-activity?activity=${encodeURIComponent(activity)}`;
18+
return api.get(url).then((res): string[] => res.data);
19+
};

frontend/src/views/CONSEP/TestingActivities/PurityContent/index.tsx

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
} from '../../../../types/consep/TestingActivityType';
3636
import ComboBoxEvent from '../../../../types/ComboBoxEvent';
3737
import testingActivitiesAPI from '../../../../api-service/consep/testingActivitiesAPI';
38+
import { getCodesByActivity } from '../../../../api-service/consep/testCodesAPI';
3839
import { deleteImpurity, patchImpurities } from '../../../../api-service/consep/impuritiesAPI';
3940
import { initReplicatesList } from '../../../../utils/TestActivitiesUtils';
4041
import { utcToIsoSlashStyle } from '../../../../utils/DateUtils';
@@ -78,6 +79,11 @@ const PurityContent = () => {
7879
refetchOnMount: true
7980
});
8081

82+
const impurityCodesQuery = useQuery({
83+
queryKey: ['impurityCodes'],
84+
queryFn: () => getCodesByActivity('DEBRIS_TYPE_CD')
85+
});
86+
8187
const updateImpuritiesMutation = useMutation({
8288
mutationFn: (impurityPayload: ImpurityPayload[]) => patchImpurities(
8389
riaKey || '',
@@ -382,6 +388,12 @@ const PurityContent = () => {
382388
}
383389
];
384390

391+
const {
392+
data: impurityCodes,
393+
isPending: isImpurityCodesPending,
394+
isError: isImpurityCodesError
395+
} = impurityCodesQuery;
396+
385397
const impurityPerReplicate = (impurity: SingleImpurityType, replicateNumber: number) => (
386398
<Row key={`impurity-${replicateNumber}-${impurity.debrisRank}`} className="consep-impurity-content">
387399
<Column sm={2} md={2} lg={2} xlg={2}>
@@ -400,16 +412,25 @@ const PurityContent = () => {
400412
className="consep-impurity-combobox"
401413
id={`impurity-${impurity.debrisRank}-${impurity.debrisCategory}`}
402414
name={fieldsConfig.impuritySection.secondaryfieldName}
403-
items={fieldsConfig.impuritySection.options}
404-
placeholder={fieldsConfig.impuritySection.placeholder}
415+
items={impurityCodes ?? []}
416+
placeholder={(() => {
417+
if (isImpurityCodesPending) return 'Loading debris types...';
418+
if (isImpurityCodesError) return 'Failed to load debris types';
419+
return fieldsConfig.impuritySection.placeholder;
420+
})()}
405421
titleText={
406422
impurity.debrisRank === 1
407423
? fieldsConfig.impuritySection.secondaryfieldName
408424
: ''
409425
}
410426
value={impurity.debrisCategory}
427+
disabled={isImpurityCodesPending || isImpurityCodesError}
411428
onChange={(e: ComboBoxEvent) => {
412429
const { selectedItem } = e;
430+
if (!selectedItem) {
431+
return;
432+
}
433+
413434
updateImpuritiesMutation.mutate([
414435
{
415436
replicateNumber,
@@ -419,6 +440,7 @@ const PurityContent = () => {
419440
]);
420441
}}
421442
/>
443+
422444
</Column>
423445
<Column className="consep-impurity-content-remove" sm={1} md={1} lg={2} xlg={2}>
424446
<Button

frontend/src/views/CONSEP/TestingActivities/TestSearch/AdvancedFilter/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
import ComboBoxEvent from '../../../../../types/ComboBoxEvent';
1919
import { capitalizeFirstLetter } from '../../../../../utils/StringUtils';
2020
import useWindowSize from '../../../../../hooks/UseWindowSize';
21-
import { getTestCategoryCodes } from '../../../../../api-service/consep/searchTestingActivitiesAPI';
21+
import { getTestCategoryCodes } from '../../../../../api-service/consep/testCodesAPI';
2222

2323
import type { TestCodeType } from '../../../../../types/consep/TestingSearchType';
2424
import {

frontend/src/views/CONSEP/TestingActivities/TestSearch/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ import { mkConfig, generateCsv, download } from 'export-to-csv';
2222

2323
import Breadcrumbs from '../../../../components/Breadcrumbs';
2424
import PageTitle from '../../../../components/PageTitle';
25-
import { searchTestingActivities, getTestTypeCodes } from '../../../../api-service/consep/searchTestingActivitiesAPI';
25+
import { searchTestingActivities } from '../../../../api-service/consep/searchTestingActivitiesAPI';
26+
import { getTestTypeCodes } from '../../../../api-service/consep/testCodesAPI';
2627
import ComboBoxEvent from '../../../../types/ComboBoxEvent';
2728
import type {
2829
TestingSearchResponseType,

oracle-api/src/main/java/ca/bc/gov/oracleapi/endpoint/consep/ActivitySearchEndpoint.java

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,20 @@
22

33
import ca.bc.gov.oracleapi.dto.consep.ActivitySearchPageResponseDto;
44
import ca.bc.gov.oracleapi.dto.consep.ActivitySearchRequestDto;
5-
import ca.bc.gov.oracleapi.dto.consep.TestCodeDto;
65
import ca.bc.gov.oracleapi.security.RoleAccessConfig;
76
import ca.bc.gov.oracleapi.service.consep.ActivitySearchService;
8-
import ca.bc.gov.oracleapi.service.consep.TestCodeService;
97
import io.swagger.v3.oas.annotations.media.Content;
108
import io.swagger.v3.oas.annotations.media.Schema;
119
import io.swagger.v3.oas.annotations.responses.ApiResponse;
1210
import io.swagger.v3.oas.annotations.responses.ApiResponses;
1311
import io.swagger.v3.oas.annotations.tags.Tag;
1412
import jakarta.validation.Valid;
15-
import java.util.List;
1613
import lombok.RequiredArgsConstructor;
1714
import org.springdoc.core.annotations.ParameterObject;
1815
import org.springframework.data.domain.Pageable;
1916
import org.springframework.data.web.PageableDefault;
2017
import org.springframework.http.HttpStatus;
2118
import org.springframework.validation.annotation.Validated;
22-
import org.springframework.web.bind.annotation.GetMapping;
2319
import jakarta.validation.constraints.Pattern;
2420
import org.springframework.web.bind.annotation.PostMapping;
2521
import org.springframework.web.bind.annotation.RequestBody;
@@ -40,7 +36,6 @@
4036
@Tag(name = "TestingActivitySearch", description = "Resource to search testing activities.")
4137
public class ActivitySearchEndpoint {
4238
private final ActivitySearchService activitySearchService;
43-
private final TestCodeService testCodeService;
4439

4540
@PostMapping("/search")
4641
@ApiResponses(value = {
@@ -87,38 +82,4 @@ public ActivitySearchPageResponseDto searchTestingActivities(
8782
sortDirection
8883
);
8984
}
90-
91-
@GetMapping("/type-codes")
92-
@ApiResponses(value = {
93-
@ApiResponse(
94-
responseCode = "200",
95-
description = "Successfully retrieved all valid test activity type codes."
96-
),
97-
@ApiResponse(
98-
responseCode = "401",
99-
description = "Access token is missing or invalid",
100-
content = @Content(schema = @Schema(implementation = Void.class))
101-
)
102-
})
103-
@RoleAccessConfig({ "SPAR_TSC_SUBMITTER", "SPAR_TSC_SUPERVISOR" })
104-
public List<TestCodeDto> getTestTypeCodes() {
105-
return testCodeService.getTestTypeCodes();
106-
}
107-
108-
@GetMapping("/category-codes")
109-
@ApiResponses(value = {
110-
@ApiResponse(
111-
responseCode = "200",
112-
description = "Successfully retrieved all valid test category codes."
113-
),
114-
@ApiResponse(
115-
responseCode = "401",
116-
description = "Access token is missing or invalid",
117-
content = @Content(schema = @Schema(implementation = Void.class))
118-
)
119-
})
120-
@RoleAccessConfig({ "SPAR_TSC_SUBMITTER", "SPAR_TSC_SUPERVISOR" })
121-
public List<TestCodeDto> getTestCategoryCodes() {
122-
return testCodeService.getTestCategoryCodes();
123-
}
12485
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package ca.bc.gov.oracleapi.endpoint.consep;
2+
3+
import ca.bc.gov.oracleapi.dto.consep.TestCodeDto;
4+
import ca.bc.gov.oracleapi.security.RoleAccessConfig;
5+
import ca.bc.gov.oracleapi.service.consep.TestCodeService;
6+
import io.swagger.v3.oas.annotations.Parameter;
7+
import io.swagger.v3.oas.annotations.media.Content;
8+
import io.swagger.v3.oas.annotations.media.Schema;
9+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
10+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
11+
import io.swagger.v3.oas.annotations.tags.Tag;
12+
import java.util.List;
13+
import lombok.RequiredArgsConstructor;
14+
import org.springframework.validation.annotation.Validated;
15+
import org.springframework.web.bind.annotation.GetMapping;
16+
import org.springframework.web.bind.annotation.RequestMapping;
17+
import org.springframework.web.bind.annotation.RequestParam;
18+
import org.springframework.web.bind.annotation.RestController;
19+
20+
/**
21+
* This class exposes test code API.
22+
*/
23+
@RestController
24+
@RequestMapping("/api/test-codes")
25+
@RequiredArgsConstructor
26+
@Validated
27+
@Tag(name = "TestCode", description = "Resource to retrieve test codes.")
28+
public class TestCodeEndpoint {
29+
private final TestCodeService testCodeService;
30+
31+
@GetMapping("/type")
32+
@ApiResponses(value = {
33+
@ApiResponse(
34+
responseCode = "200",
35+
description = "Successfully retrieved all valid test activity type codes."
36+
),
37+
@ApiResponse(
38+
responseCode = "401",
39+
description = "Access token is missing or invalid",
40+
content = @Content(schema = @Schema(implementation = Void.class))
41+
)
42+
})
43+
@RoleAccessConfig({ "SPAR_TSC_SUBMITTER", "SPAR_TSC_SUPERVISOR" })
44+
public List<TestCodeDto> getTestTypeCodes() {
45+
return testCodeService.getTestTypeCodes();
46+
}
47+
48+
@GetMapping("/category")
49+
@ApiResponses(value = {
50+
@ApiResponse(
51+
responseCode = "200",
52+
description = "Successfully retrieved all valid test category codes."
53+
),
54+
@ApiResponse(
55+
responseCode = "401",
56+
description = "Access token is missing or invalid",
57+
content = @Content(schema = @Schema(implementation = Void.class))
58+
)
59+
})
60+
@RoleAccessConfig({ "SPAR_TSC_SUBMITTER", "SPAR_TSC_SUPERVISOR" })
61+
public List<TestCodeDto> getTestCategoryCodes() {
62+
return testCodeService.getTestCategoryCodes();
63+
}
64+
65+
@GetMapping("/by-activity")
66+
@ApiResponses(value = {
67+
@ApiResponse(
68+
responseCode = "200",
69+
description = "Successfully retrieved all valid codes for the specified activity."
70+
),
71+
@ApiResponse(
72+
responseCode = "401",
73+
description = "Access token is missing or invalid",
74+
content = @Content(schema = @Schema(implementation = Void.class))
75+
)})
76+
@RoleAccessConfig({"SPAR_TSC_SUBMITTER", "SPAR_TSC_SUPERVISOR"})
77+
public List<String> getCodesByActivity(
78+
@Parameter(
79+
description = """
80+
Activity name used to filter test codes.
81+
This value corresponds to the columnName field in TestCodeEntity.
82+
Must be a non-blank string.
83+
""",
84+
required = true,
85+
example = "SEEDLOT_TEST")
86+
@RequestParam String activity) {
87+
if (activity == null || activity.trim().isEmpty()) {
88+
throw new IllegalArgumentException("activity must not be blank");
89+
}
90+
return testCodeService.getCodesByColumnActivity(activity);
91+
}
92+
}

oracle-api/src/main/java/ca/bc/gov/oracleapi/repository/consep/TestCodeRepository.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,14 @@ public interface TestCodeRepository extends JpaRepository<TestCodeEntity, String
2727
AND (t.expiryDate IS NULL OR t.expiryDate >= CURRENT_DATE)
2828
""")
2929
List<Object[]> findTestCategoryCodes();
30+
31+
@Query("""
32+
SELECT t.codeArgument AS code
33+
FROM TestCodeEntity t
34+
WHERE t.columnName = :activity
35+
AND t.effectiveDate <= CURRENT_DATE
36+
AND (t.expiryDate IS NULL OR t.expiryDate >= CURRENT_DATE)
37+
ORDER BY t.codeArgument
38+
""")
39+
List<Object[]> findCodesByActivity(String activity);
3040
}

oracle-api/src/main/java/ca/bc/gov/oracleapi/service/consep/TestCodeService.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package ca.bc.gov.oracleapi.service.consep;
22

33
import ca.bc.gov.oracleapi.dto.consep.TestCodeDto;
4+
import ca.bc.gov.oracleapi.entity.consep.TestCodeEntity;
45
import ca.bc.gov.oracleapi.repository.consep.TestCodeRepository;
56
import java.util.List;
67
import lombok.RequiredArgsConstructor;
@@ -42,8 +43,27 @@ public List<TestCodeDto> getTestCategoryCodes() {
4243
.stream()
4344
.map(obj -> new TestCodeDto(
4445
(String) obj[0],
45-
(String) obj[1]
46-
))
46+
(String) obj[1]))
47+
.toList();
48+
}
49+
50+
/**
51+
* Retrieves all valid code values for a specific test-code activity.
52+
* <p>
53+
* The {@code activity} parameter represents a value of the
54+
* {@code columnName} field in {@link TestCodeEntity}, such as
55+
* {@code "DEBRIS_TYPE_CD"}, rather than an arbitrary database column name.
56+
* Only codes that are currently effective (effectiveDate <= today and
57+
* expiryDate >= today or null) are returned.
58+
*
59+
* @param activity the test code activity identifier (e.g. "DEBRIS_TYPE_CD")
60+
* used to filter codes by {@code TestCodeEntity.columnName}
61+
* @return list of valid code values as strings, sorted for consistent display
62+
*/
63+
public List<String> getCodesByColumnActivity(String activity) {
64+
return testCodeRepository.findCodesByActivity(activity)
65+
.stream()
66+
.map(obj -> (String) obj[0])
4767
.toList();
4868
}
4969
}

0 commit comments

Comments
 (0)