Skip to content

Commit 0fca397

Browse files
authored
Address SonarQube issues (#66)
* gh-65 address SonarQube issues Signed-off-by: Victor Chang <[email protected]>
1 parent 0c9b358 commit 0fca397

File tree

61 files changed

+482
-365
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+482
-365
lines changed

.editorconfig

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44
root = true
55

66
# Copyright File Header
7-
file_header_template = Copyright [year file created] - [last year file modified], MONAI Consortium\nSPDX-License-Identifier: Apache License 2.0
7+
file_header_template = SPDX-FileCopyrightText: © [year file created] - [last year file modified], MONAI Consortium\nSPDX-License-Identifier: Apache License 2.0
88
dotnet_diagnostic.IDE0073.severity = error
99

10-
1110
# Default settings:
1211
# A newline ending every file
1312
# Use 4 spaces as indentation

.github/workflows/ci.yml

+26-43
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@ on:
99
push:
1010
paths-ignore:
1111
- 'demos/**'
12-
pull_request:
13-
paths-ignore:
14-
- 'demos/**'
1512

1613
# Allows you to run this workflow manually from the Actions tab
1714
workflow_dispatch:
@@ -90,19 +87,15 @@ jobs:
9087

9188
- name: Perform CodeQL Analysis
9289
uses: github/codeql-action/analyze@v1
90+
9391

9492
unit-test:
95-
runs-on: ${{ matrix.os }}
96-
strategy:
97-
matrix:
98-
os: [ubuntu-latest]
99-
fail-fast: true
100-
93+
runs-on: windows-latest
10194
steps:
102-
- name: Checkout repository
103-
uses: actions/checkout@v2
95+
- name: Set up JDK 11
96+
uses: actions/setup-java@v1
10497
with:
105-
fetch-depth: 0
98+
java-version: 1.11
10699

107100
- uses: actions/setup-dotnet@v1
108101
with:
@@ -116,67 +109,57 @@ jobs:
116109
restore-keys: |
117110
${{ runner.os }}-nuget
118111
119-
- name: Restore dependencies
120-
run: dotnet restore
121-
working-directory: ./src
122-
123-
- name: Build Solution
124-
run: dotnet build -c ${{ env.BUILD_CONFIG }} --nologo ${{ env.SOLUTION }}
125-
working-directory: ./src
126-
127-
- name: Run Unit Test
128-
run: dotnet test --filter FullyQualifiedName\!~Monai.Deploy.InformaticsGateway.Integration.Test -c ${{ env.BUILD_CONFIG }} -v=minimal --results-directory "${{ env.TEST_RESULTS }}" --collect:"XPlat Code Coverage" --settings coverlet.runsettings ${{ env.SOLUTION }}
129-
working-directory: ./src
130-
131-
- uses: codecov/codecov-action@v2
132-
with:
133-
token: ${{ secrets.CODECOV_TOKEN }}
134-
directory: "src/${{ env.TEST_RESULTS }}"
135-
files: "**/coverage.opencover.xml"
136-
flags: unittests
137-
name: codecov-umbrella
138-
fail_ci_if_error: true
139-
verbose: true
140-
141-
sonar-qube:
142-
runs-on: windows-latest
143-
steps:
144-
- name: Set up JDK 11
145-
uses: actions/setup-java@v1
146-
with:
147-
java-version: 1.11
148112
- uses: actions/checkout@v2
149113
with:
150114
fetch-depth: 0
115+
151116
- name: Cache SonarCloud packages
152117
uses: actions/cache@v1
153118
with:
154119
path: ~\sonar\cache
155120
key: ${{ runner.os }}-sonar
156121
restore-keys: ${{ runner.os }}-sonar
122+
157123
- name: Cache SonarCloud scanner
158124
id: cache-sonar-scanner
159125
uses: actions/cache@v1
160126
with:
161127
path: .\.sonar\scanner
162128
key: ${{ runner.os }}-sonar-scanner
163129
restore-keys: ${{ runner.os }}-sonar-scanner
130+
164131
- name: Install SonarCloud scanner
165132
if: steps.cache-sonar-scanner.outputs.cache-hit != 'true'
166133
shell: powershell
167134
run: |
168135
New-Item -Path .\.sonar\scanner -ItemType Directory
169-
dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner
136+
dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner
137+
138+
- name: Restore dependencies
139+
run: dotnet restore
140+
working-directory: ./src
141+
170142
- name: Build and analyze
171143
env:
172144
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
173145
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
174146
shell: powershell
175147
run: |
176-
.\.sonar\scanner\dotnet-sonarscanner begin /k:"Project-MONAI_monai-deploy-informatics-gateway" /o:"project-monai" /d:sonar.login="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io"
148+
.\.sonar\scanner\dotnet-sonarscanner begin /k:"Project-MONAI_monai-deploy-informatics-gateway" /o:"project-monai" /d:sonar.login="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.opencover.reportsPaths="src\${{ env.TEST_RESULTS }}\**\*.xml"
177149
dotnet build -c ${{ env.BUILD_CONFIG }} --nologo "src\${{ env.SOLUTION }}"
150+
dotnet test --no-build --filter FullyQualifiedName\!~Monai.Deploy.InformaticsGateway.Integration.Test -c ${{ env.BUILD_CONFIG }} -v=minimal --results-directory "src\${{ env.TEST_RESULTS }}" --collect:"XPlat Code Coverage" --settings src\coverlet.runsettings "src\${{ env.SOLUTION }}"
178151
.\.sonar\scanner\dotnet-sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}"
179152
153+
- uses: codecov/codecov-action@v2
154+
with:
155+
token: ${{ secrets.CODECOV_TOKEN }}
156+
directory: "src/${{ env.TEST_RESULTS }}"
157+
files: "**/coverage.opencover.xml"
158+
flags: unittests
159+
name: codecov-umbrella
160+
fail_ci_if_error: true
161+
verbose: true
162+
180163
build:
181164
runs-on: ${{ matrix.os }}
182165
needs: [calc-version]

