package org.mitre.rhex; import edu.umd.cs.findbugs.annotations.NonNull; import org.apache.commons.lang.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.Namespace; import org.mitre.test.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.Collections; import java.util.List; /** * Test for section document creation * * <pre> * 6.4 baseURL/sectionpath * * 6.4.2.2 POST Add new document * * When adding a new section document, the request Content Type MUST be �multipart/form-data� * if including metadata. In this case, the content part MUST contain the section document. * The content part MUST include a Content-Disposition header with a disposition of �form-data� * and a name of �content.� The metadata part MUST contain the metadata for this section document. * The metadata part MUST include a Content-Disposition header with a disposition of �form-data� * and a name of �metadata.� It is to be treated as informational, since the service MUST compute * the valid new metadata based on the requirements found in the HRF specification. The content * media type MUST conform to the media type of either the section or the media type identified * by metadata of the section document. For XML media types, the document MUST also conform to * the XML schema identified by the extensionId for the section or the document metadata. * * If the content cannot be validated against the media type and the XML schema identified * by the content type of this section, the server MUST return a status code of 400. * * If the request is successful, the new section document MUST show up in the document * feed for the section. The server returns a 201 with a Location header containing * the URI of the new document. * * Status Code: 201, 400 * </pre> * * @author Jason Mathews, MITRE Corp. * Date: 2/20/12 10:45 AM */ public class DocumentCreateCheck extends BaseXmlTest { @NonNull @Override public String getId() { return "6.4.2.3"; } @Override public boolean isRequired() { return true; } @NonNull public String getName() { return "New document MUST show up in the ATOM feed for the section"; } @NonNull public List<Class<? extends TestUnit>> getDependencyClasses() { return Collections.<Class<? extends TestUnit>> singletonList(DocumentCreate.class); // 6.4.2.2 } public void execute() throws TestException { // pre-conditions: for this test to be executed the prerequisite test BaseUrlRootXml must have passed // with 200 HTTP response and valid root.xml content. TestUnit baseTest = getDependency(DocumentCreate.class); if (baseTest == null) { // assertion failed: this should never be null log.error("Failed to retrieve prerequisite test: BaseUrlRootXml"); setStatus(StatusEnumType.SKIPPED, "Failed to retrieve prerequisite test: 6.3.1.1"); return; } final DocumentCreate documentCreate = (DocumentCreate) baseTest; URI documentURL = documentCreate.getDocumentURL(); if (documentURL == null) { log.error("Failed to retrieve prerequisite test results: DocumentCreate"); setStatus(StatusEnumType.SKIPPED, "Failed to retrieve prerequisite test results: 6.4.2.2"); return; } /* expecting: <?xml version="1.0" encoding="UTF-8"?> <root xmlns="http://projecthdata.org/hdata/schemas/2009/06/core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <extensions> <extension extensionId="1">http://projecthdata.org/extension/c32</extension> <extension extensionId="2">http://projecthdata.org/hdata/schemas/2009/06/allergy</extension> ... </extensions> <sections> <section path="c32" name="C32" extensionId="1"/> <section path="allergies" name="Allergies" extensionId="2"/> ... </sections> </root> */ String sectionPath = documentCreate.getSectionPath(); if (sectionPath == null) { log.error("Failed to retrieve prerequisite section from DocumentCreate"); setStatus(StatusEnumType.SKIPPED, "Failed to retrieve prerequisite test results: 6.4.2.2"); return; } System.out.println("section path: " + sectionPath); sendRequest(sectionPath, documentURL); } protected void sendRequest(String sectionPath, URI documentURL) throws TestException { final Context context = Loader.getInstance().getContext(); final HttpClient client = context.getHttpClient(); try { URI baseUrl = context.getBaseURL(sectionPath); if (log.isDebugEnabled()) { System.out.println("\nURL: " + baseUrl); } HttpGet req = new HttpGet(baseUrl); req.setHeader("Accept", MIME_APPLICATION_XML); // "application/atom+xml, text/xml, application/xml"); System.out.println("executing request: " + req.getRequestLine()); HttpResponse response = context.executeRequest(client, req); int code = response.getStatusLine().getStatusCode(); final HttpEntity entity = response.getEntity(); if (code != 200 || log.isDebugEnabled()) { System.out.println("----------------------------------------"); dumpResponse(req, response, false); } if (code != 200) { setStatus(StatusEnumType.FAILED, "Expected 200 HTTP status code but was: " + code); return; } validateContent(context, entity, documentURL); setStatus(StatusEnumType.SUCCESS); } catch (IOException e) { throw new TestException(e); } catch (URISyntaxException e) { throw new TestException(e); } catch (JDOMException e) { throw new TestException(e); } finally { client.getConnectionManager().shutdown(); } } private void validateContent(Context context, HttpEntity entity, URI documentURL) throws TestException, JDOMException, IOException { if (entity == null) { // no body log.info("no BODY in response for section feed"); throw new TestException("encountered non-body response to section request"); } final String contentType = ClientHelper.getContentType(entity); if (!MIME_APPLICATION_ATOM_XML.equals(contentType)) { addWarning("Expected " + MIME_APPLICATION_ATOM_XML + " content-type for section but was: " + contentType); } /* verify document is now added to the section ATOM feed: <?xml version="1.0"?> <feed xmlns="http://www.w3.org/2005/Atom"> <id>1333458159</id> <title>/vital_signs</title> <generator version="1.0">atom feed generator</generator> <entry> <id>1</id> <title>Systolic Blood Pressure</title> <updated>1292389200</updated> <link href="http://rhex.mitre.org:3000/records/1/vital_signs/4f735368d7d76a43b200001f" type="application/xml"/> <link href="http://rhex.mitre.org:3000/records/1/vital_signs/4f735368d7d76a43b200001f" type="application/json"/> </entry> ... */ ByteArrayOutputStream bos = new ByteArrayOutputStream(); entity.writeTo(bos); Document doc = getDefaultDocument(context, bos); final Namespace atomNs = Namespace.getNamespace(NAMESPACE_W3_ATOM_2005); // String targetUrl = documentURL.toASCIIString(); for(Object feedChild : doc.getRootElement().getChildren("entry", atomNs)) { if (!(feedChild instanceof Element)) continue; Element entry = (Element)feedChild; for(Object entryChild : entry.getChildren("link", atomNs)) { if (!(entryChild instanceof Element)) continue; Element link = (Element)entryChild; String href = link.getAttributeValue("href"); // required if (StringUtils.isNotBlank(href)) try { URI uri = new URI(href); if (!uri.isAbsolute()) { uri = documentURL.resolve(uri); log.trace("relative URL {} -> {}", href, uri); } if (documentURL.equals(uri)) { log.debug("Found target href in ATOM feed"); return; } } catch (URISyntaxException e) { if (addLogWarning("Bad URL syntax")) log.warn("Bad URL " + href, e); } } } if (log.isDebugEnabled()) System.out.println(bos.toString()); throw new TestException("Failed to verify document appears in ATOM feed"); } }