diff --git a/klass-api/pom.xml b/klass-api/pom.xml
index 7bae142a..65a6624d 100644
--- a/klass-api/pom.xml
+++ b/klass-api/pom.xml
@@ -10,7 +10,7 @@
no.ssb.klass
klass-root
- 2.0.18
+ 2.1.0
diff --git a/klass-api/src/main/asciidoc/api-guide.adoc b/klass-api/src/main/asciidoc/api-guide.adoc
index e87ef8fd..1059553b 100644
--- a/klass-api/src/main/asciidoc/api-guide.adoc
+++ b/klass-api/src/main/asciidoc/api-guide.adoc
@@ -216,7 +216,7 @@ Used to search codes from a classification variant. A range is specified when re
and the response will for each code indicate its valid range (validFrom/validTo).
The format and character set used, can be specified in the http header (character set is only available for csv).
For more information about the range see <<_range, range>>. +
-To get a snapshot of codes valid at a specified date, use <<_variantaAt, variantAt>>.
+To get a snapshot of codes valid at a specified date, use <<_variantat, variantAt>>.
See also <<_variants_by_id, variants>>
===== Supported formats
@@ -510,15 +510,28 @@ include::{snippets}/select-codes-example-csv/http-response.adoc[]
=== csvSeparator
-`csvSeparator` is used to specify separator to be used for csv format. Default is `,`
+`csvSeparator` is used to specify a separator symbol to be used for csv format. Default is `,`
===== Example request for csvSeparator=;
include::{snippets}/csv-separator-example/curl-request.adoc[]
===== Example response
include::{snippets}/csv-separator-example/http-response.adoc[]
+
+=== csvFields
+`csvFields` is an optional parameter that allows a user to filter columns and specify columns order in the Csv output.
+It is applicable for `/codes`, `/codesAt`, `/variantAt`, `/correspondsAt` services.
+
+The field names are case-sensitive. Misspelled field names will cause a 400 BAD REQUEST error.
+
+===== Example request for csvFields=name,code with /codesAt;
+include::{snippets}/csv-fields-codes-at-example/curl-request.adoc[]
+===== Example response
+include::{snippets}/csv-fields-codes-at-example/http-response.adoc[]
+
+
=== language
-`language` is used to specify which language data shall be presented in. Default if none is selected is nb (Norwegian Bokmål).
+`language` is used to specify a language data shall be presented in. If none selected, the default is "nb" (Norwegian Bokmål).
Supported languages
|===
diff --git a/klass-api/src/main/java/no/ssb/klass/api/controllers/ClassificationController.java b/klass-api/src/main/java/no/ssb/klass/api/controllers/ClassificationController.java
index 42bc3cbf..29f6573e 100644
--- a/klass-api/src/main/java/no/ssb/klass/api/controllers/ClassificationController.java
+++ b/klass-api/src/main/java/no/ssb/klass/api/controllers/ClassificationController.java
@@ -6,11 +6,11 @@
import java.net.MalformedURLException;
import java.net.URL;
import java.time.LocalDate;
+import java.util.Arrays;
import java.util.Date;
import java.util.List;
-
import javax.transaction.Transactional;
-
+import no.ssb.klass.api.controllers.validators.CsvFieldsValidator;
import no.ssb.klass.core.util.AlphaNumericalComparator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -90,15 +90,18 @@ public class ClassificationController {
private final SubscriberService subscriberService;
private final SearchService searchService;
private final StatisticsService statisticsService;
+ private final CsvFieldsValidator csvFieldsValidator;
@Autowired
public ClassificationController(ClassificationService classificationService,
SubscriberService subscriberService,
- SearchService searchService, StatisticsService statisticsService) {
+ SearchService searchService, StatisticsService statisticsService,
+ CsvFieldsValidator csvFieldsValidator) {
this.classificationService = classificationService;
this.subscriberService = subscriberService;
this.searchService = searchService;
this.statisticsService = statisticsService;
+ this.csvFieldsValidator = csvFieldsValidator;
}
@ExceptionHandler
@@ -121,6 +124,12 @@ public String argumentTypeMismatchExceptionHandler(MethodArgumentTypeMismatchExc
return exception.getMessage();
}
+ @ExceptionHandler
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ public String argumentTypeMismatchExceptionHandler(IllegalArgumentException exception) {
+ return exception.getMessage();
+ }
+
@ExceptionHandler
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String serverErrorExceptionHandler(Exception exception) {
@@ -200,7 +209,7 @@ public KlassPagedResources search(
// @formatter:off
@RequestParam(value = "query") String query,
@RequestParam(value = "ssbSection", required = false) String ssbSection,
- @RequestParam(value = "includeCodelists", defaultValue = "false") boolean includeCodelists,
+ @RequestParam(value = "includeCodelists", defaultValue = "false") boolean includeCodelists,
Pageable pageable, PagedResourcesAssembler assembler) {
// @formatter:on
Link self = new Link(getCurrentRequest(), Link.REL_SELF);
@@ -258,6 +267,7 @@ public CodeList codes(@PathVariable Long id,
@RequestParam(value = "from") @DateTimeFormat(pattern = RestConstants.DATE_FORMAT) LocalDate from,
@RequestParam(value = "to", required = false) @DateTimeFormat(pattern = RestConstants.DATE_FORMAT) LocalDate to,
@RequestParam(value = "csvSeparator", defaultValue = ",") String csvSeparator,
+ @RequestParam(value = "csvFields", defaultValue = "") String csvFields,
@RequestParam(value = "selectLevel", required = false) String selectLevel,
@RequestParam(value = "selectCodes", required = false) String selectCodes,
@RequestParam(value = "presentationNamePattern", required = false) String presentationNamePattern,
@@ -265,8 +275,15 @@ public CodeList codes(@PathVariable Long id,
@RequestParam(value = "includeFuture", defaultValue = "false") Boolean includeFuture
) {
// @formatter:on
- return codesInternal(id, new DateRangeHolder(from, to), csvSeparator, selectLevel, selectCodes,
+ CodeList codeList = codesInternal(id, new DateRangeHolder(from, to), csvSeparator, selectLevel, selectCodes,
presentationNamePattern, language, includeFuture);
+
+ if (!csvFields.isEmpty()) {
+ List csvFieldsList = getCsvFieldsList(csvFields);
+ csvFieldsValidator.validateFieldsCodeItem( csvFieldsList);
+ codeList.setCsvFields(csvFieldsList);
+ }
+ return codeList;
}
@RequestMapping(value = "/classifications/{id}/codesAt", method = RequestMethod.GET)
@@ -274,6 +291,7 @@ public CodeList codesAt(@PathVariable Long id,
// @formatter:off
@RequestParam(value = "date") @DateTimeFormat(pattern = RestConstants.DATE_FORMAT) LocalDate date,
@RequestParam(value = "csvSeparator", defaultValue = ",") String csvSeparator,
+ @RequestParam(value = "csvFields", defaultValue = "") String csvFields,
@RequestParam(value = "selectLevel", required = false) String selectLevel,
@RequestParam(value = "selectCodes", required = false) String selectCodes,
@RequestParam(value = "presentationNamePattern", required = false) String presentationNamePattern,
@@ -281,8 +299,16 @@ public CodeList codesAt(@PathVariable Long id,
@RequestParam(value = "includeFuture", defaultValue = "false") Boolean includeFuture
) {
// @formatter:on
- return codesInternal(id, new DateRangeHolder(date), csvSeparator, selectLevel, selectCodes,
+
+ CodeList codeList = codesInternal(id, new DateRangeHolder(date), csvSeparator, selectLevel, selectCodes,
presentationNamePattern, language, includeFuture);
+
+ if (!csvFields.isEmpty()) {
+ List csvFieldsList = getCsvFieldsList(csvFields);
+ csvFieldsValidator.validateFieldsCodeItem(csvFieldsList);
+ codeList.setCsvFields(csvFieldsList);
+ }
+ return codeList;
}
private CodeList codesInternal(Long id, DateRangeHolder dateRangeHolder, String csvSeparator, String selectLevel,
@@ -304,6 +330,7 @@ public CodeChangeList changes(@PathVariable Long id,
@RequestParam(value = "from") @DateTimeFormat(pattern = RestConstants.DATE_FORMAT) LocalDate from,
@RequestParam(value = "to", required = false) @DateTimeFormat(pattern = RestConstants.DATE_FORMAT) LocalDate to,
@RequestParam(value = "csvSeparator", defaultValue = ",") String csvSeparator,
+ @RequestParam(value = "csvFields", defaultValue = "") String csvFields,
@RequestParam(value = "language", defaultValue = "nb") Language language,
@RequestParam(value = "includeFuture", defaultValue = "false") Boolean includeFuture) {
// @formatter:on
@@ -315,6 +342,13 @@ public CodeChangeList changes(@PathVariable Long id,
for (CorrespondenceTable changeTable : changeTables) {
codeChanges = codeChanges.merge(codeChanges.convert(changeTable, language));
}
+
+ if (!csvFields.isEmpty()) {
+ List csvFieldsList = getCsvFieldsList(csvFields);
+ csvFieldsValidator.validateFieldsChangeItemSchema(csvFieldsList);
+ codeChanges.setCsvFields(csvFieldsList);
+ }
+
return codeChanges;
}
@@ -325,6 +359,7 @@ public CodeList variant(@PathVariable Long id,
@RequestParam(value = "from") @DateTimeFormat(pattern = RestConstants.DATE_FORMAT) LocalDate from,
@RequestParam(value = "to", required = false) @DateTimeFormat(pattern = RestConstants.DATE_FORMAT) LocalDate to,
@RequestParam(value = "csvSeparator", defaultValue = ",") String csvSeparator,
+ @RequestParam(value = "csvFields", defaultValue = "") String csvFields,
@RequestParam(value = "level", required = false) String selectLevel,
@RequestParam(value = "selectCodes", required = false) String selectCodes,
@RequestParam(value = "presentationNamePattern", required = false) String presentationNamePattern,
@@ -332,8 +367,17 @@ public CodeList variant(@PathVariable Long id,
@RequestParam(value = "includeFuture", defaultValue = "false") Boolean includeFuture
) {
// @formatter:on
- return variantInternal(id, variantName, new DateRangeHolder(from, to), csvSeparator, selectLevel, selectCodes,
+ CodeList codeList = variantInternal(id, variantName, new DateRangeHolder(from, to), csvSeparator, selectLevel, selectCodes,
presentationNamePattern, language, includeFuture);
+
+ if (!csvFields.isEmpty()) {
+ List csvFieldsList = getCsvFieldsList(csvFields);
+ csvFieldsValidator.validateFieldsCodeItem(csvFieldsList);
+ codeList.setCsvFields(csvFieldsList);
+ }
+
+ return codeList;
+
}
@RequestMapping(value = "/classifications/{id}/variantAt", method = RequestMethod.GET)
@@ -342,6 +386,7 @@ public CodeList variantAt(@PathVariable Long id,
@RequestParam(value = "variantName") String variantName,
@RequestParam(value = "date", required = false) @DateTimeFormat(pattern = RestConstants.DATE_FORMAT) LocalDate date,
@RequestParam(value = "csvSeparator", defaultValue = ",") String csvSeparator,
+ @RequestParam(value = "csvFields", defaultValue = "") String csvFields,
@RequestParam(value = "level", required = false) String selectLevel,
@RequestParam(value = "selectCodes", required = false) String selectCodes,
@RequestParam(value = "presentationNamePattern", required = false) String presentationNamePattern,
@@ -349,10 +394,20 @@ public CodeList variantAt(@PathVariable Long id,
@RequestParam(value = "includeFuture", defaultValue = "false") Boolean includeFuture
) {
// @formatter:on
- return variantInternal(id, variantName, new DateRangeHolder(date), csvSeparator, selectLevel, selectCodes,
+ CodeList codeList = variantInternal(id, variantName, new DateRangeHolder(date), csvSeparator, selectLevel, selectCodes,
presentationNamePattern, language, includeFuture);
+
+ if (!csvFields.isEmpty()) {
+ List csvFieldsList = getCsvFieldsList(csvFields);
+ csvFieldsValidator.validateFieldsCodeItem(csvFieldsList);
+ codeList.setCsvFields(csvFieldsList);
+ }
+
+ return codeList;
}
+
+
private CodeList variantInternal(Long id, String variantName, DateRangeHolder dateRangeHolder, String csvSeparator,
String selectLevel, String selectCodes, String presentationNamePattern, Language language, Boolean includeFuture) {
List codes = classificationService.findVariantClassificationCodes(id, variantName, language,
@@ -369,10 +424,19 @@ public CorrespondenceItemList corresponds(@PathVariable Long id,
@RequestParam(value = "from") @DateTimeFormat(pattern = RestConstants.DATE_FORMAT) LocalDate from,
@RequestParam(value = "to", required = false) @DateTimeFormat(pattern = RestConstants.DATE_FORMAT) LocalDate to,
@RequestParam(value = "csvSeparator", defaultValue = ",") String csvSeparator,
+ @RequestParam(value = "csvFields", defaultValue = "") String csvFields,
@RequestParam(value = "language", defaultValue = "nb") Language language,
@RequestParam(value = "includeFuture", defaultValue = "false") Boolean includeFuture) {
// @formatter:on
- return correspondsInternal(id, targetClassificationId, new DateRangeHolder(from, to), csvSeparator, language, includeFuture);
+ CorrespondenceItemList correspondenceList = correspondsInternal(id, targetClassificationId, new DateRangeHolder(from, to), csvSeparator, language, includeFuture);
+
+ if (!csvFields.isEmpty()) {
+ List csvFieldsList = getCsvFieldsList(csvFields);
+ csvFieldsValidator.validateFieldsCorrespondenceItem(csvFieldsList);
+ correspondenceList.setCsvFields(csvFieldsList);
+ }
+
+ return correspondenceList;
}
@RequestMapping(value = "/classifications/{id}/correspondsAt", method = RequestMethod.GET)
@@ -381,10 +445,19 @@ public CorrespondenceItemList correspondsAt(@PathVariable Long id,
@RequestParam(value = "targetClassificationId") Long targetClassificationId,
@RequestParam(value = "date", required = false) @DateTimeFormat(pattern = RestConstants.DATE_FORMAT) LocalDate date,
@RequestParam(value = "csvSeparator", defaultValue = ",") String csvSeparator,
+ @RequestParam(value = "csvFields", defaultValue = "") String csvFields,
@RequestParam(value = "language", defaultValue = "nb") Language language,
@RequestParam(value = "includeFuture", defaultValue = "false") Boolean includeFuture) {
// @formatter:on
- return correspondsInternal(id, targetClassificationId, new DateRangeHolder(date), csvSeparator, language, includeFuture);
+ CorrespondenceItemList correspondenceList = correspondsInternal(id, targetClassificationId, new DateRangeHolder(date), csvSeparator, language, includeFuture);
+
+ if (!csvFields.isEmpty()) {
+ List csvFieldsList = getCsvFieldsList(csvFields);
+ csvFieldsValidator.validateFieldsCorrespondenceItem(csvFieldsList);
+ correspondenceList.setCsvFields(csvFieldsList);
+ }
+
+ return correspondenceList;
}
private CorrespondenceItemList correspondsInternal(Long id, Long targetClassificationId,
@@ -479,6 +552,11 @@ private String extractSsbSection(String ssbSection) {
return Strings.isNullOrEmpty(ssbSection) ? null : ssbSection;
}
+ private List getCsvFieldsList(String csvFields) {
+ return Arrays.asList(csvFields.split(","));
+ }
+
+
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Language.class, new CaseInsensitiveConverter<>(Language.class));
diff --git a/klass-api/src/main/java/no/ssb/klass/api/controllers/validators/CsvFieldsValidator.java b/klass-api/src/main/java/no/ssb/klass/api/controllers/validators/CsvFieldsValidator.java
new file mode 100644
index 00000000..d6b99f23
--- /dev/null
+++ b/klass-api/src/main/java/no/ssb/klass/api/controllers/validators/CsvFieldsValidator.java
@@ -0,0 +1,40 @@
+package no.ssb.klass.api.controllers.validators;
+
+import com.fasterxml.jackson.dataformat.csv.CsvMapper;
+import com.fasterxml.jackson.dataformat.csv.CsvSchema;
+import no.ssb.klass.api.dto.CodeChangeItem;
+import no.ssb.klass.api.dto.CodeItem;
+import no.ssb.klass.api.dto.CorrespondenceItem;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+import static java.util.stream.Collectors.toList;
+
+@Component
+public class CsvFieldsValidator {
+
+ private final CsvSchema correspondenceItemSchema = new CsvMapper().schemaFor(CorrespondenceItem.RangedCorrespondenceItem.class);
+ private final CsvSchema codeItemSchema = new CsvMapper().schemaFor(CodeItem.class);
+ private final CsvSchema codeChangeItemSchema = new CsvMapper().schemaFor(CodeChangeItem.class);
+
+ public void validateFieldsCorrespondenceItem(List csvFields) {
+ List fieldsNotFound = csvFields.stream().filter(s -> correspondenceItemSchema.column(s) == null).collect(toList());
+ if(!fieldsNotFound.isEmpty()) {
+ throw new IllegalArgumentException("CorrespondenceList does not contain the following field(s): " + String.join(",", fieldsNotFound));
+ }
+ }
+ public void validateFieldsCodeItem(List csvFields) {
+ List fieldsNotFound = csvFields.stream().filter(s -> codeItemSchema.column(s) == null).collect(toList());
+ if(!fieldsNotFound.isEmpty()) {
+ throw new IllegalArgumentException("CorrespondenceList does not contain the following field(s): " + String.join(",", fieldsNotFound));
+ }
+ }
+ public void validateFieldsChangeItemSchema(List csvFields) {
+ List fieldsNotFound = csvFields.stream().filter(s -> codeChangeItemSchema.column(s) == null).collect(toList());
+ if(!fieldsNotFound.isEmpty()) {
+ throw new IllegalArgumentException("CorrespondenceList does not contain the following field(s): " + String.join(",", fieldsNotFound));
+ }
+ }
+
+}
diff --git a/klass-api/src/main/java/no/ssb/klass/api/converters/AbstractCsvConverter.java b/klass-api/src/main/java/no/ssb/klass/api/converters/AbstractCsvConverter.java
index ff9568a8..599f0b26 100644
--- a/klass-api/src/main/java/no/ssb/klass/api/converters/AbstractCsvConverter.java
+++ b/klass-api/src/main/java/no/ssb/klass/api/converters/AbstractCsvConverter.java
@@ -3,7 +3,9 @@
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
+import java.util.List;
+import com.fasterxml.jackson.core.JsonGenerator;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
@@ -29,11 +31,24 @@ protected ObjectWriter createWriter(Class> clazz) {
}
protected ObjectWriter createWriter(Class> clazz, char csvSeparator) {
+ return createWriter(clazz, csvSeparator, null);
+ }
+ protected ObjectWriter createWriter(Class> clazz, char csvSeparator, List csvFields) {
CsvMapper mapper = new CsvMapper();
- CsvSchema schema = mapper.schemaFor(clazz).withHeader().withColumnSeparator(csvSeparator);
+ CsvSchema schema;
+ if (csvFields == null) {
+ schema = mapper
+ .schemaFor(clazz)
+ .withHeader()
+ .withColumnSeparator(csvSeparator);
+ } else {
+ mapper.enable(JsonGenerator.Feature.IGNORE_UNKNOWN);
+ schema = CsvSchema.builder()
+ .addColumns(csvFields, CsvSchema.ColumnType.STRING)
+ .build().withHeader().withColumnSeparator(csvSeparator);
+ }
- return mapper.writer(schema).withFeatures(
- CsvGenerator.Feature.ALWAYS_QUOTE_STRINGS,
+ return mapper.writer(schema).withFeatures(CsvGenerator.Feature.ALWAYS_QUOTE_STRINGS,
CsvGenerator.Feature.OMIT_MISSING_TAIL_COLUMNS);
}
diff --git a/klass-api/src/main/java/no/ssb/klass/api/converters/CodeChangeListCsvConverter.java b/klass-api/src/main/java/no/ssb/klass/api/converters/CodeChangeListCsvConverter.java
index ebd81600..317d0baa 100644
--- a/klass-api/src/main/java/no/ssb/klass/api/converters/CodeChangeListCsvConverter.java
+++ b/klass-api/src/main/java/no/ssb/klass/api/converters/CodeChangeListCsvConverter.java
@@ -20,7 +20,7 @@ protected boolean supports(Class> clazz) {
@Override
protected void writeInternal(CodeChangeList codeChangeList, HttpOutputMessage outputMessage) throws IOException {
Charset charset = selectCharsetAndUpdateOutput(outputMessage);
- ObjectWriter writer = createWriter(CodeChangeItem.class, codeChangeList.getCsvSeparator());
+ ObjectWriter writer = createWriter(CodeChangeItem.class, codeChangeList.getCsvSeparator(), codeChangeList.getCsvFields());
writer.writeValue(new OutputStreamWriter(outputMessage.getBody(), charset), codeChangeList.getCodeChanges());
}
}
\ No newline at end of file
diff --git a/klass-api/src/main/java/no/ssb/klass/api/converters/CodeListCsvConverter.java b/klass-api/src/main/java/no/ssb/klass/api/converters/CodeListCsvConverter.java
index 4f7afaf9..94f58355 100644
--- a/klass-api/src/main/java/no/ssb/klass/api/converters/CodeListCsvConverter.java
+++ b/klass-api/src/main/java/no/ssb/klass/api/converters/CodeListCsvConverter.java
@@ -19,7 +19,7 @@ protected boolean supports(Class> clazz) {
@Override
protected void writeInternal(CodeList codeList, HttpOutputMessage outputMessage) throws IOException {
Charset charset = selectCharsetAndUpdateOutput(outputMessage);
- ObjectWriter writer = createWriter(codeList.codeItemsJavaType(), codeList.getCsvSeparator());
+ ObjectWriter writer = createWriter(codeList.codeItemsJavaType(), codeList.getCsvSeparator(), codeList.getCsvFields());
writer.writeValue(new OutputStreamWriter(outputMessage.getBody(), charset), codeList.getCodes());
}
}
\ No newline at end of file
diff --git a/klass-api/src/main/java/no/ssb/klass/api/converters/CorrespondenceItemListCsvConverter.java b/klass-api/src/main/java/no/ssb/klass/api/converters/CorrespondenceItemListCsvConverter.java
index 5720e026..d7b43cdf 100644
--- a/klass-api/src/main/java/no/ssb/klass/api/converters/CorrespondenceItemListCsvConverter.java
+++ b/klass-api/src/main/java/no/ssb/klass/api/converters/CorrespondenceItemListCsvConverter.java
@@ -21,7 +21,7 @@ protected void writeInternal(CorrespondenceItemList correspondenceItemList, Http
throws IOException {
Charset charset = selectCharsetAndUpdateOutput(outputMessage);
ObjectWriter writer = createWriter(correspondenceItemList.classificationItemsJavaType(), correspondenceItemList
- .getCsvSeparator());
+ .getCsvSeparator(), correspondenceItemList.getCsvFields());
writer.writeValue(new OutputStreamWriter(outputMessage.getBody(), charset), correspondenceItemList
.getCorrespondenceItems());
}
diff --git a/klass-api/src/main/java/no/ssb/klass/api/dto/CodeChangeList.java b/klass-api/src/main/java/no/ssb/klass/api/dto/CodeChangeList.java
index 3aa22ede..ababa13a 100644
--- a/klass-api/src/main/java/no/ssb/klass/api/dto/CodeChangeList.java
+++ b/klass-api/src/main/java/no/ssb/klass/api/dto/CodeChangeList.java
@@ -18,6 +18,7 @@
public class CodeChangeList {
private final char csvSeparator;
private final List codeChanges;
+ private List csvFields;
public CodeChangeList(String csvSeparator) {
if (csvSeparator.toCharArray().length != 1) {
@@ -43,6 +44,16 @@ public char getCsvSeparator() {
return csvSeparator;
}
+ @JsonIgnore
+ public List getCsvFields() {
+ return csvFields;
+ }
+
+ @JsonIgnore
+ public void setCsvFields(List csvFields) {
+ this.csvFields = csvFields;
+ }
+
public CodeChangeList convert(CorrespondenceTable correspondenceTable, Language language) {
LocalDate changeOccurred = correspondenceTable.getOccured();
boolean isTargetOldest = correspondenceTable.getTarget().getDateRange().getFrom().isBefore(correspondenceTable
diff --git a/klass-api/src/main/java/no/ssb/klass/api/dto/CodeList.java b/klass-api/src/main/java/no/ssb/klass/api/dto/CodeList.java
index 1f63b348..e0435cd0 100644
--- a/klass-api/src/main/java/no/ssb/klass/api/dto/CodeList.java
+++ b/klass-api/src/main/java/no/ssb/klass/api/dto/CodeList.java
@@ -33,6 +33,7 @@ public class CodeList {
private final List codeItems;
private final Boolean includeFuture;
private final DateRange dateRange;
+ private List csvFields;
public CodeList(String csvSeparator, boolean displayWithValidRange, DateRange dateRange, Boolean includeFuture) {
if (csvSeparator.toCharArray().length != 1) {
@@ -67,6 +68,16 @@ public char getCsvSeparator() {
return csvSeparator;
}
+ @JsonIgnore
+ public List getCsvFields() {
+ return csvFields;
+ }
+
+ @JsonIgnore
+ public void setCsvFields(List csvFields) {
+ this.csvFields = csvFields;
+ }
+
public CodeList merge(CodeList other) {
List combined = new ArrayList<>(codeItems);
combined.addAll(other.codeItems);
diff --git a/klass-api/src/main/java/no/ssb/klass/api/dto/CorrespondenceItemList.java b/klass-api/src/main/java/no/ssb/klass/api/dto/CorrespondenceItemList.java
index cec07ea2..c4defbd7 100644
--- a/klass-api/src/main/java/no/ssb/klass/api/dto/CorrespondenceItemList.java
+++ b/klass-api/src/main/java/no/ssb/klass/api/dto/CorrespondenceItemList.java
@@ -17,6 +17,7 @@ public class CorrespondenceItemList {
private final boolean displayWithValidRange;
private final List correspondenceItems;
private final Boolean includeFuture;
+ private List csvFields;
public CorrespondenceItemList(String csvSeparator, boolean displayWithValidRange, boolean includeFuture) {
if (csvSeparator.toCharArray().length != 1) {
@@ -50,6 +51,17 @@ public char getCsvSeparator() {
return csvSeparator;
}
+ @JsonIgnore
+ public List getCsvFields() {
+ return csvFields;
+ }
+
+ @JsonIgnore
+ public void setCsvFields(List csvFields) {
+ this.csvFields = csvFields;
+ }
+
+
public CorrespondenceItemList convert(List correspondences) {
return newList(correspondences.stream()
.map(RangedCorrespondenceItem::new)
diff --git a/klass-api/src/main/java/no/ssb/klass/api/dto/hal/ClassificationResource.java b/klass-api/src/main/java/no/ssb/klass/api/dto/hal/ClassificationResource.java
index 1e1a5697..e115f955 100644
--- a/klass-api/src/main/java/no/ssb/klass/api/dto/hal/ClassificationResource.java
+++ b/klass-api/src/main/java/no/ssb/klass/api/dto/hal/ClassificationResource.java
@@ -95,14 +95,14 @@ public List getVersions() {
private Link createVariantAtRelation(Long id) {
ControllerLinkBuilder linkBuilder = linkTo(ControllerLinkBuilder.methodOn(ClassificationController.class).variantAt(id, "name",
- LocalDate.now(), ",", "level", "selectCodes", "presentationNamePattern", Language.getDefault(), null));
+ LocalDate.now(), ",", null, "level", "selectCodes", "presentationNamePattern", Language.getDefault(), null));
return new Link(createUriTemplate(linkBuilder, "variantName", date(), "csvSeparator", "level", "selectCodes",
"presentationNamePattern"), "variantAt");
}
private Link createVariantRelation(Long id) {
ControllerLinkBuilder linkBuilder = linkTo(ControllerLinkBuilder.methodOn(ClassificationController.class).variant(id, "name",
- LocalDate.now(), LocalDate.now(), ",", "level", "selectCodes", "presentationNamePattern", Language
+ LocalDate.now(), LocalDate.now(), ",",null, "level", "selectCodes", "presentationNamePattern", Language
.getDefault(), null));
return new Link(createUriTemplate(linkBuilder, "variantName", from(), to(), "csvSeparator", "level",
"selectCodes", "presentationNamePattern"), "variant");
@@ -110,34 +110,34 @@ private Link createVariantRelation(Long id) {
private Link createCodesAtRelation(Long id) {
ControllerLinkBuilder linkBuilder = linkTo(ControllerLinkBuilder.methodOn(ClassificationController.class).codesAt(id, LocalDate.now(),
- ",", "level", "selectCodes", "presentationNamePattern", Language.getDefault(), null));
+ ",",null, "level", "selectCodes", "presentationNamePattern", Language.getDefault(), null));
return new Link(createUriTemplate(linkBuilder, date(), "csvSeparator", "level", "selectCodes",
"presentationNamePattern"), "codesAt");
}
private Link createCodesRelation(Long id) {
ControllerLinkBuilder linkBuilder = linkTo(ControllerLinkBuilder.methodOn(ClassificationController.class).codes(id, LocalDate.now(),
- LocalDate.now(), ",", "level", "selectCodes", "presentationNamePattern", Language.getDefault(), null));
+ LocalDate.now(), ",",null, "level", "selectCodes", "presentationNamePattern", Language.getDefault(), null));
return new Link(createUriTemplate(linkBuilder, from(), to(), "csvSeparator", "level", "selectCodes",
"presentationNamePattern"), "codes");
}
private Link createChangesRelation(Long id) {
ControllerLinkBuilder linkBuilder = linkTo(ControllerLinkBuilder.methodOn(ClassificationController.class).changes(id, LocalDate.now(),
- LocalDate.now(), ",", Language.getDefault(), null));
+ LocalDate.now(), ",",null, Language.getDefault(), null));
return new Link(createUriTemplate(linkBuilder, from(), to(), "csvSeparator"), "changes");
}
private Link createCorrespondsAtRelation(Long id) {
ControllerLinkBuilder linkBuilder = linkTo(ControllerLinkBuilder.methodOn(ClassificationController.class).correspondsAt(id, 2L,
- LocalDate.now(), ",", Language.getDefault(), null));
+ LocalDate.now(), ",",null, Language.getDefault(), null));
return new Link(createUriTemplate(linkBuilder, "targetClassificationId", date(), "csvSeparator"),
"correspondsAt");
}
private Link createCorrespondsRelation(Long id) {
ControllerLinkBuilder linkBuilder = linkTo(ControllerLinkBuilder.methodOn(ClassificationController.class).corresponds(id, 2L,
- LocalDate.now(), LocalDate.now(), ",", Language.getDefault(), null));
+ LocalDate.now(), LocalDate.now(), ",",null, Language.getDefault(), null));
return new Link(createUriTemplate(linkBuilder, "targetClassificationId", from(), to(), "csvSeparator"),
"corresponds");
}
diff --git a/klass-api/src/test/java/no/ssb/klass/api/ApiDocumentation.java b/klass-api/src/test/java/no/ssb/klass/api/ApiDocumentation.java
index 0a3912e5..454d4fc2 100644
--- a/klass-api/src/test/java/no/ssb/klass/api/ApiDocumentation.java
+++ b/klass-api/src/test/java/no/ssb/klass/api/ApiDocumentation.java
@@ -442,7 +442,7 @@ public void codesOptionalParametersExample() throws Exception {
List codes = createKommuneInndelingCodes(dateRange);
when(classificationServiceMock.findClassificationCodes(any(), any(), any(), any())).thenReturn(codes);
// @formatter:off
- this.mockMvc.perform(getWithContextUri("/classifications/" + CLASS_ID_KOMMUNEINNDELING + "/codes?from=2014-01-01&to=2015-01-01&csvSeparator=;"
+ this.mockMvc.perform(getWithContextUri("/classifications/" + CLASS_ID_KOMMUNEINNDELING + "/codes?from=2014-01-01&to=2015-01-01&csvSeparator=;&csvFields=name,code"
+ "&selectLevel=1&selectCodes=01*&presentationNamePattern={code}-{name}&language=nb&includeFuture=true")
.accept("text/csv"))
.andDo(this.documentationHandler
@@ -451,6 +451,7 @@ public void codesOptionalParametersExample() throws Exception {
fromParameterDescription(),
toParameterDescription(),
csvSeparatorParameterDescription(),
+ csvFieldsParameterDescription(),
selectCodesParameterDescription(),
selectLevelParameterDescription(),
presentationNamePatternParameterDescription(),
@@ -494,12 +495,13 @@ public void codesAtOptionalParametersExample() throws Exception {
when(classificationServiceMock.findClassificationCodes(any(), any(), any(), any())).thenReturn(codes);
// @formatter:off
this.mockMvc.perform(getWithContextUri("/classifications/" + CLASS_ID_KOMMUNEINNDELING
- + "/codesAt?date=2015-01-01&csvSeparator=;&selectLevel=1&selectCodes=01*"
+ + "/codesAt?date=2015-01-01&csvSeparator=;&csvFields=name,code&selectLevel=1&selectCodes=01*"
+ "&presentationNamePattern={code}-{name}&language=nb&includeFuture=true").accept("text/csv"))
.andDo(this.documentationHandler.document(
requestParameters(
dateParameterDescription(),
csvSeparatorParameterDescription(),
+ csvFieldsParameterDescription(),
selectCodesParameterDescription(),
selectLevelParameterDescription(),
presentationNamePatternParameterDescription(),
@@ -549,7 +551,7 @@ public void variantOptionalParametersExample() throws Exception {
this.mockMvc.perform(
getWithContextUri("/classifications/" + CLASS_ID_GREENHOUSE_GASES
+ "/variant?variantName=Klimagasser"
- + "&from=2014-01-01&to=2015-01-01&csvSeparator=;&selectLevel=1&selectCodes=01*"
+ + "&from=2014-01-01&to=2015-01-01&csvSeparator=;&csvFields=name,code&selectLevel=1&selectCodes=01*"
+ "&presentationNamePattern={code}-{name}&language=nb&includeFuture=true")
.accept("text/csv"))
.andDo(this.documentationHandler = document("{method-name}",
@@ -561,6 +563,7 @@ public void variantOptionalParametersExample() throws Exception {
fromParameterDescription(),
toParameterDescription(),
csvSeparatorParameterDescription(),
+ csvFieldsParameterDescription(),
selectCodesParameterDescription(),
selectLevelParameterDescription(),
presentationNamePatternParameterDescription(),
@@ -610,7 +613,7 @@ public void variantAtOptionalParametersExample() throws Exception {
this.mockMvc.perform(
getWithContextUri("/classifications/" + CLASS_ID_GREENHOUSE_GASES
+ "/variantAt?variantName=Klimagasser"
- + "&date=2015-01-01&csvSeparator=;&selectLevel=1&selectCodes=01*"
+ + "&date=2015-01-01&csvSeparator=;&csvFields=name,code&selectLevel=1&selectCodes=01*"
+ "&presentationNamePattern={code}-{name}&language=nb&includeFuture=true").accept("text/csv"))
.andDo(this.documentationHandler = document("{method-name}",
preprocessRequest(prettyPrint()),
@@ -620,6 +623,7 @@ public void variantAtOptionalParametersExample() throws Exception {
variantNameParameterDescription(),
dateParameterDescription(),
csvSeparatorParameterDescription(),
+ csvFieldsParameterDescription(),
selectCodesParameterDescription(),
selectLevelParameterDescription(),
presentationNamePatternParameterDescription(),
@@ -715,7 +719,7 @@ public void correspondsOptionalParametersExample() throws Exception {
this.mockMvc.perform(
getWithContext("/classifications/" + CLASS_ID_KOMMUNEINNDELING
+ "/corresponds?targetClassificationId=" + CLASS_ID_BYDELSINNDELING
- + "&from=2014-01-01&to=2016-01-01&csvSeparator=;"
+ + "&from=2014-01-01&to=2016-01-01&csvSeparator=;&csvFields=sourceCode,sourceName"
+ "&language=nb&includeFuture=true").accept("text/csv"))
.andDo(this.documentationHandler = document("{method-name}",
preprocessRequest(prettyPrint()),
@@ -725,6 +729,7 @@ public void correspondsOptionalParametersExample() throws Exception {
targetClassificationIdParameterDescription(),
fromParameterDescription(),
toParameterDescription(),
+ csvFieldsParameterDescription(),
csvSeparatorParameterDescription(),
languageDescription(),
includeFutureDescription(""))))
@@ -757,7 +762,7 @@ public void correspondsAtOptionalParametersExample() throws Exception {
// @formatter:off
this.mockMvc.perform(
getWithContext("/classifications/" + CLASS_ID_KOMMUNEINNDELING
- + "/correspondsAt?targetClassificationId=" + CLASS_ID_BYDELSINNDELING + "&date=2016-01-01&csvSeparator=;"
+ + "/correspondsAt?targetClassificationId=" + CLASS_ID_BYDELSINNDELING + "&date=2016-01-01&csvSeparator=;&csvFields=sourceCode,sourceName"
+ "&language=nb&includeFuture=true").accept("text/csv"))
.andDo(this.documentationHandler = document("{method-name}",
preprocessRequest(prettyPrint()),
@@ -767,6 +772,7 @@ public void correspondsAtOptionalParametersExample() throws Exception {
targetClassificationIdParameterDescription(),
dateParameterDescription(),
csvSeparatorParameterDescription(),
+ csvFieldsParameterDescription(),
languageDescription(),
includeFutureDescription(""))))
.andExpect(status().isOk());
@@ -884,7 +890,38 @@ public void csvSeparatorExample() throws Exception {
.andExpect(status().isOk());
// @formatter:on
}
-
+ @Test
+ public void csvFieldsCodesExample() throws Exception {
+ DateRange dateRange = DateRange.create("2014-01-01", "2015-01-01");
+ when(classificationServiceMock.findClassificationCodes(any(), any(), any(), any())).thenReturn(
+ createKommuneInndelingCodes(dateRange));
+ // @formatter:off
+ this.mockMvc.perform(
+ getWithContext("/classifications/" + CLASS_ID_KOMMUNEINNDELING
+ + "/codes?from=2014-01-01&to=2015-01-01&csvSeparator=;&csvFields=name,code")
+ .accept("text/csv"))
+ .andDo(this.documentationHandler = document("{method-name}",
+ preprocessRequest(prettyPrint()),
+ preprocessResponse(/*prettyPrint()*/)))
+ .andExpect(status().isOk());
+ // @formatter:on
+ }
+ @Test
+ public void csvFieldsCodesAtExample() throws Exception {
+ DateRange dateRange = DateRange.create("2014-01-01", "2015-01-01");
+ when(classificationServiceMock.findClassificationCodes(any(), any(), any(), any())).thenReturn(
+ createKommuneInndelingCodes(dateRange));
+ // @formatter:off
+ this.mockMvc.perform(
+ getWithContext("/classifications/" + CLASS_ID_KOMMUNEINNDELING
+ + "/codesAt?date=2014-01-01&csvSeparator=;&csvFields=name,code")
+ .accept("text/csv"))
+ .andDo(this.documentationHandler = document("{method-name}",
+ preprocessRequest(prettyPrint()),
+ preprocessResponse(/*prettyPrint()*/)))
+ .andExpect(status().isOk());
+ // @formatter:on
+ }
@Test
public void selectLevelExample() throws Exception {
DateRange dateRange = DateRange.create("2014-01-01", "2015-01-01");
@@ -1107,6 +1144,10 @@ private ParameterDescriptor csvSeparatorParameterDescription() {
return parameterWithName("csvSeparator").description(
"[Optional] specifies separator to be used for csv format. For details see <<_csvseparator, csvSeparator>>");
}
+ private ParameterDescriptor csvFieldsParameterDescription() {
+ return parameterWithName("csvFields").description(
+ "[Optional] specifies which fields should be included in the csv output. For details see <<_csvfields, csvfields>>");
+ }
private ParameterDescriptor selectCodesParameterDescription() {
return parameterWithName("selectCodes").description(
diff --git a/klass-api/src/test/java/no/ssb/klass/api/config/MockConfig.java b/klass-api/src/test/java/no/ssb/klass/api/config/MockConfig.java
index e4aa900d..3520f71e 100644
--- a/klass-api/src/test/java/no/ssb/klass/api/config/MockConfig.java
+++ b/klass-api/src/test/java/no/ssb/klass/api/config/MockConfig.java
@@ -1,5 +1,6 @@
package no.ssb.klass.api.config;
+import no.ssb.klass.api.controllers.validators.CsvFieldsValidator;
import no.ssb.klass.core.service.*;
import no.ssb.klass.api.controllers.ClassificationController;
import org.mockito.Mockito;
@@ -19,10 +20,13 @@ public class MockConfig {
@Autowired
private StatisticsService statisticsService;
- @Autowired
+ @Autowired
private UserService userService;
+ @Autowired
+ private CsvFieldsValidator csvFieldsValidator;
+
@Bean
public ClassificationService classificationService() {
return Mockito.mock(ClassificationService.class);
@@ -48,8 +52,14 @@ public UserService userService() {
return Mockito.mock(UserService.class);
}
+ @Bean
+ private CsvFieldsValidator csvFieldsValidator() {
+ return new CsvFieldsValidator();
+ }
+
+
@Bean
public ClassificationController classificationController() {
- return new ClassificationController(classificationService, subscriberService, searchService, statisticsService);
+ return new ClassificationController(classificationService, subscriberService, searchService, statisticsService, csvFieldsValidator);
}
}
diff --git a/klass-forvaltning/pom.xml b/klass-forvaltning/pom.xml
index e41716b3..e7a385d7 100644
--- a/klass-forvaltning/pom.xml
+++ b/klass-forvaltning/pom.xml
@@ -14,7 +14,7 @@
no.ssb.klass
klass-root
- 2.0.18
+ 2.1.0
../
diff --git a/klass-shared/pom.xml b/klass-shared/pom.xml
index 4780f534..2a811c76 100644
--- a/klass-shared/pom.xml
+++ b/klass-shared/pom.xml
@@ -11,7 +11,7 @@
no.ssb.klass
klass-root
- 2.0.18
+ 2.1.0
diff --git a/klass-solr/pom.xml b/klass-solr/pom.xml
index bbeaa266..c816cbc2 100644
--- a/klass-solr/pom.xml
+++ b/klass-solr/pom.xml
@@ -13,7 +13,7 @@
no.ssb.klass
klass-root
- 2.0.18
+ 2.1.0
diff --git a/pom.xml b/pom.xml
index 716069e7..792c87e3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
no.ssb.klass
klass-root
- 2.0.18
+ 2.1.0
pom