src/Api/LoggingDataDictionary.cs

+11
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,24 @@
22
// SPDX-FileCopyrightText: © 2021 NVIDIA Corporation
33
// SPDX-License-Identifier: Apache License 2.0
44

5+
using System;
56
using System.Collections.Generic;
67
using System.Linq;
8+
using System.Runtime.Serialization;
79

810
namespace Monai.Deploy.InformaticsGateway.Api
911
{
12+
[Serializable]
1013
public class LoggingDataDictionary<K, V> : Dictionary<K, V>
1114
{
15+
public LoggingDataDictionary()
16+
{
17+
}
18+
19+
protected LoggingDataDictionary(SerializationInfo info, StreamingContext context) : base(info, context)
20+
{
21+
}
22+
1223
public override string ToString()
1324
{
1425
var pairs = this.Select(x => string.Format("{0}={1}", x.Key, x.Value));

src/Api/MessageBroker/JsonMessage.cs

+1-3
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ namespace Monai.Deploy.InformaticsGateway.Api.MessageBroker
99
{
1010
public sealed class JsonMessage<T> : MessageBase
1111
{
12-
public static readonly string JsonApplicationType = "application/json";
13-
1412
/// <summary>
1513
/// Body of the message.
1614
/// </summary>
@@ -65,7 +63,7 @@ public JsonMessage(T body,
6563
MessageDescription = bodyDescription;
6664
MessageId = messageId;
6765
ApplicationId = applicationId;
68-
ContentType = JsonApplicationType;
66+
ContentType = MessageContentTypes.JsonApplicationType;
6967
CorrelationId = correlationId;
7068
CreationDateTime = creationDateTime;
7169
DeliveryTag = deliveryTag;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// SPDX-FileCopyrightText: © 2021-2022 MONAI Consortium
2+
// SPDX-License-Identifier: Apache License 2.0
3+
4+
namespace Monai.Deploy.InformaticsGateway.Api.MessageBroker
5+
{
6+
public static class MessageContentTypes
7+
{
8+
public static readonly string JsonApplicationType = "application/json";
9+
}
10+
}

src/Api/Rest/InferenceRequest.cs

+93-67
Original file line numberDiff line numberDiff line change
@@ -215,17 +215,23 @@ private bool Validate(out string details)
215215
errors.Add("'transactionId' is required.");
216216
}
217217

218-
if (InputResources.IsNullOrEmpty() ||
219-
InputResources.Count(predicate => predicate.Interface != InputInterfaceType.Algorithm) == 0)
220-
{
221-
errors.Add("No 'inputResources' specified.");
222-
}
223-
224218
if (Application is null)
225219
{
226220
errors.Add("No algorithm defined or more than one algorithms defined in 'inputResources'. 'inputResources' must include one algorithm/pipeline for the inference request.");
227221
}
228222

223+
ValidateInputResources(errors);
224+
ValidateInputMetadata(errors);
225+
ValidateOUtputResources(errors);
226+
227+
details = string.Join(" ", errors);
228+
return errors.Count == 0;
229+
}
230+
231+
private void ValidateOUtputResources(List<string> errors)
232+
{
233+
Guard.Against.Null(errors, nameof(errors));
234+
229235
if (InputMetadata.Inputs.IsNullOrEmpty())
230236
{
231237
errors.Add("Request has no `inputMetadata` defined. At least one `inputs` or `inputMetadata` required.");
@@ -237,18 +243,11 @@ private bool Validate(out string details)
237243
CheckInputMetadataDetails(inputDetails, errors);
238244
}
239245
}
246+
}
240247

241-
foreach (var input in InputResources)
242-
{
243-
if (input.Interface == InputInterfaceType.DicomWeb)
244-
{
245-
CheckDicomWebConnectionDetails("inputResources", errors, input.ConnectionDetails);
246-
}
247-
else if (input.Interface == InputInterfaceType.Fhir)
248-
{
249-
CheckFhirConnectionDetails("inputResources", errors, input.ConnectionDetails);
250-
}
251-
}
248+
private void ValidateInputMetadata(List<string> errors)
249+
{
250+
Guard.Against.Null(errors, nameof(errors));
252251

253252
foreach (var output in OutputResources)
254253
{
@@ -261,51 +260,37 @@ private bool Validate(out string details)
261260
CheckFhirConnectionDetails("outputResources", errors, output.ConnectionDetails);
262261
}
263262
}
263+
}
264264

265-
details = string.Join(" ", errors);
266-
return errors.Count == 0;
265+
private void ValidateInputResources(List<string> errors)
266+
{
267+
Guard.Against.Null(errors, nameof(errors));
268+
269+
if (InputResources.IsNullOrEmpty() ||
270+
!InputResources.Any(predicate => predicate.Interface != InputInterfaceType.Algorithm))
271+
{
272+
errors.Add("No 'inputResources' specified.");
273+
}
274+
275+
foreach (var input in InputResources)
276+
{
277+
if (input.Interface == InputInterfaceType.DicomWeb)
278+
{
279+
CheckDicomWebConnectionDetails("inputResources", errors, input.ConnectionDetails);
280+
}
281+
else if (input.Interface == InputInterfaceType.Fhir)
282+
{
283+
CheckFhirConnectionDetails("inputResources", errors, input.ConnectionDetails);
284+
}
285+
}
267286
}
268287

269288
private void CheckInputMetadataDetails(InferenceRequestDetails details, List<string> errors)
270289
{
271290
switch (details.Type)
272291
{
273292
case InferenceRequestType.DicomUid:
274-
if (details.Studies.IsNullOrEmpty())
275-
{
276-
errors.Add("Request type is set to `DICOM_UID` but no `studies` defined.");
277-
}
278-
else
279-
{
280-
foreach (var study in details.Studies)
281-
{
282-
if (string.IsNullOrWhiteSpace(study.StudyInstanceUid))
283-
{
284-
errors.Add("`StudyInstanceUID` cannot be empty.");
285-
}
286-
287-
if (study.Series is null) continue;
288-
289-
foreach (var series in study.Series)
290-
{
291-
if (string.IsNullOrWhiteSpace(series.SeriesInstanceUid))
292-
{
293-
errors.Add("`SeriesInstanceUID` cannot be empty.");
294-
}
295-
296-
if (series.Instances is null) continue;
297-
298-
foreach (var instance in series.Instances)
299-
{
300-
if (instance.SopInstanceUid.IsNullOrEmpty() ||
301-
instance.SopInstanceUid.Any(p => string.IsNullOrWhiteSpace(p)))
302-
{
303-
errors.Add("`SOPInstanceUID` cannot be empty.");
304-
}
305-
}
306-
}
307-
}
308-
}
293+
CheckInputMetadataWithTypDeicomUid(details, errors);
309294
break;
310295

311296
case InferenceRequestType.DicomPatientId:
@@ -323,25 +308,66 @@ private void CheckInputMetadataDetails(InferenceRequestDetails details, List<str
323308
break;
324309

325310
case InferenceRequestType.FhireResource:
326-
if (details.Resources.IsNullOrEmpty())
311+
CheckInputMetadataWithTypeFhirResource(details, errors);
312+
break;
313+
314+
default:
315+
errors.Add($"'inputMetadata' does not yet support type '{details.Type}'.");
316+
break;
317+
}
318+
}
319+
320+
private static void CheckInputMetadataWithTypeFhirResource(InferenceRequestDetails details, List<string> errors)
321+
{
322+
Guard.Against.Null(details, nameof(details));
323+
Guard.Against.Null(errors, nameof(errors));
324+
325+
if (details.Resources.IsNullOrEmpty())
326+
{
327+
errors.Add("Request type is set to `FHIR_RESOURCE` but no FHIR `resources` defined.");
328+
}
329+
else
330+
{
331+
errors.AddRange(details.Resources.Where(resource => string.IsNullOrWhiteSpace(resource.Type)).Select(resource => "A FHIR resource type cannot be empty."));
332+
}
333+
}
334+
335+
private static void CheckInputMetadataWithTypDeicomUid(InferenceRequestDetails details, List<string> errors)
336+
{
337+
Guard.Against.Null(details, nameof(details));
338+
Guard.Against.Null(errors, nameof(errors));
339+
340+
if (details.Studies.IsNullOrEmpty())
341+
{
342+
errors.Add("Request type is set to `DICOM_UID` but no `studies` defined.");
343+
}
344+
else
345+
{
346+
foreach (var study in details.Studies)
347+
{
348+
if (string.IsNullOrWhiteSpace(study.StudyInstanceUid))
327349
{
328-
errors.Add("Request type is set to `FHIR_RESOURCE` but no FHIR `resources` defined.");
350+
errors.Add("`StudyInstanceUID` cannot be empty.");
329351
}
330-
else
352+
353+
if (study.Series is null) continue;
354+
355+
foreach (var series in study.Series)
331356
{
332-
foreach (var resource in details.Resources)
357+
if (string.IsNullOrWhiteSpace(series.SeriesInstanceUid))
333358
{
334-
if (string.IsNullOrWhiteSpace(resource.Type))
335-
{
336-
errors.Add("A FHIR resource type cannot be empty.");
337-
}
359+
errors.Add("`SeriesInstanceUID` cannot be empty.");
338360
}
339-
}
340-
break;
341361

342-
default:
343-
errors.Add($"'inputMetadata' does not yet support type '{details.Type}'.");
344-
break;
362+
if (series.Instances is null) continue;
363+
errors.AddRange(
364+
series.Instances
365+
.Where(
366+
instance => instance.SopInstanceUid.IsNullOrEmpty() ||
367+
instance.SopInstanceUid.Any(p => string.IsNullOrWhiteSpace(p)))
368+
.Select(instance => "`SOPInstanceUID` cannot be empty."));
369+
}
370+
}
345371
}
346372
}
347373

0 commit comments

Comments
 (0)