Skip to content

Deploy the Application Package

Deploying an Application Package using the OGC API Processes API uses the API resource highlighted in the table below:

Resource Path Purpose Part
Landing page / Top-level resource serving as an entry point. Part 1
Conformance declaration /conformance Information about the functionality supported by the server. Part 1
API Definition /api Metadata about the API itself. Part 1
Process list /processes Lists available processes with identifiers and links to descriptions. Part 1
Process description /processes/{processID} Retrieves detailed information about a specific process. Part 1
Process execution /processes/{processID}/execution (POST) Executes a process, creating a job. Part 1
Deploy Process /processes (POST) Deploys a new process on the server. Part 2
Replace Process /processes/{processID} (PUT) Replaces an existing process with a new version. Part 2
Undeploy Process /processes/{processID} (DELETE) Removes an existing process from the server. Part 2
EO Application Package /processes/{processID}/package Get the EOAP associated with a deployed process. Part 2
Job status info /jobs/{jobID} Retrieves the current status of a job. Part 1
Job results /jobs/{jobID}/results Retrieves the results of a job. Part 1
Job list /jobs Retrieves a list of submitted jobs. Part 1
Job deletion /jobs/{jobID} (DELETE) Cancels and deletes a job. Part 1

This resource permits the deployment of the an Application Package and provide two options for the Content-Type.

Hands-on - Deploy the water_bodies Application Package

Encoding Options

The deployment can use two encodings that are based on the same CWL conformance class. Both methods utilize the same water_bodies.cwl file, but the way the file is provided differs: - OGC Application Package Encoding (application/ogcapppkg+json): This method allows you to reference the CWL file by providing its location, rather than including the file's content in the request. - CWL Encoding (application/cwl+yaml): This method requires the CWL file content to be included directly in the request body.

Request Configuration

When selecting a content type, the request body text area updates to contain a relevant payload template for that encoding. Warning: If the payload is manually edited, switching to a different encoding may not refresh the text area. In this case, the Reset button can be used to restore the appropriate template.

Server Response

After executing the deployment request, the server responds with a process summary similar to the one obtained from the previous process listing endpoint. The server’s response includes a Location header containing the URL to access the detailed process description.

Next Steps

You can either: - Return to the list of available processes to verify the newly deployed process. - Proceed to the next step to review the process description in detail

Setup

Lists available processes in the acme namespace.

NOTE:: if the acme namespace does not exist, ZOO Project will create it.

import requests
import json
import yaml
import time
import os
import sys
from loguru import logger
from pystac.item_collection import ItemCollection
from pprint import pprint
namespace = "acme"

ogc_api_endpoint = f"http://zoo-project-dru-service/{namespace}/ogc-api"

r = requests.get(f"{ogc_api_endpoint}/processes")

r.status_code
200

If the application package was deployed previously, delete it.

def undeploy(process_id):

    r = requests.delete(f"{ogc_api_endpoint}/processes/{process_id}")

    return r

app_package_entry_point = "water-bodies"

r = undeploy(app_package_entry_point)

r.status_code
404

OGC Application Package Encoding (application/ogcapppkg+json)

OGC Application Package Encoding (application/ogcapppkg+json): This method allows you to reference the CWL file by providing its location, rather than including the file's content in the request.

app_package_entry_point = "water-bodies"
def app_package_deployment(app_package_entry_point, app_package_url):  
  headers = {"accept": "application/json", 
          "Content-Type": "application/ogcapppkg+json"}
  response = requests.post(

      f"{ogc_api_endpoint}/processes/?w={app_package_entry_point}",
      headers=headers,
      json = {
        "executionUnit": {
          "href": app_package_url,
          "type": "application/cwl"
        }
      }
  )
  return response

def check_app_package_deployment(app_package_entry_point):
    r = requests.get(f"{ogc_api_endpoint}/processes/")

    if r.status_code == 200:
        # Parse the JSON response
        all_processes = r.json()
        for process in all_processes.get("processes", []):
          if process.get("id") in [app_package_entry_point]:
              logger.info(f"Application package {app_package_entry_point} is already deployed.")
              return True
          else:
              logger.warning(f"{app_package_entry_point} still not deployed.")
              return False
    else:
        logger.error(f"Failed to retrieve processes. Status code: {r.status_code}")
        sys.exit(1)

def get_latest_application_package_version(repository_owner, repo_name):

    url = f"https://api.github.com/repos/{repository_owner}/{repo_name}/releases/latest"
    response = requests.get(url)
    response.raise_for_status()  # raise error if request failed

    latest_tag = response.json().get("tag_name")
    return latest_tag
