Exemplo n.º 1
0
def update_ig_config(data_path, ig_control_filepath, add=True, rm_file=False):
    """
    Add/remove the configuration entries to/from IG resource file for all
    resources in data_path.

    Optional - delete the resource file(s). Only applies if add=False.

    When a new resource file is added to the IG it will not be picked up for
    validation or site generation by the IG publisher unless the expected
    configuration for that resource is present.

    :param data_path: Path to directory or file containing resource(s) to
    remove from the IG configuration
    :param ig_control_filepath: Path to the implementation guide control file
    :type ig_control_filepath: str
    :param add: Whether to add the configuration versus remove it
    :type add: bool
    :param rm_file: Whether to delete the resource file(s). Only applies if
    add=False
    :type rm_file: bool
    """
    # Load resource dicts
    resource_dicts = loader.load_resources(data_path)
    # Load IG resource dict
    ig_resource_dict = deepcopy(_load_ig_resource_dict(ig_control_filepath))
    # Validate and add resource to IG configuration
    _custom_validate(resource_dicts)

    # Update the IG configuration
    _update_ig_config(resource_dicts, ig_resource_dict)
Exemplo n.º 2
0
def validate(ig_control_filepath, clear_output=False, publisher_opts=''):
    """
    Validate the FHIR data model (FHIR conformance and example resources)

    Run the HL7 FHIR implementation guide (IG) publisher in a Docker container
    to validate conformance resources and any example resources against the
    conformance resources.

    See https://confluence.hl7.org/display/FHIR/IG+Publisher+Documentation

    Validation fails if any of the following are true:
        - The publisher returns a non-zero exit code
        - QA report contains errors with the FHIR resources.
        - Any one of the resource files fail model validation in
        _custom_validate

    IG build errors are ignored since this method only validates the data
    model

    :param ig_control_filepath: Path to the implementation guide control file
    :type ig_control_filepath: str
    :param clear_output: Whether to clear all generated output before
    validating
    :type clear_output: boolean
    :param publisher_opts: IG publisher command line options forwarded directly
    to the publisher CLI
    :type publisher_opts: str
    """
    logger.info('Begin validation of FHIR data model')

    ig_control_filepath = os.path.abspath(
        os.path.expanduser(ig_control_filepath))

    # Clear previously generated output
    if clear_output:
        clear_ig_output(ig_control_filepath)

    # Read in ig resource file
    ig_resource_dict = _load_ig_resource_dict(ig_control_filepath)

    # Collect resource filepaths
    resource_dicts = []
    site_root = os.path.dirname(ig_control_filepath)
    ig = ig_resource_dict['content']
    for param in ig.get('definition', {}).get('parameter', []):
        if param.get('code') != 'path-resource':
            continue
        resource_dicts.extend(
            loader.load_resources(os.path.join(site_root, param.get('value'))))

    # Validate and add resource to IG configuration
    _custom_validate(resource_dicts)

    # Add entry to IG configuration
    _update_ig_config(resource_dicts, ig_resource_dict, add=True)

    # Do the standard HL7 FHIR validation via the IG Publisher
    _fhir_validate(ig_control_filepath, publisher_opts)

    logger.info('End validation of FHIR data model')
Exemplo n.º 3
0
def _load_ig_resource_dict(ig_control_filepath):
    """
    Load IG resource JSON into a dict

    Find the location of the IG resource file from the ig control file first

    :param ig_control_filepath: Path to the implementation guide control file
    :type ig_control_filepath: str
    :returns: IG resource dict
    """
    # Read in ig control file
    ig_control_filepath = os.path.abspath(
        os.path.expanduser(ig_control_filepath))
    ig_config = ConfigParser()
    ig_config.read(ig_control_filepath)

    # Read in ig resource file
    ig_filepath = os.path.join(
        os.path.split(ig_control_filepath)[0],
        dict(ig_config["IG"]).get("ig"))
    return loader.load_resources(ig_filepath)[0]
Exemplo n.º 4
0
def test_search_params(client, search_param_filename, resource_type,
                       search_value, expected_count):
    """
    Test SearchParameters

    Query the test server at the /{resource_type} endpoint using the
    search parameter and specified search value. Check that the returned total
    matches specified expected count.
    """
    sp = load_resources(os.path.join(SEARCH_PARAM_DIR,
                                     search_param_filename))[0]["content"]
    success, result = client.send_request(
        "get",
        f"{client.base_url}/{resource_type}",
        params={
            sp["code"]: search_value,
            "_total": "accurate"
        },
    )
    assert success
    pprint(result)
    assert result["response"]["total"] == expected_count
