Skip to content

Commit

Permalink
Merge pull request #2434 from IBM/tbieste-issue-896
Browse files Browse the repository at this point in the history
Issue #896 - Add checking for MIME-type parameter fhirVersion
  • Loading branch information
tbieste authored Jun 3, 2021
2 parents 020299e + a7e01ae commit ad4acab
Show file tree
Hide file tree
Showing 7 changed files with 320 additions and 14 deletions.
4 changes: 2 additions & 2 deletions docs/src/pages/Conformance.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
layout: post
title: Conformance
description: Notes on the Conformance of the IBM FHIR Server
date: 2021-05-19
date: 2021-05-26
permalink: /conformance/
---

Expand All @@ -12,7 +12,7 @@ The IBM FHIR Server aims to be a conformant implementation of the HL7 FHIR speci
## Capability statement
The HL7 FHIR specification defines [an interaction](https://www.hl7.org/fhir/R4/http.html#capabilities) for retrieving a machine-readable description of the server's capabilities via the `[base]/metadata` endpoint. The IBM FHIR Server implements this interaction and generates a `CapabilityStatement` resource based on the current server configuration. While the `CapabilityStatement` resource is ideal for certain uses, this markdown document provides a human-readable summary of important details, with a special focus on limitations of the current implementation and deviations from the specification.

The IBM FHIR Server supports only version 4.0.1 of the specification and ignores the optional MIME-type parameter `fhirVersion`.
The IBM FHIR Server supports only version 4.0.1 of the specification.

## FHIR HTTP API
The HL7 FHIR specification is more than just a data format. It defines an [HTTP API](https://www.hl7.org/fhir/R4/http.html) for creating, reading, updating, deleting, and searching over FHIR resources. The IBM FHIR Server implements the full API for every resource defined in the specification, with the following exceptions:
Expand Down
16 changes: 14 additions & 2 deletions fhir-core/src/main/java/com/ibm/fhir/core/FHIRMediaType.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
/*
* (C) Copyright IBM Corp. 2019
* (C) Copyright IBM Corp. 2019, 2021
*
* SPDX-License-Identifier: Apache-2.0
*/

package com.ibm.fhir.core;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import javax.ws.rs.core.MediaType;

/**
* This class contains definitions of some non-standard media types.
*/
public class FHIRMediaType extends MediaType {

public final static String SUBTYPE_FHIR_JSON = "fhir+json";
public final static String APPLICATION_FHIR_JSON = "application/" + SUBTYPE_FHIR_JSON;
public final static MediaType APPLICATION_FHIR_JSON_TYPE = new MediaType("application", SUBTYPE_FHIR_JSON);
Expand All @@ -29,6 +35,12 @@ public class FHIRMediaType extends MediaType {
public final static MediaType APPLICATION_FHIR_NDJSON_TYPE = new MediaType("application", SUBTYPE_FHIR_NDJSON);

public final static String SUBTYPE_FHIR_PARQUET = "fhir+parquet";
public static final String APPLICATION_PARQUET = "application/" + SUBTYPE_FHIR_PARQUET;
public static final String APPLICATION_PARQUET = "application/" + SUBTYPE_FHIR_PARQUET;
public final static MediaType APPLICATION_FHIR_PARQUET_TYPE = new MediaType("application", SUBTYPE_FHIR_PARQUET);

// Supported values for the MIME-type parameter fhirVersion.
// https://www.hl7.org/fhir/http.html#version-parameter
public static final String FHIR_VERSION_PARAMETER = "fhirVersion";
public static final Set<String> SUPPORTED_FHIR_VERSIONS =
Collections.unmodifiableSet(new HashSet<>(Arrays.asList("4.0","4.0.1")));
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import jakarta.json.JsonObject;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.testng.annotations.Test;
Expand Down Expand Up @@ -49,6 +50,8 @@
import com.ibm.fhir.path.evaluator.FHIRPathEvaluator.EvaluationContext;
import com.ibm.fhir.path.exception.FHIRPathException;

import jakarta.json.JsonObject;

/**
* Basic sniff test of the FHIR Server.
*/
Expand Down Expand Up @@ -125,6 +128,38 @@ public void testMetadataAPI_XML() {
assertNotNull(conf.getName());
}

/**
* Verify the 'metadata' API with valid fhirVersion in Accept header.
*/
@Test(groups = { "server-basic" })
public void testMetadataAPI_validFhirVersion() {
WebTarget target = getWebTarget();
MediaType mediaType = new MediaType("application", "fhir+json",
Collections.singletonMap(FHIRMediaType.FHIR_VERSION_PARAMETER, "4.0"));
Response response = target.path("metadata").request(mediaType).get();
assertResponse(response, Response.Status.OK.getStatusCode());
assertEquals(mediaType, response.getMediaType());

CapabilityStatement conf = response.readEntity(CapabilityStatement.class);
assertNotNull(conf);
assertNotNull(conf.getFormat());
assertEquals(6, conf.getFormat().size());
assertNotNull(conf.getVersion());
assertNotNull(conf.getName());
}

/**
* Verify the 'metadata' API with invalid fhirVersion in Accept header.
*/
@Test(groups = { "server-basic" })
public void testMetadataAPI_invalidFhirVersion() {
WebTarget target = getWebTarget();
MediaType mediaType = new MediaType("application", "fhir+json",
Collections.singletonMap(FHIRMediaType.FHIR_VERSION_PARAMETER, "3.0"));
Response response = target.path("metadata").request(mediaType).get();
assertResponse(response, Response.Status.NOT_ACCEPTABLE.getStatusCode());
}

/**
* Create a Patient, then make sure we can retrieve it.
*/
Expand Down Expand Up @@ -175,6 +210,50 @@ public void testCreatePatient_minimal() throws Exception {
TestUtil.assertResourceEquals(patient, responsePatient);
}

/**
* Create a minimal Patient with valid fhirVersion in Content-Type header, then make sure we can retrieve it.
*/
@Test(groups = { "server-basic" })
public void testCreatePatient_minimal_validFhirVersion() throws Exception {
WebTarget target = getWebTarget();

// Build a new Patient and then call the 'create' API.
Patient patient = TestUtil.readLocalResource("Patient_DavidOrtiz.json");

MediaType mediaType = new MediaType("application", "fhir+json",
Collections.singletonMap(FHIRMediaType.FHIR_VERSION_PARAMETER, "4.0"));
Entity<Patient> entity = Entity.entity(patient, mediaType);
Response response = target.path("Patient").request().post(entity, Response.class);
assertResponse(response, Response.Status.CREATED.getStatusCode());

// Get the patient's logical id value.
String patientId = getLocationLogicalId(response);

// Next, call the 'read' API to retrieve the new patient and verify it.
response = target.path("Patient/" + patientId).request(mediaType).get();
assertResponse(response, Response.Status.OK.getStatusCode());
Patient responsePatient = response.readEntity(Patient.class);

TestUtil.assertResourceEquals(patient, responsePatient);
}

/**
* Attempt to create a minimal Patient with invalid fhirVersion in Content-Type header.
*/
@Test( groups = { "server-basic" })
public void testCreatePatient_minimal_invalidFhirVersion() throws Exception {
WebTarget target = getWebTarget();

// Build a new Patient and then call the 'create' API.
Patient patient = TestUtil.readLocalResource("Patient_DavidOrtiz.json");

MediaType mediaType = new MediaType("application", "fhir+json",
Collections.singletonMap(FHIRMediaType.FHIR_VERSION_PARAMETER, "3.0"));
Entity<Patient> entity = Entity.entity(patient, mediaType);
Response response = target.path("Patient").request().post(entity, Response.class);
assertResponse(response, Response.Status.UNSUPPORTED_MEDIA_TYPE.getStatusCode());
}

/**
* Create a minimal Patient, then make sure we can retrieve it with varying format
*/
Expand All @@ -193,13 +272,60 @@ public void testCreatePatientMinimalWithFormat() throws Exception {
String patientId = getLocationLogicalId(response);

// Next, call the 'read' API to retrieve the new patient and verify it.
response = target.path("Patient/" + patientId).request(FHIRMediaType.APPLICATION_FHIR_JSON).header("_format", "application/fhir+json").get();
response = target.path("Patient/" + patientId).queryParam("_format", "application/fhir+json").request(FHIRMediaType.APPLICATION_FHIR_JSON).get();
assertResponse(response, Response.Status.OK.getStatusCode());
Patient responsePatient = response.readEntity(Patient.class);

TestUtil.assertResourceEquals(patient, responsePatient);
}

/**
* Create a minimal Patient, then make sure we can retrieve it with varying format with valid FHIR version
*/
@Test(groups = { "server-basic" })
public void testCreatePatientMinimalWithFormat_validFhirVersion() throws Exception {
WebTarget target = getWebTarget();

// Build a new Patient and then call the 'create' API.
Patient patient = TestUtil.readLocalResource("Patient_DavidOrtiz.json");

Entity<Patient> entity = Entity.entity(patient, FHIRMediaType.APPLICATION_FHIR_JSON);
Response response = target.path("Patient").request().post(entity, Response.class);
assertResponse(response, Response.Status.CREATED.getStatusCode());

// Get the patient's logical id value.
String patientId = getLocationLogicalId(response);

// Next, call the 'read' API to retrieve the new patient and verify it.
response = target.path("Patient/" + patientId).queryParam("_format", "application/fhir+json;fhirVersion=4.0").request(FHIRMediaType.APPLICATION_FHIR_JSON).get();
assertResponse(response, Response.Status.OK.getStatusCode());
Patient responsePatient = response.readEntity(Patient.class);

TestUtil.assertResourceEquals(patient, responsePatient);
}

/**
* Create a minimal Patient, then attempt to retrieve it with varying format with invalid FHIR version
*/
@Test(groups = { "server-basic" })
public void testCreatePatientMinimalWithFormat_invalidFhirVersion() throws Exception {
WebTarget target = getWebTarget();

// Build a new Patient and then call the 'create' API.
Patient patient = TestUtil.readLocalResource("Patient_DavidOrtiz.json");

Entity<Patient> entity = Entity.entity(patient, FHIRMediaType.APPLICATION_FHIR_JSON);
Response response = target.path("Patient").request().post(entity, Response.class);
assertResponse(response, Response.Status.CREATED.getStatusCode());

// Get the patient's logical id value.
String patientId = getLocationLogicalId(response);

// Next, call the 'read' API to attempt to retrieve the new patient
response = target.path("Patient/" + patientId).queryParam("_format", "application/fhir+json;fhirVersion=3.0").request(FHIRMediaType.APPLICATION_FHIR_JSON).get();
assertResponse(response, Response.Status.NOT_ACCEPTABLE.getStatusCode());
}

/**
* Create a minimal Patient, then make sure we can retrieve it.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* (C) Copyright IBM Corp. 2017,2019
* (C) Copyright IBM Corp. 2017, 2021
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -45,18 +45,18 @@ public void testPrettyFormatting() throws Exception {

// Next, call the 'read' API to retrieve the new patient and verify it.
response =
target.queryParam("_pretty", "true").path("Patient/" + patientId)
.request(FHIRMediaType.APPLICATION_FHIR_JSON).header("_format", "application/fhir+json").get();
target.queryParam("_pretty", "true").queryParam("_format", "application/fhir+json").path("Patient/" + patientId)
.request(FHIRMediaType.APPLICATION_FHIR_JSON).get();
assertResponse(response, Response.Status.OK.getStatusCode());

String prettyOutput = response.readEntity(String.class);

response =
target.queryParam("_pretty", "false").path("Patient/" + patientId)
.request(FHIRMediaType.APPLICATION_FHIR_JSON).header("_format", "application/fhir+json").get();
target.queryParam("_pretty", "false").queryParam("_format", "application/fhir+json").path("Patient/" + patientId)
.request(FHIRMediaType.APPLICATION_FHIR_JSON).get();

String notPrettyOutput = response.readEntity(String.class);

assertNotEquals(prettyOutput, notPrettyOutput);
assertFalse(notPrettyOutput.contains("\n"));
assertTrue(prettyOutput.contains("\n"));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* (C) Copyright IBM Corp. 2021
*
* SPDX-License-Identifier: Apache-2.0
*/

package com.ibm.fhir.server.exception;

import javax.servlet.http.HttpServletResponse;

import com.ibm.fhir.exception.FHIROperationException;

public class FHIRRestServletRequestException extends FHIROperationException {
private static final long serialVersionUID = 1L;
private int httpStatusCode = HttpServletResponse.SC_BAD_REQUEST;

public FHIRRestServletRequestException(String message) {
super(message);
}

public FHIRRestServletRequestException(String message, Throwable cause) {
super(message, cause);
}

public FHIRRestServletRequestException(String message, int httpStatusCode) {
this(message, httpStatusCode, null);
}

public FHIRRestServletRequestException(String message, int httpStatusCode, Throwable t) {
super(message, t);
this.httpStatusCode = httpStatusCode;
}

public int getHttpStatusCode() {
return httpStatusCode;
}
}
Loading

0 comments on commit ad4acab

Please sign in to comment.