app_package_entry_point = "water-bodies"
is_package_deployed = check_app_package_deployment(app_package_entry_point)
repo_name = "mastering-app-package"
repository_owner = os.environ.get("REPOSITORY_OWNER", "eoap")
latest_application_package_version = get_latest_application_package_version(repository_owner, repo_name)
logger.info(f"Latest version is:  {latest_application_package_version}")
app_package_url = f"https://github.com/{repository_owner}/mastering-app-package/releases/download/{latest_application_package_version}/app-water-bodies-cloud-native.{latest_application_package_version}.cwl"
if not is_package_deployed:

    response= app_package_deployment(app_package_entry_point, app_package_url)
    pprint(response.json())
2025-08-20 13:40:19.690 | WARNING  | __main__:check_app_package_deployment:29 - water-bodies still not deployed.
2025-08-20 13:40:19.985 | INFO     | __main__:<module>:6 - Latest version is:  1.1.1


{'description': 'Water bodies detection based on NDWI and otsu threshold '
                'applied to Sentinel-2 COG STAC items',
 'id': 'water-bodies',
 'jobControlOptions': ['async-execute', 'dismiss'],
 'links': [{'href': 'http://localhost:8080/acme/ogc-api/processes/water-bodies/execution',
            'rel': 'http://www.opengis.net/def/rel/ogc/1.0/execute',
            'title': 'Execute End Point',
            'type': 'application/json'}],
 'metadata': [{'role': 'https://schema.org/softwareVersion', 'value': '1.1.1'},
              {'role': 'https://schema.org/author',
               'value': {'@context': 'https://schema.org',
                         '@type': 'Person',
                         's.affiliation': 'ACME',
                         's.email': 'jane.doe@acme.earth',
                         's.name': 'Jane Doe'}}],
 'mutable': True,
 'outputTransmission': ['value', 'reference'],
 'title': 'Water bodies detection based on NDWI and otsu threshold',
 'version': '1.1.1'}
dict(response.headers)
{'Date': 'Wed, 20 Aug 2025 13:40:19 GMT',
 'Server': 'Apache/2.4.41 (Ubuntu)',
 'X-Powered-By': 'ZOO-Project-DRU',
 'X-Also-Powered-By': 'jwt.securityIn',
 'X-Also-Also-Powered-By': 'dru.securityIn',
 'Location': 'http://localhost:8080/acme/ogc-api/processes/water-bodies',
 'Keep-Alive': 'timeout=5, max=100',
 'Connection': 'Keep-Alive',
 'Transfer-Encoding': 'chunked',
 'Content-Type': 'application/json;charset=UTF-8'}

CWL Encoding (application/cwl+yaml)

This method requires the CWL file content to be included directly in the request body.

If the application package was deployed previously, delete it.

app_package_entry_point = "water-bodies"

r = undeploy(app_package_entry_point)
r.status_code
204

Download the application package from https://github.com/eoap/mastering-app-package/releases/download/1.0.0/app-water-bodies-cloud-native.1.0.0.cwl

r = requests.get(app_package_url)

app_package_content  = yaml.safe_load(r.content)