Exemplo n.º 5
0
def publish_to_server(
    resource_file_or_dir,
    base_url,
    username=None,
    password=None,
    fhir_version=None,
    submission_order=RESOURCE_SUBMISSION_ORDER,
):
    """
    Push FHIR resources to a FHIR server

    Delete the resources if they exist on the server
    PUT any resources that have an `id` attribute defined
    POST any resources that do not have an `id` attribute defined

    :param resource_file_or_dir: path to a directory containing FHIR resource
    files or path to a single resource file
    :type resource_file_or_dir: str
    :param resource_type: directory containing FHIR resource files
    :type resource_type: str
    :param username: Server account username
    :type username: str
    :param password: Server account password
    :type password: str
    :param fhir_version: FHIR version number
    :type fhir_version: str
    """
    logger.info(
        f"Begin publishing resources in {resource_file_or_dir} to {base_url}")

    if username and password:
        auth = HTTPBasicAuth(username, password)
    else:
        auth = None

    client = FhirApiClient(base_url=base_url,
                           auth=auth,
                           fhir_version=fhir_version)
    resources = loader.load_resources(resource_file_or_dir)

    # Re-order resources according to submission order
    resources_by_type = defaultdict(list)
    for r_dict in resources:
        resources_by_type[r_dict["resource_type"]].append(r_dict)
    resources = []
    for r_type in submission_order:
        resources.extend(resources_by_type.pop(r_type, []))
    for r_type, remaining in resources_by_type.items():
        resources.extend(remaining)

    # Delete existing resources
    for r_dict in resources:
        r = r_dict["content"]
        if "url" in r:
            success = client.delete_all(f'{base_url}/{r["resourceType"]}',
                                        params={"url": r["url"]})
        elif "id" in r:
            success, results = client.send_request(
                "delete", f'{base_url}/{r["resourceType"]}/{r["id"]}')
        else:
            logger.warning(
                f'⚠️ Could not delete {r_dict["filename"]}. No way to '
                "identify the resource. Tried looking for `url` and `id` in "
                "payload.")

    # POST if no id is provided, PUT if id is provideds
    for r_dict in resources:
        r = r_dict["content"]
        id_ = r.get("id")
        if id_:
            success, results = client.send_request(
                "put", f'{base_url}/{r["resourceType"]}/{id_}', json=r)
        else:
            success, results = client.send_request(
                "post", f'{base_url}/{r["resourceType"]}', json=r)
        if not success:
            errors = [
                r for r in results["response"]["issue"]
                if r["severity"] == "error"
            ]
            raise Exception(f"Publish failed! Caused by:\n{pformat(errors)}")
Exemplo n.º 6
0
def publish_to_server(resource_file_or_dir,
                      base_url,
                      username=None,
                      password=None,
                      fhir_version=None):
    """
    Push FHIR resources to a FHIR server

    Delete the resources if they exist on the server
    PUT any resources that have an `id` attribute defined
    POST any resources that do not have an `id` attribute defined

    :param resource_file_or_dir: path to a directory containing FHIR resource
    files or path to a single resource file
    :type resource_file_or_dir: str
    :param resource_type: directory containing FHIR resource files
    :type resource_type: str
    :param username: Server account username
    :type username: str
    :param password: Server account password
    :type password: str
    :param fhir_version: FHIR version number
    :type fhir_version: str
    """
    logger.info(
        f'Begin publishing resources in {resource_file_or_dir} to {base_url}')

    if username and password:
        auth = HTTPBasicAuth(username, password)
    else:
        auth = None

    client = FhirApiClient(base_url=base_url,
                           auth=auth,
                           fhir_version=fhir_version)
    resources = loader.load_resources(resource_file_or_dir)

    # Delete existing resources
    for r_dict in resources:
        r = r_dict['content']
        if 'url' in r:
            success = client.delete_all(f'{base_url}/{r["resourceType"]}',
                                        params={'url': r['url']})
        elif 'id' in r:
            success, results = client.send_request(
                'delete', f'{base_url}/{r["resourceType"]}/{r["id"]}')
        else:
            logger.warning(
                f'⚠️ Could not delete {r_dict["filename"]}. No way to '
                'identify the resource. Tried looking for `url` and `id` in '
                'payload.')

    # POST if no id is provided, PUT if id is provided
    for r_dict in resources:
        r = r_dict['content']
        id_ = r.get('id')
        if id_:
            success, results = client.send_request(
                'put', f'{base_url}/{r["resourceType"]}/{id_}', json=r)
        else:
            success, results = client.send_request(
                'post', f'{base_url}/{r["resourceType"]}', json=r)