app_package_content
{'cwlVersion': 'v1.0',
 '$namespaces': {'s': 'https://schema.org/'},
 's:softwareVersion': '1.1.1',
 'schemas': ['http://schema.org/version/9.0/schemaorg-current-http.rdf'],
 '$graph': [{'class': 'Workflow',
   'id': 'water-bodies',
   'label': 'Water bodies detection based on NDWI and otsu threshold',
   'doc': 'Water bodies detection based on NDWI and otsu threshold applied to Sentinel-2 COG STAC items',
   'requirements': [{'class': 'ScatterFeatureRequirement'},
    {'class': 'SubworkflowFeatureRequirement'}],
   'inputs': {'aoi': {'label': 'area of interest',
     'doc': 'area of interest as a bounding box',
     'type': 'string'},
    'epsg': {'label': 'EPSG code',
     'doc': 'EPSG code',
     'type': 'string',
     'default': 'EPSG:4326'},
    'stac_items': {'label': 'Sentinel-2 STAC items',
     'doc': 'list of Sentinel-2 COG STAC items',
     'type': 'string[]'},
    'bands': {'label': 'bands used for the NDWI',
     'doc': 'bands used for the NDWI',
     'type': 'string[]',
     'default': ['green', 'nir']}},
   'outputs': [{'id': 'stac_catalog',
     'outputSource': ['node_stac/stac_catalog'],
     'type': 'Directory'}],
   'steps': {'node_water_bodies': {'run': '#detect_water_body',
     'in': {'item': 'stac_items',
      'aoi': 'aoi',
      'epsg': 'epsg',
      'bands': 'bands'},
     'out': ['detected_water_body'],
     'scatter': 'item',
     'scatterMethod': 'dotproduct'},
    'node_stac': {'run': '#stac',
     'in': {'item': 'stac_items',
      'rasters': {'source': 'node_water_bodies/detected_water_body'}},
     'out': ['stac_catalog']}}},
  {'class': 'Workflow',
   'id': 'detect_water_body',
   'label': 'Water body detection based on NDWI and otsu threshold',
   'doc': 'Water body detection based on NDWI and otsu threshold',
   'requirements': [{'class': 'ScatterFeatureRequirement'}],
   'inputs': {'aoi': {'doc': 'area of interest as a bounding box',
     'type': 'string'},
    'epsg': {'doc': 'EPSG code', 'type': 'string', 'default': 'EPSG:4326'},
    'bands': {'doc': 'bands used for the NDWI', 'type': 'string[]'},
    'item': {'doc': 'STAC item', 'type': 'string'}},
   'outputs': [{'id': 'detected_water_body',
     'outputSource': ['node_otsu/binary_mask_item'],
     'type': 'File'}],
   'steps': {'node_crop': {'run': '#crop',
     'in': {'item': 'item', 'aoi': 'aoi', 'epsg': 'epsg', 'band': 'bands'},
     'out': ['cropped'],
     'scatter': 'band',
     'scatterMethod': 'dotproduct'},
    'node_normalized_difference': {'run': '#norm_diff',
     'in': {'rasters': {'source': 'node_crop/cropped'}},
     'out': ['ndwi']},
    'node_otsu': {'run': '#otsu',
     'in': {'raster': {'source': 'node_normalized_difference/ndwi'}},
     'out': ['binary_mask_item']}}},
  {'class': 'CommandLineTool',
   'id': 'crop',
   'requirements': {'InlineJavascriptRequirement': {},
    'EnvVarRequirement': {'envDef': {'PATH': '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin',
      'PYTHONPATH': '/app'}},
    'ResourceRequirement': {'coresMax': 1, 'ramMax': 512}},
   'hints': {'DockerRequirement': {'dockerPull': 'ghcr.io/parham-membari-terradue/mastering-app-package/crop@sha256:0fc019633a1968a611a07f335ddcc9a478f6971c72757a57e060423fae57473e'}},
   'baseCommand': ['python', '-m', 'app'],
   'arguments': [],
   'inputs': {'item': {'type': 'string',
     'inputBinding': {'prefix': '--input-item'}},
    'aoi': {'type': 'string', 'inputBinding': {'prefix': '--aoi'}},
    'epsg': {'type': 'string', 'inputBinding': {'prefix': '--epsg'}},
    'band': {'type': 'string', 'inputBinding': {'prefix': '--band'}}},
   'outputs': {'cropped': {'outputBinding': {'glob': '*.tif'},
     'type': 'File'}}},
  {'class': 'CommandLineTool',
   'id': 'norm_diff',
   'requirements': {'InlineJavascriptRequirement': {},
    'EnvVarRequirement': {'envDef': {'PATH': '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin',
      'PYTHONPATH': '/app'}},
    'ResourceRequirement': {'coresMax': 1, 'ramMax': 512}},
   'hints': {'DockerRequirement': {'dockerPull': 'ghcr.io/parham-membari-terradue/mastering-app-package/norm_diff@sha256:e46491095215833722db2c3d3c24feae381c1008981ae3508973b6aa1f5a880a'}},
   'baseCommand': ['python', '-m', 'app'],
   'arguments': [],
   'inputs': {'rasters': {'type': 'File[]', 'inputBinding': {'position': 1}}},
   'outputs': {'ndwi': {'outputBinding': {'glob': '*.tif'}, 'type': 'File'}}},
  {'class': 'CommandLineTool',
   'id': 'otsu',
   'requirements': {'InlineJavascriptRequirement': {},
    'EnvVarRequirement': {'envDef': {'PATH': '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin',
      'PYTHONPATH': '/app'}},
    'ResourceRequirement': {'coresMax': 1, 'ramMax': 512}},
   'hints': {'DockerRequirement': {'dockerPull': 'ghcr.io/parham-membari-terradue/mastering-app-package/otsu@sha256:f026bcac96c9bf1ce486855ea6e8fd27ccc501e82dbc6837021c32d9708d097a'}},
   'baseCommand': ['python', '-m', 'app'],
   'arguments': [],
   'inputs': {'raster': {'type': 'File', 'inputBinding': {'position': 1}}},
   'outputs': {'binary_mask_item': {'outputBinding': {'glob': '*.tif'},
     'type': 'File'}}},
  {'class': 'CommandLineTool',
   'id': 'stac',
   'requirements': {'InlineJavascriptRequirement': {},
    'EnvVarRequirement': {'envDef': {'PATH': '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin',
      'PYTHONPATH': '/app'}},
    'ResourceRequirement': {'coresMax': 1, 'ramMax': 512}},
   'hints': {'DockerRequirement': {'dockerPull': 'ghcr.io/parham-membari-terradue/mastering-app-package/stac@sha256:c687a9f18ebb9352a150bb7ac2d687ec9e08e2f4ef2519da342012895dbb693e'}},
   'baseCommand': ['python', '-m', 'app'],
   'arguments': [],
   'inputs': {'item': {'type': {'type': 'array',
      'items': 'string',
      'inputBinding': {'prefix': '--input-item'}}},
    'rasters': {'type': {'type': 'array',
      'items': 'File',
      'inputBinding': {'prefix': '--water-body'}}}},
   'outputs': {'stac_catalog': {'outputBinding': {'glob': '.'},
     'type': 'Directory'}}}],
 's:codeRepository': {'URL': 'https://github.com/parham-membari-terradue/mastering-app-package.git'},
 's:author': [{'class': 's:Person',
   's.name': 'Jane Doe',
   's.email': 'jane.doe@acme.earth',
   's.affiliation': 'ACME'}]}
def app_package_deployment_cwl_encoding(app_package_entry_point, app_package_content):  

    headers = {"accept": "application/json", 
            "Content-Type": "application/cwl+yaml"}

    response = requests.post(
        f"{ogc_api_endpoint}/processes?w={app_package_entry_point}",
        headers=headers,
        json=app_package_content
    )
    print(response.status_code)
    return response
app_package_entry_point = "water-bodies"
is_package_deployed = check_app_package_deployment(app_package_entry_point)
if not is_package_deployed:
    response = app_package_deployment_cwl_encoding(app_package_entry_point, app_package_content)
    pprint(response.json())
2025-08-20 13:40:49.695 | WARNING  | __main__:check_app_package_deployment:29 - water-bodies still not deployed.


201
{'description': 'Water bodies detection based on NDWI and otsu threshold '
                'applied to Sentinel-2 COG STAC items',
 'id': 'water-bodies',
 'jobControlOptions': ['async-execute', 'dismiss'],
 'links': [{'href': 'http://localhost:8080/acme/ogc-api/processes/water-bodies/execution',
            'rel': 'http://www.opengis.net/def/rel/ogc/1.0/execute',
            'title': 'Execute End Point',
            'type': 'application/json'}],
 'metadata': [{'role': 'https://schema.org/softwareVersion', 'value': '1.1.1'},
              {'role': 'https://schema.org/author',
               'value': {'@context': 'https://schema.org',
                         '@type': 'Person',
                         's.affiliation': 'ACME',
                         's.email': 'jane.doe@acme.earth',
                         's.name': 'Jane Doe'}}],
 'mutable': True,
 'outputTransmission': ['value', 'reference'],
 'title': 'Water bodies detection based on NDWI and otsu threshold',
 'version': '1.1.1'}
dict(response.headers)
{'Date': 'Wed, 20 Aug 2025 13:40:49 GMT',
 'Server': 'Apache/2.4.41 (Ubuntu)',
 'X-Powered-By': 'ZOO-Project-DRU',
 'X-Also-Powered-By': 'jwt.securityIn',
 'X-Also-Also-Powered-By': 'dru.securityIn',
 'Location': 'http://localhost:8080/acme/ogc-api/processes/water-bodies',
 'Keep-Alive': 'timeout=5, max=100',
 'Connection': 'Keep-Alive',
 'Transfer-Encoding': 'chunked',
 'Content-Type': 'application/json;charset=UTF-8'}

Next Steps

You can either: - Return to the list of available processes to verify the newly deployed process. - Proceed to the next step to review the process description in detail