diff --git a/.circleci/config.yml b/.circleci/config.yml index 1f64348b..c04f2301 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -17,7 +17,7 @@ references: jobs: build_and_test: docker: - - image: circleci/node:8.11 + - image: circleci/node:12.0 steps: - *restore_repo - checkout diff --git a/.gitignore b/.gitignore index 9f671835..58e84a09 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,6 @@ yarn.lock lerna-debug.log *.lerna_backup _book +.idea/ +.DS_STORE +.serverless/ diff --git a/.nvmrc b/.nvmrc index 22ec1e64..87e3933a 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -8.10 +12.13 diff --git a/Dockerfile b/Dockerfile index c83e2437..50af9cf5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ # - ES_HOST: Elasticsearch https endpoint -FROM node:8 +FROM node:12 ENV \ HOME=/home/sat-utils diff --git a/package.json b/package.json index 154308bb..3b87d494 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "test": "lerna run test", "update": "lerna publish --skip-git --skip-npm", "eslint": "eslint packages/* --ext .js", + "deploy": "sls deploy", "build-api-docs": "yarn widdershins --search false --language_tabs 'nodejs:NodeJS' 'python:Python' --summary ./packages/api-lib/api-spec.yaml -o ./docs/api.md & yarn shins --inline --logo ./docs/images/logo.png -o ./docs/index.html ./docs/api.md" }, "devDependencies": { @@ -21,7 +22,11 @@ "eslint-plugin-jsx-a11y": "^6.0.3", "eslint-plugin-react": "^7.7.0", "lerna": "^2.11.0", + "serverless": "^1.53.0", + "serverless-pseudo-parameters": "^2.5.0", + "serverless-step-functions": "^2.11.0", "shins": "^2.3.2-3", "widdershins": "^3.6.6" - } + }, + "name": "sat-api" } diff --git a/packages/api-lib/api-spec.yaml b/packages/api-lib/api-spec.yaml deleted file mode 100644 index 1e05f386..00000000 --- a/packages/api-lib/api-spec.yaml +++ /dev/null @@ -1,940 +0,0 @@ -openapi: 3.0.1 -info: - title: The SAT-API - version: 0.2.0 - description: >- - Sat-api is a STAC compliant web API for searching and serving metadata for - geospatial data (including but not limited to satellite imagery). - Development Seed runs an instance of sat-api for the Landsat-8 - and Sentinel-2 imagery that is hosted on AWS. - contact: - name: Development Seed - email: info@developmentseed.org - url: 'https://developmentseed.org/contacts/' - license: - name: MIT License - url: 'https://github.com/sat-utils/sat-api/blob/master/LICENSE' -servers: - - url: 'https://sat-api.developmentseed.org/' - description: Production server - - url: 'https://sat-api-dev.developmentseed.org/' - description: Development server -paths: - /stac: - get: - summary: Return the root catalog or collection. - description: >- - Returns the root STAC Catalog or STAC Collection that is the entry point - for users to browse with STAC Browser or for search engines to crawl. - This can either return a single STAC Collection or more commonly a STAC - catalog that usually lists sub-catalogs of STAC Collections, i.e. a - simple catalog that lists all collections available through the API. - tags: - - STAC - responses: - '200': - description: A catalog json definition. Used as an entry point for a crawler. - content: - application/json: - schema: - $ref: '#/components/schemas/catalogDefinition' - - /stac/search: - get: - summary: Search STAC items by simple filtering. - description: >- - Retrieve Items matching filters. Intended as a shorthand API for simple - queries. - operationId: getSearchSTAC - tags: - - STAC - parameters: - - $ref: '#/components/parameters/bbox' - - $ref: '#/components/parameters/time' - - $ref: '#/components/parameters/limit' - - $ref: '#/components/parameters/query' - - $ref: '#/components/parameters/sort' - - $ref: '#/components/parameters/fields' - responses: - '200': - description: A feature collection. - content: - application/geo+json: - schema: - $ref: '#/components/schemas/itemCollection' - default: - description: An error occurred. - content: - application/json: - schema: - $ref: '#/components/schemas/exception' - - post: - summary: Search STAC items by full-featured filtering. - description: >- - retrieve items matching filters. Intended as the standard, full-featured - query API. This method is mandatory. - operationId: postSearchSTAC - tags: - - STAC - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/searchBody' - responses: - '200': - description: A feature collection. - content: - application/geo+json: - schema: - $ref: '#/components/schemas/itemCollection' - text/html: - schema: - type: string - default: - description: An error occurred. - content: - application/json: - schema: - $ref: '#/components/schemas/exception' - text/html: - schema: - type: string - /: - get: - summary: landing page of this API - description: >- - The landing page provides links to the API definition, the Conformance - statements and the metadata about the feature data in this dataset. - operationId: getLandingPage - tags: - - Capabilities - responses: - '200': - description: links to the API capabilities - content: - application/json: - schema: - $ref: '#/components/schemas/root' - text/html: - schema: - type: string - - /collections: - get: - summary: describe the feature collections in the dataset - operationId: describeCollections - tags: - - Capabilities - responses: - '200': - description: Metdata about the feature collections shared by this API. - content: - application/json: - schema: - $ref: '#/components/schemas/content' - - '/collections/{collectionId}': - get: - summary: 'describe the {collectionId} feature collection' - operationId: describeCollection - tags: - - Capabilities - parameters: - - $ref: '#/components/parameters/collectionId' - responses: - '200': - description: 'Metadata about the {collectionId} collection shared by this API.' - content: - application/json: - schema: - $ref: '#/components/schemas/collectionInfo' - default: - description: An error occurred. - content: - application/json: - schema: - $ref: '#/components/schemas/exception' - - '/collections/{collectionId}/items': - get: - summary: 'retrieve features of feature collection {collectionId}' - description: >- - Every feature in a dataset belongs to a collection. A dataset may - consist of multiple feature collections. A feature collection is often a - collection of features of a similar type, based on a common schema.\ - - Use content negotiation to request HTML or GeoJSON. - operationId: getFeatures - tags: - - Features - parameters: - - $ref: '#/components/parameters/collectionId' - - $ref: '#/components/parameters/limit' - - $ref: '#/components/parameters/bbox' - - $ref: '#/components/parameters/time' - - $ref: '#/components/parameters/query' - - $ref: '#/components/parameters/sort' - responses: - '200': - description: >- - Information about the feature collection plus the first features - matching the selection parameters. - content: - application/geo+json: - schema: - $ref: '#/components/schemas/itemCollection' - default: - description: An error occurred. - content: - application/json: - schema: - $ref: '#/components/schemas/exception' - - '/collections/{collectionId}/items/{featureId}': - get: - summary: retrieve a feature; use content negotiation to request HTML or GeoJSON - operationId: getFeature - tags: - - Features - parameters: - - $ref: '#/components/parameters/collectionId' - - $ref: '#/components/parameters/featureId' - responses: - '200': - description: A feature. - content: - application/geo+json: - schema: - $ref: '#/components/schemas/item' - default: - description: An error occurred. - content: - application/json: - schema: - $ref: '#/components/schemas/exception' - -components: - parameters: - limit: - name: limit - in: query - description: | - The optional limit parameter limits the number of items that are - presented in the response document. - - Only items are counted that are on the first level of the collection in - the response document. Nested objects contained within the explicitly - requested items shall not be counted. - - * Minimum = 1 - * Maximum = 10000 - * Default = 10 - required: false - schema: - type: integer - minimum: 1 - maximum: 10000 - default: 10 - style: form - explode: false - bbox: - name: bbox - in: query - description: | - Only features that have a geometry that intersects the bounding box are - selected. The bounding box is provided as four or six numbers, - depending on whether the coordinate reference system includes a - vertical axis (elevation or depth): - - * Lower left corner, coordinate axis 1 - * Lower left corner, coordinate axis 2 - * Lower left corner, coordinate axis 3 (optional) - * Upper right corner, coordinate axis 1 - * Upper right corner, coordinate axis 2 - * Upper right corner, coordinate axis 3 (optional) - - The coordinate reference system of the values is WGS84 - longitude/latitude (http://www.opengis.net/def/crs/OGC/1.3/CRS84) unless - a different coordinate reference system is specified in the parameter - `bbox-crs`. - - For WGS84 longitude/latitude the values are in most cases the sequence - of minimum longitude, minimum latitude, maximum longitude and maximum - latitude. However, in cases where the box spans the antimeridian the - first value (west-most box edge) is larger than the third value - (east-most box edge). - - - If a feature has multiple spatial geometry properties, it is the - decision of the server whether only a single spatial geometry property - is used to determine the extent or all relevant geometries. - required: false - schema: - type: array - minItems: 4 - maxItems: 6 - items: - type: number - style: form - explode: false - time: - name: time - in: query - description: > - Either a date-time or a period string that adheres to RFC3339. Examples: - - * A date-time: "2018-02-12T23:20:50Z" - * A period: "2018-02-12T00:00:00Z/2018-03-18T12:31:12Z" or "2018-02-12T00:00:00Z/P1M6DT12H31M12S" - - Only features that have a temporal property that intersects the value - of `time` are selected. If a feature has multiple temporal properties, it - is the decision of the server whether only a single temporal property is - used to determine the extent or all relevant temporal properties. - required: false - schema: - type: string - style: form - explode: false - collectionId: - name: collectionId - in: path - required: true - description: Identifier (name) of a specific collection - schema: - type: string - featureId: - name: featureId - in: path - description: Local identifier of a specific feature - required: true - schema: - type: string - query: - name: query - in: query - description: >- - query for properties in items. Use the JSON form of the queryFilter used - in POST. - required: false - schema: - type: string - sort: - name: sort - in: query - description: Allows sorting results by the specified properties - required: false - schema: - $ref: '#/components/schemas/sort' - fields: - name: fields - in: query - description: Determines the shape of the features in the response - required: false - schema: - $ref: '#/components/schemas/fields' - style: form - explode: false - schemas: - exception: - type: object - required: - - code - properties: - code: - type: string - description: - type: string - links: - type: array - items: - $ref: '#/components/schemas/link' - link: - type: object - required: - - href - - rel - additionalProperties: true - properties: - href: - type: string - format: url - example: 'http://www.geoserver.example/stac/naip/child/catalog.json' - rel: - type: string - example: child - type: - type: string - example: application/json - title: - type: string - example: NAIP Child Catalog - searchBody: - description: The search criteria - type: object - allOf: - - $ref: '#/components/schemas/bboxFilter' - - $ref: '#/components/schemas/timeFilter' - - $ref: '#/components/schemas/intersectsFilter' - - $ref: '#/components/schemas/queryFilter' - - $ref: '#/components/schemas/sortFilter' - bbox: - description: | - Only features that have a geometry that intersects the bounding box are - selected. The bounding box is provided as four or six numbers, - depending on whether the coordinate reference system includes a - vertical axis (elevation or depth): - - * Lower left corner, coordinate axis 1 - * Lower left corner, coordinate axis 2 - * Lower left corner, coordinate axis 3 (optional) - * Upper right corner, coordinate axis 1 - * Upper right corner, coordinate axis 2 - * Upper right corner, coordinate axis 3 (optional) - - The coordinate reference system of the values is WGS84 - longitude/latitude (http://www.opengis.net/def/crs/OGC/1.3/CRS84) unless - a different coordinate reference system is specified in the parameter - `bbox-crs`. - - For WGS84 longitude/latitude the values are in most cases the sequence - of minimum longitude, minimum latitude, maximum longitude and maximum - latitude. However, in cases where the box spans the antimeridian the - first value (west-most box edge) is larger than the third value - (east-most box edge). - - - If a feature has multiple spatial geometry properties, it is the - decision of the server whether only a single spatial geometry property - is used to determine the extent or all relevant geometries. - type: array - minItems: 4 - maxItems: 6 - items: - type: number - example: - - -110 - - 39.5 - - -105 - - 40.5 - bboxFilter: - type: object - description: Only return items that intersect the provided bounding box. - properties: - bbox: - $ref: '#/components/schemas/bbox' - timeFilter: - description: An object representing a time based filter. - type: object - properties: - time: - $ref: '#/components/schemas/time' - intersectsFilter: - type: object - description: Only returns items that intersect with the provided polygon. - properties: - intersects: - $ref: 'http://geojson.org/schema/Geometry.json' - time: - type: string - description: > - Either a date-time or a period string that adheres to RFC 3339. - Examples: - - * A date-time: "2018-02-12T23:20:50Z" - * A period: "2018-02-12T00:00:00Z/2018-03-18T12:31:12Z" or "2018-02-12T00:00:00Z/P1M6DT12H31M12S" - - Only features that have a temporal property that intersects the value of - `time` are selected. - - If a feature has multiple temporal properties, it is the decision of the - server whether only a single temporal property is used to determine the - extent or all relevant temporal properties. - example: '2018-02-12T00:00:00Z/2018-03-18T12:31:12Z' - catalogDefinition: - type: object - required: - - stac_version - - id - - description - - links - additionalProperties: true - properties: - stac_version: - type: string - example: 0.6.0 - id: - type: string - example: naip - title: - type: string - example: NAIP Imagery - description: - type: string - example: Catalog of NAIP Imagery. - links: - $ref: '#/components/schemas/links' - itemCollection: - type: object - required: - - features - - type - properties: - type: - type: string - enum: - - FeatureCollection - features: - type: array - items: - $ref: '#/components/schemas/item' - links: - $ref: '#/components/schemas/itemCollectionLinks' - item: - type: object - required: - - id - - type - - geometry - - bbox - - links - - properties - - assets - properties: - id: - $ref: '#/components/schemas/itemId' - bbox: - $ref: '#/components/schemas/bbox' - geometry: - $ref: 'http://geojson.org/schema/Geometry.json' - type: - $ref: '#/components/schemas/itemType' - properties: - $ref: '#/components/schemas/itemProperties' - links: - $ref: '#/components/schemas/links' - assets: - $ref: '#/components/schemas/itemAssets' - example: - type: Feature - id: CS3-20160503_132130_04 - bbox: - - -122.59750209 - - 37.48803556 - - -122.2880486 - - 37.613537207 - geometry: - type: Polygon - coordinates: - - - - -122.308150179 - - 37.488035566 - - - -122.597502109 - - 37.538869539 - - - -122.576687533 - - 37.613537207 - - - -122.2880486 - - 37.562818007 - - - -122.308150179 - - 37.488035566 - properties: - datetime: '2016-05-03T13:21:30.040Z' - links: - - rel: self - href: >- - http://https://sat-api.developmentseed.org/collections/landsat-8-l1/items/LC80100102015050LGN00.json - assets: - analytic: - title: 4-Band Analytic - href: >- - http://cool-sat.com/LC80100102015050LGN00/band4.tiff - type: image/tiff - thumbnail: - title: Thumbnail - href: >- - http://cool-sat.com/LC80100102015050LGN00/thumb.png - type: image/png - itemId: - type: string - example: path/to/example.tif - description: 'Provider identifier, a unique ID, potentially a link to a file.' - itemType: - type: string - description: The GeoJSON type - enum: - - Feature - itemAssets: - type: object - additionalProperties: - type: object - required: - - href - properties: - href: - type: string - format: url - description: Link to the asset object - example: >- - http://cool-sat.com/LC80100102015050LGN00/thumb.png - title: - type: string - description: Displayed title - example: Thumbnail - type: - type: string - description: Media type of the asset - example: image/png - itemProperties: - type: object - required: - - datetime - description: provides the core metatdata fields plus extensions - properties: - datetime: - $ref: '#/components/schemas/time' - additionalProperties: - description: Any additional properties added in via extensions. - itemCollectionLinks: - type: array - description: >- - An array of links. Can be used for pagination, e.g. by providing a link - with the `next` relation type. - items: - $ref: '#/components/schemas/link' - example: - - rel: next - href: >- - http://sat-api.developmentseed.org/collections/landsat-8-l1/items/gasd312fsaeg - root: - type: object - required: - - links - properties: - links: - type: array - items: - $ref: '#/components/schemas/link' - example: - - href: 'http://sat-api.developmentseed.org' - rel: self - type: application/json - title: this document - - href: 'http://sat-api.developmentseed.org/api' - rel: service - type: application/json - title: this document - - href: 'http://sat-api.developmentseed.org/collections' - rel: data - type: application/json - title: Metadata about the feature collections - req-classes: - type: object - required: - - conformsTo - properties: - conformsTo: - type: array - items: - type: string - example: - - 'http://www.opengis.net/spec/wfs-1/3.0/req/core' - - 'http://www.opengis.net/spec/wfs-1/3.0/req/oas30' - - 'http://www.opengis.net/spec/wfs-1/3.0/req/html' - - 'http://www.opengis.net/spec/wfs-1/3.0/req/geojson' - content: - type: object - required: - - links - - collections - properties: - links: - type: array - items: - $ref: '#/components/schemas/link' - example: - - href: 'http://data.example.org/collections.json' - rel: self - type: application/json - title: this document - - href: 'http://data.example.org/collections.html' - rel: alternate - type: text/html - title: this document as HTML - - href: 'http://schemas.example.org/1.0/foobar.xsd' - rel: describedBy - type: application/xml - title: XML schema for Acme Corporation data - collections: - type: array - items: - $ref: '#/components/schemas/collectionInfo' - collectionInfo: - type: object - required: - - name - - links - - stac_version - - id - - description - - license - - extent - properties: - name: - description: 'identifier of the collection used, for example, in URIs' - type: string - example: buildings - title: - description: human readable title of the collection - type: string - example: Buildings - description: - description: a description of the features in the collection - type: string - example: Buildings in the city of Bonn. - links: - type: array - items: - $ref: '#/components/schemas/link' - example: - - href: 'http://data.example.org/collections/buildings/items' - rel: item - type: application/geo+json - title: Buildings - - href: 'http://example.org/concepts/building.html' - rel: describedBy - type: text/html - title: Feature catalogue for buildings - extent: - $ref: '#/components/schemas/extent' - crs: - description: >- - The coordinate reference systems in which geometries may be - retrieved. Coordinate reference systems are identified by a URI. The - first coordinate reference system is the coordinate reference system - that is used by default. This is always - "http://www.opengis.net/def/crs/OGC/1.3/CRS84", i.e. WGS84 - longitude/latitude. - type: array - items: - type: string - default: - - 'http://www.opengis.net/def/crs/OGC/1.3/CRS84' - stac_version: - type: string - example: 0.6.0 - id: - description: 'identifier of the collection used, for example, in URIs' - type: string - example: buildings - keywords: - title: Keywords - type: array - items: - type: string - example: - - buildings - - properties - - constructions - version: - title: Collection Version - type: string - example: 1 - license: - title: Collection License Name - type: string - example: Apache-2.0 - providers: - type: array - items: - properties: - name: - title: Organization name - type: string - example: Big Building Corp - description: - title: Provider description - type: string - example: No further processing applied. - roles: - title: Organization roles - type: array - items: - type: string - enum: - - producer - - licensor - - processor - - host - example: - - producer - - licensor - url: - title: Homepage - description: >- - Homepage on which the provider describes the dataset and - publishes contact information. - type: string - format: url - example: 'http://www.big-building.com' - queryFilter: - type: object - description: Allows users to query properties for specific values - properties: - query: - $ref: '#/components/schemas/query' - query: - type: object - description: Define which properties to query and the operatations to apply - additionalProperties: - $ref: '#/components/schemas/queryProp' - example: - 'eo:cloud_cover': - lt: 50 - queryProp: - description: Apply query operations to a specific property - anyOf: - - description: >- - if the object doesn't contain any of the operators, it is equivalent - to using the equals operator - - type: object - description: Match using an operator - properties: - eq: - description: >- - Find items with a property that is equal to the specified value. - For strings, a case-insensitive comparison must be performed. - gt: - type: number - description: >- - Find items with a property value greater than the specified - value. - lt: - type: number - description: Find items with a property value less than the specified value. - gte: - type: number - description: >- - Find items with a property value greater than or equal the - specified value. - lte: - type: number - description: >- - Find items with a property value greater than or equal the - specified value. - sortFilter: - type: object - description: Sort the results - properties: - sort: - $ref: '#/components/schemas/sort' - sort: - type: array - description: | - An array of objects containing a property name and sort direction. - minItems: 1 - items: - type: object - required: - - field - properties: - field: - type: string - direction: - type: string - default: asc - enum: - - asc - - desc - example: - - field: 'eo:cloud_cover' - direction: desc - extent: - type: object - properties: - crs: - description: >- - Coordinate reference system of the coordinates in the spatial extent - (property `spatial`). In the Core, only WGS84 longitude/latitude is - supported. Extensions may support additional coordinate reference - systems. - type: string - enum: - - 'http://www.opengis.net/def/crs/OGC/1.3/CRS84' - default: 'http://www.opengis.net/def/crs/OGC/1.3/CRS84' - spatial: - description: >- - West, south, east, north edges of the spatial extent. The minimum - and maximum values apply to the coordinate reference system WGS84 - longitude/latitude that is supported in the Core. If, for example, a - projected coordinate reference system is used, the minimum and - maximum values need to be adjusted. - type: array - minItems: 4 - maxItems: 6 - items: - type: number - example: - - -180 - - -90 - - 180 - - 90 - trs: - description: >- - Temporal reference system of the coordinates in the temporal extent - (property `temporal`). In the Core, only the Gregorian calendar is - supported. Extensions may support additional temporal reference - systems. - type: string - enum: - - 'http://www.opengis.net/def/uom/ISO-8601/0/Gregorian' - default: 'http://www.opengis.net/def/uom/ISO-8601/0/Gregorian' - temporal: - description: Begin and end times of the temporal extent. - type: array - minItems: 2 - maxItems: 2 - items: - type: string - format: dateTime - example: - - '2011-11-11T12:22:11Z' - - '2012-11-24T12:32:43Z' - fieldsFilter: - type: object - description: Determines the shape of the features in the response - properties: - fields: - $ref: '#/components/schemas/fields' - fields: - description: > - The geometry member determines whether the geometry is populated or is - null. The - - include and exclude members specify an array of property names that are - either - - included or excluded from the result, respectively. If both include and - exclude - - are specified, include takes precedence. - - id and links are required feature properties and cannot be excluded. - type: object - properties: - geometry: - type: boolean - include: - type: array - items: - type: string - example: - - 'eo:cloud_cover' - exclude: - type: array - items: - type: string - example: - - 'eo:sun_azimuth' -tags: - - name: STAC - description: Extension to WFS3 Core to support STAC metadata model and search API diff --git a/packages/api-lib/libs/ElasticSearchWriteableStream.js b/packages/api-lib/libs/ElasticSearchWriteableStream.js index eaf2607c..e04e106a 100644 --- a/packages/api-lib/libs/ElasticSearchWriteableStream.js +++ b/packages/api-lib/libs/ElasticSearchWriteableStream.js @@ -1,5 +1,5 @@ const stream = require('stream') -const logger = require('./logger') +const logger = console //require('./logger') class ElasticSearchWritableStream extends stream.Writable { constructor(config, options) { diff --git a/packages/api-lib/libs/api.js b/packages/api-lib/libs/api.js index 5c08a5c5..f04992cc 100644 --- a/packages/api-lib/libs/api.js +++ b/packages/api-lib/libs/api.js @@ -1,9 +1,8 @@ const gjv = require('geojson-validation') const extent = require('@mapbox/extent') -const logger = require('./logger') -const fs = require('fs') const yaml = require('js-yaml') - +const fs = require('fs') +const logger = console //require('./logger') // max number of collections to retrieve const COLLECTION_LIMIT = process.env.SATAPI_COLLECTION_LIMIT || 100 @@ -241,18 +240,6 @@ const collectionsToCatalogLinks = function (results, endpoint) { href: `${endpoint}/collections/${id}` } }) - catalog.links.push({ - rel: 'self', - href: `${endpoint}/stac` - }) - catalog.links.push({ - rel: 'search', - href: `${endpoint}/stac/search` - }) - catalog.links.push({ - rel: 'service', - href: process.env.STAC_DOCS_URL - }) return catalog } @@ -338,18 +325,27 @@ const searchItems = async function (collectionId, queryParameters, backend, endp } -const getRoot = async function (endpoint = '') { - const stac_version = process.env.STAC_VERSION - const stac_id = process.env.STAC_ID - const stac_title = process.env.STAC_TITLE - const stac_description = process.env.STAC_DESCRIPTION - const catalog = { - stac_version: stac_version, - id: stac_id, - title: stac_title, - description: stac_description, - links: [] +const getAPI = async function () { + const spec = yaml.safeLoad(fs.readFileSync('./api.yaml', 'utf8')) + return spec +} + + +const getConformance = async function () { + const conformance = { + conformsTo: [ + 'http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/core', + 'http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/html', + 'http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/geojson' + ] } + return conformance +} + + +const getCatalog = async function (backend, endpoint = '') { + const { results } = await backend.search({}, 'collections', 1, COLLECTION_LIMIT) + const catalog = collectionsToCatalogLinks(results, endpoint) catalog.links.push({ rel: 'service-desc', type: 'application/vnd.oai.openapi+json;version=3.0', @@ -361,7 +357,7 @@ const getRoot = async function (endpoint = '') { href: `${endpoint}/conformance` }) catalog.links.push({ - rel: 'data', + rel: 'children', type: 'application/json', href: `${endpoint}/collections` }) @@ -370,30 +366,18 @@ const getRoot = async function (endpoint = '') { type: 'application/json', href: `${endpoint}/` }) - return catalog -} - - -const getAPI = async function () { - return yaml.safeLoad(fs.readFileSync('../api-definition.yaml', 'utf8')) -} - - -const getConformance = async function () { - const conformance = { - conformsTo: [ - 'http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/core', - 'http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/html', - 'http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/geojson' - ] + catalog.links.push({ + rel: 'search', + type: 'application/json', + href: `${endpoint}/stac/search` + }) + if (process.env.STAC_DOCS_URL) { + catalog.links.push({ + rel: 'docs', + href: process.env.STAC_DOCS_URL + }) } - return conformance -} - - -const getCatalog = async function (backend, endpoint = '') { - const { results } = await backend.search({}, 'collections', 1, COLLECTION_LIMIT) - return collectionsToCatalogLinks(results, endpoint) + return catalog } @@ -461,7 +445,7 @@ const API = async function ( // API Root if (root) { - apiResponse = await getRoot(endpoint) + apiResponse = await getCatalog(backend, endpoint) } // API Definition if (api) { @@ -505,7 +489,6 @@ const API = async function ( } module.exports = { - getRoot, getAPI, getConformance, getCatalog, diff --git a/packages/api-lib/libs/es.js b/packages/api-lib/libs/es.js index 2f0b99ab..a90d95ff 100644 --- a/packages/api-lib/libs/es.js +++ b/packages/api-lib/libs/es.js @@ -5,7 +5,7 @@ const httpAwsEs = require('http-aws-es') const elasticsearch = require('elasticsearch') const through2 = require('through2') const ElasticsearchWritableStream = require('./ElasticSearchWriteableStream') -const logger = require('./logger') +const logger = console //require('./logger') let _esClient /* @@ -14,14 +14,15 @@ searching records, and managing the indexes. It looks for the ES_HOST environmen variable which is the URL to the elasticsearch host */ -// Connect to an Elasticsearch cluster +// Connect to an Elasticsearch instance async function connect() { let esConfig - let client // use local client if (!process.env.ES_HOST) { - client = new elasticsearch.Client({ host: 'localhost:9200' }) + esConfig = { + host: 'localhost:9200' + } } else { await new Promise((resolve, reject) => AWS.config.getCredentials((err) => { if (err) return reject(err) @@ -42,13 +43,15 @@ async function connect() { // Note that this doesn't abort the query. requestTimeout: 120000 // milliseconds } - client = new elasticsearch.Client(esConfig) } + logger.debug(`Elasticsearch config: ${JSON.stringify(esConfig)}`) + const client = new elasticsearch.Client(esConfig) + await new Promise((resolve, reject) => client.ping({ requestTimeout: 1000 }, (err) => { if (err) { - reject('Unable to connect to elasticsearch') + reject(`Unable to connect to elasticsearch: ${err}`) } else { resolve() } diff --git a/packages/api-lib/libs/ingest.js b/packages/api-lib/libs/ingest.js index 8a05396f..95cbeb6f 100644 --- a/packages/api-lib/libs/ingest.js +++ b/packages/api-lib/libs/ingest.js @@ -7,8 +7,7 @@ const isUrl = require('is-url') const MemoryStream = require('memorystream') const { Readable } = require('readable-stream') const pump = require('pump') -const uuid = require('uuid/v4') -const logger = require('./logger') +const logger = console //require('./logger') const limiter = new Bottleneck({ maxConcurrent: 500 @@ -150,8 +149,7 @@ async function ingest(url, backend, recursive = true, collectionsOnly = false) { await backend.prepare('collections') await backend.prepare('items') const { toEs, esStream } = await backend.stream() - const ingestJobId = uuid() - logger.info(`${ingestJobId} Started`) + logger.info(`Job Started for ${url}`) const promise = new Promise((resolve, reject) => { pump( duplexStream, @@ -162,7 +160,7 @@ async function ingest(url, backend, recursive = true, collectionsOnly = false) { logger.error(error) reject(error) } else { - logger.info(`${ingestJobId} Completed`) + logger.info(`Job completed for ${url}`) resolve(true) } } diff --git a/packages/api-lib/libs/logger.js b/packages/api-lib/libs/logger.js index af1177b9..bf344793 100644 --- a/packages/api-lib/libs/logger.js +++ b/packages/api-lib/libs/logger.js @@ -1,10 +1,20 @@ const winston = require('winston') -const logger = new (winston.Logger)({ - level: process.env.LOG_LEVEL || 'info', + +const logger = winston.createLogger({ + level: process.env.LOG_LEVEL || 'debug', + format: winston.format.combine( + winston.format.timestamp(), + winston.format.colorize(), + winston.format.printf((info) => `[${info.timestamp}] ${info.level}: ${info.message}`) + ), transports: [ - new (winston.transports.Console)({ timestamp: true }) + new winston.transports.Console({ + format: winston.format.simple(), + level: process.env.LOG_LEVEL || 'debug' + }) ] }) + module.exports = logger diff --git a/packages/api-lib/package.json b/packages/api-lib/package.json index 11735cb9..e9dec8da 100644 --- a/packages/api-lib/package.json +++ b/packages/api-lib/package.json @@ -38,8 +38,7 @@ "request": "^2.88.0", "request-promise-native": "^1.0.5", "through2": "^3.0.1", - "uuid": "^3.3.2", - "winston": "^2.2.0" + "winston": "^3.2.0" }, "devDependencies": { "ava": "^0.16.0", diff --git a/packages/api-lib/api-definition.yaml b/packages/api/api.yaml similarity index 63% rename from packages/api-lib/api-definition.yaml rename to packages/api/api.yaml index ea66e6a9..4ef5f4d5 100644 --- a/packages/api-lib/api-definition.yaml +++ b/packages/api/api.yaml @@ -1,7 +1,7 @@ openapi: 3.0.1 info: title: The SpatioTemporal Asset Catalog API + Extensions - version: 0.8.0 + version: 0.8.1 license: name: Apache License 2.0 url: 'http://www.apache.org/licenses/LICENSE-2.0' @@ -10,7 +10,8 @@ info: specification. Any service that implements this endpoint to allow search of spatiotemporal assets can be considered a STAC API. The endpoint is also available as an OpenAPI fragment that can be integrated with other OpenAPI - definitions, and is designed to slot seamlessly into a WFS 3 API definition. + definitions, and is designed to slot seamlessly into a OGC API - Features + definition. contact: name: STAC Specification url: 'http://stacspec.org' @@ -20,11 +21,9 @@ tags: - name: Data description: access to data (features) - name: STAC - description: Extension to WFS3 Core to support STAC metadata model and search API - - name: Transaction Extension description: >- - STAC-specific operations to add, remove, and edit items within WFS3 - collections. + Extension to OGC API - Features to support STAC metadata model and search + API paths: /: get: @@ -46,7 +45,7 @@ paths: - Capabilities summary: information about specifications that this API conforms to description: |- - A list of all conformance classes specified in a standard that the + A list of all conformance classes specified in a standard that the server conforms to. operationId: getConformanceDeclaration responses: @@ -87,11 +86,9 @@ paths: summary: fetch features description: |- Fetch features of the feature collection with id `collectionId`. - Every feature in a dataset belongs to a collection. A dataset may consist of multiple feature collections. A feature collection is often a collection of features of a similar type, based on a common schema. - Use content negotiation to request HTML or GeoJSON. operationId: getFeatures parameters: @@ -108,54 +105,6 @@ paths: $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/ServerError' - post: - summary: add a new feature to a collection - description: create a new feature in a specific collection - operationId: postFeature - tags: - - Transaction Extension - parameters: - - $ref: '#/components/parameters/collectionId' - requestBody: - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/item' - - $ref: '#/components/schemas/itemCollection' - responses: - '201': - description: Status of the create request. - headers: - Location: - description: A link to the item - schema: - type: string - format: url - ETag: - schema: - type: string - description: A string to ensure the item has not been modified - content: - application/geo+json: - schema: - type: string - text/html: - schema: - type: string - '400': - $ref: '#/components/responses/BadRequest' - 5XX: - $ref: '#/components/responses/InternalServerError' - default: - description: An error occurred. - content: - application/json: - schema: - $ref: '#/components/schemas/exception' - text/html: - schema: - type: string '/collections/{collectionId}/items/{featureId}': get: tags: @@ -164,7 +113,6 @@ paths: description: |- Fetch the feature with id `featureId` in the feature collection with id `collectionId`. - Use content negotiation to request HTML or GeoJSON. operationId: getFeature parameters: @@ -173,137 +121,10 @@ paths: responses: '200': $ref: '#/components/responses/Feature' - headers: - ETag: - schema: - type: string - description: A string to ensure the item has not been modified '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/ServerError' - put: - summary: update an existing feature by Id with a complete item definition - description: >- - Use this method to update an existing feature. Requires the entire - GeoJSON description be submitted. - operationId: putFeature - tags: - - Transaction Extension - parameters: - - $ref: '#/components/parameters/collectionId' - - $ref: '#/components/parameters/featureId' - - $ref: '#/components/parameters/IfMatch' - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/item' - responses: - '200': - description: Status of the update request. - headers: - ETag: - schema: - type: string - description: A string to ensure the item has not been modified - content: - text/html: - schema: - type: string - application/json: - schema: - type: string - '400': - $ref: '#/components/responses/BadRequest' - '404': - $ref: '#/components/responses/NotFound' - 5XX: - $ref: '#/components/responses/InternalServerError' - default: - description: An error occurred. - content: - application/json: - schema: - $ref: '#/components/schemas/exception' - text/html: - schema: - type: string - patch: - summary: update an existing feature by Id with a partial item definition - description: >- - Use this method to update an existing feature. Requires a GeoJSON - fragement (containing the fields to be updated) be submitted. - operationId: patchFeature - tags: - - Transaction Extension - parameters: - - $ref: '#/components/parameters/collectionId' - - $ref: '#/components/parameters/featureId' - - $ref: '#/components/parameters/IfMatchOptional' - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/partialItem' - responses: - '200': - description: Status of the update request. - headers: - ETag: - schema: - type: string - description: A string to ensure the item has not been modified - content: - text/html: - schema: - type: string - application/json: - schema: - type: string - '400': - $ref: '#/components/responses/BadRequest' - '404': - $ref: '#/components/responses/NotFound' - 5XX: - $ref: '#/components/responses/InternalServerError' - default: - description: An error occurred. - content: - application/json: - schema: - $ref: '#/components/schemas/exception' - text/html: - schema: - type: string - delete: - summary: delete an existing feature by Id - description: Use this method to delete an existing feature. - operationId: deleteFeature - tags: - - Transaction Extension - parameters: - - $ref: '#/components/parameters/collectionId' - - $ref: '#/components/parameters/featureId' - - $ref: '#/components/parameters/IfMatch' - responses: - '204': - description: Status of the delete request. - '400': - $ref: '#/components/responses/BadRequest' - '404': - $ref: '#/components/responses/NotFound' - 5XX: - $ref: '#/components/responses/InternalServerError' - default: - description: An error occurred. - content: - application/json: - schema: - $ref: '#/components/schemas/exception' - text/html: - schema: - type: string /stac: get: summary: Return the root catalog or collection. @@ -327,11 +148,8 @@ paths: summary: Search STAC items with simple filtering. description: >- Retrieve Items matching filters. Intended as a shorthand API for simple - queries. - - - This method is optional, but you MUST implement `POST /stac/search` if - you want to implement this method. + queries. This method is optional, but you MUST implement `POST + /stac/search` if you want to implement this method. operationId: getSearchSTAC tags: - STAC @@ -368,13 +186,10 @@ paths: summary: Search STAC items with full-featured filtering. description: >- retrieve items matching filters. Intended as the standard, full-featured - query API. - - - This method is mandatory to implement if `GET /stac/search` is - implemented. If this endpoint is implemented on a server, it is required - to add a link with `rel` set to `search` to the `links` array in `GET - /stac` that refers to this endpoint. + query API. This method is mandatory to implement if `GET /stac/search` + is implemented. If this endpoint is implemented on a server, it is + required to add a link with `rel` set to `search` to the `links` array + in `GET /stac` that refers to this endpoint. operationId: postSearchSTAC tags: - STAC @@ -404,6 +219,38 @@ paths: type: string components: parameters: + bbox: + name: bbox + in: query + description: >- + Only features that have a geometry that intersects the bounding box are + selected. The bounding box is provided as four or six numbers, depending + on whether the coordinate reference system includes a vertical axis + (height or depth): * Lower left corner, coordinate axis 1 * Lower left + corner, coordinate axis 2 * Minimum value, coordinate axis 3 (optional) + * Upper right corner, coordinate axis 1 * Upper right corner, coordinate + axis 2 * Maximum value, coordinate axis 3 (optional) The coordinate + reference system of the values is WGS 84 longitude/latitude + (http://www.opengis.net/def/crs/OGC/1.3/CRS84) unless a different + coordinate reference system is specified in the parameter `bbox-crs`. + For WGS 84 longitude/latitude the values are in most cases the sequence + of minimum longitude, minimum latitude, maximum longitude and maximum + latitude. However, in cases where the box spans the antimeridian the + first value (west-most box edge) is larger than the third value + (east-most box edge). If the vertical axis is included, the third and + the sixth number are the bottom and the top of the 3-dimensional + bounding box. If a feature has multiple spatial geometry properties, it + is the decision of the server whether only a single spatial geometry + property is used to determine the extent or all relevant geometries. + required: false + schema: + type: array + minItems: 4 + maxItems: 6 + items: + type: number + style: form + explode: false collectionId: name: collectionId in: path @@ -411,6 +258,24 @@ components: required: true schema: type: string + datetime: + name: datetime + in: query + description: >- + Either a date-time or an interval, open or closed. Date and time + expressions adhere to RFC 3339. Open intervals are expressed using + double-dots. Examples: * A date-time: "2018-02-12T23:20:50Z" * A closed + interval: "2018-02-12T00:00:00Z/2018-03-18T12:31:12Z" * Open intervals: + "2018-02-12T00:00:00Z/.." or "../2018-03-18T12:31:12Z" Only features + that have a temporal property that intersects the value of `datetime` + are selected. If a feature has multiple temporal properties, it is the + decision of the server whether only a single temporal property is used + to determine the extent or all relevant temporal properties. + required: false + schema: + type: string + style: form + explode: false featureId: name: featureId in: path @@ -421,10 +286,18 @@ components: limit: name: limit in: query - description: The maximum number of results to return (page size). Defaults to 10 + description: >- + The optional limit parameter limits the number of items that are + presented in the response document. Only items are counted that are on + the first level of the collection in the response document. Nested + objects contained within the explicitly requested items shall not be + counted. Minimum = 1. Maximum = 10000. Default = 10. required: false schema: - $ref: '#/components/schemas/limit' + type: integer + minimum: 1 + maximum: 10000 + default: 10 style: form explode: false next: @@ -442,9 +315,8 @@ components: in: query description: > Array of Item ids to return. All other filter parameters that further - restrict the number of - - search results (except `next` and `limit`) are ignored + restrict the number of search results (except `next` and `limit`) are + ignored required: false schema: $ref: '#/components/schemas/ids' @@ -459,77 +331,6 @@ components: schema: $ref: '#/components/schemas/collectionsArray' explode: false - bbox: - name: bbox - in: query - description: | - Only features that have a geometry that intersects the bounding box are - selected. The bounding box is provided as four or six numbers, - depending on whether the coordinate reference system includes a - vertical axis (elevation or depth): - - * Lower left corner, coordinate axis 1 - * Lower left corner, coordinate axis 2 - * Lower left corner, coordinate axis 3 (optional) - * Upper right corner, coordinate axis 1 - * Upper right corner, coordinate axis 2 - * Upper right corner, coordinate axis 3 (optional) - - The coordinate reference system of the values is WGS84 - longitude/latitude (http://www.opengis.net/def/crs/OGC/1.3/CRS84) unless - a different coordinate reference system is specified in the parameter - `bbox-crs`. - - For WGS84 longitude/latitude the values are in most cases the sequence - of minimum longitude, minimum latitude, maximum longitude and maximum - latitude. However, in cases where the box spans the antimeridian the - first value (west-most box edge) is larger than the third value - (east-most box edge). - - - If a feature has multiple spatial geometry properties, it is the - decision of the server whether only a single spatial geometry property - is used to determine the extent or all relevant geometries. - required: false - schema: - $ref: '#/components/schemas/bbox' - style: form - explode: false - datetime: - name: datetime - in: query - description: >- - Either a date-time or an interval, open or closed. Date and time - expressions - - adhere to RFC 3339. Open intervals are expressed using double-dots. - - - Examples: - - - * A date-time: "2018-02-12T23:20:50Z" - - * A closed interval: "2018-02-12T00:00:00Z/2018-03-18T12:31:12Z" - - * Open intervals: "2018-02-12T00:00:00Z/.." or "../2018-03-18T12:31:12Z" - - - Only features that have a temporal property that intersects the value of - - `datetime` are selected. - - - If a feature has multiple temporal properties, it is the decision of the - - server whether only a single temporal property is used to determine - - the extent or all relevant temporal properties. - schema: - $ref: '#/components/schemas/datetime' - required: false - explode: false - style: form query: name: query in: query @@ -555,26 +356,16 @@ components: $ref: '#/components/schemas/fields' style: form explode: false - IfMatch: - name: If-Match - in: header - description: Only take the action if the ETag of the item still matches - required: true - schema: - type: string - IfMatchOptional: - name: If-Match - in: header - description: Only take the action if the ETag of the item still matches - required: false - schema: - type: string schemas: collection: type: object required: - id - links + - stac_version + - description + - license + - extent properties: id: description: 'identifier of the collection used, for example, in URIs' @@ -585,7 +376,10 @@ components: type: string example: address description: - description: a description of the features in the collection + description: >- + Detailed multi-line description to fully explain the collection. + [CommonMark 0.29](http://commonmark.org/) syntax MAY be used for + rich text representation. type: string example: An address. links: @@ -616,6 +410,138 @@ components: example: - 'http://www.opengis.net/def/crs/OGC/1.3/CRS84' - 'http://www.opengis.net/def/crs/EPSG/0/4326' + stac_version: + $ref: '#/components/schemas/stac_version' + stac_extensions: + $ref: '#/components/schemas/stac_extensions' + keywords: + type: array + description: List of keywords describing the collection. + items: + type: string + version: + type: string + description: Version of the collection. + license: + $ref: '#/components/schemas/license' + providers: + $ref: '#/components/schemas/providers' + summaries: + $ref: '#/components/schemas/summaries' + example: + stac_version: 0.8.1 + stac_extensions: [] + id: Sentinel-2 + title: 'Sentinel-2 MSI: MultiSpectral Instrument, Level-1C' + description: | + Sentinel-2 is a wide-swath, high-resolution, multi-spectral + imaging mission... + license: proprietary + keywords: + - copernicus + - esa + - eu + - msi + - radiance + - sentinel + providers: + - name: ESA + roles: + - producer + - licensor + url: 'https://sentinel.esa.int/web/sentinel/user-guides/sentinel-2-msi' + extent: + spatial: + bbox: + - - -180 + - -56 + - 180 + - 83 + temporal: + interval: + - - '2015-06-23T00:00:00Z' + - '2019-07-10T13:44:56Z' + summaries: + datetime: + min: '2015-06-23T00:00:00Z' + max: '2019-07-10T13:44:56Z' + 'sci:citation': + - 'Copernicus Sentinel data [Year]' + 'eo:gsd': + - 10 + - 30 + - 60 + 'eo:platform': + - sentinel-2a + - sentinel-2b + 'eo:constellation': + - sentinel-2 + 'eo:instrument': + - msi + 'eo:off_nadir': + min: 0 + max: 100 + 'eo:sun_elevation': + min: 6.78 + max: 89.9 + 'eo:bands': + - - name: B1 + common_name: coastal + center_wavelength: 4.439 + gsd: 60 + - name: B2 + common_name: blue + center_wavelength: 4.966 + gsd: 10 + - name: B3 + common_name: green + center_wavelength: 5.6 + gsd: 10 + - name: B4 + common_name: red + center_wavelength: 6.645 + gsd: 10 + - name: B5 + center_wavelength: 7.039 + gsd: 20 + - name: B6 + center_wavelength: 7.402 + gsd: 20 + - name: B7 + center_wavelength: 7.825 + gsd: 20 + - name: B8 + common_name: nir + center_wavelength: 8.351 + gsd: 10 + - name: B8A + center_wavelength: 8.648 + gsd: 20 + - name: B9 + center_wavelength: 9.45 + gsd: 60 + - name: B10 + center_wavelength: 1.3735 + gsd: 60 + - name: B11 + common_name: swir16 + center_wavelength: 1.6137 + gsd: 20 + - name: B12 + common_name: swir22 + center_wavelength: 2.2024 + gsd: 20 + links: + - rel: self + href: 'http://cool-sat.com/collections/Sentinel-2' + - rel: root + href: 'http://cool-sat.com/collections' + - rel: license + href: >- + https://scihub.copernicus.eu/twiki/pub/SciHubWebPortal/TermsConditions/Sentinel_Data_Terms_and_Conditions.pdf + title: >- + Legal notice on the use of Copernicus Sentinel Data and Service + Information collections: type: object required: @@ -644,50 +570,58 @@ components: description: >- Information about the exception: an error code plus an optional description. + required: + - code properties: code: type: string description: type: string - required: - - code extent: type: object description: >- The extent of the features in the collection. In the Core only spatial - and temporal - - extents are specified. Extensions may add additional members to - represent other - - extents, for example, thermal or pressure ranges. + and temporal extents are specified. Extensions may add additional + members to represent other extents, for example, thermal or pressure + ranges. properties: spatial: + description: The spatial extent of the features in the collection. type: object properties: bbox: description: >- One or more bounding boxes that describe the spatial extent of - the dataset. - - In the Core only a single bounding box is supported. Extensions - may support - - additional areas. If multiple areas are provided, the union of - the bounding - - boxes describes the spatial extent. + the dataset. In the Core only a single bounding box is + supported. Extensions may support additional areas. If multiple + areas are provided, the union of the bounding boxes describes + the spatial extent. type: array minItems: 1 items: description: >- - West, south, east, north edges of the bounding box. The - coordinates - - are in the coordinate reference system specified in `crs`. By - default - - this is WGS 84 longitude/latitude. + Each bounding box is provided as four or six numbers, + depending on whether the coordinate reference system includes + a vertical axis (height or depth): * Lower left corner, + coordinate axis 1 * Lower left corner, coordinate axis 2 * + Minimum value, coordinate axis 3 (optional) * Upper right + corner, coordinate axis 1 * Upper right corner, coordinate + axis 2 * Maximum value, coordinate axis 3 (optional) The + coordinate reference system of the values is WGS 84 + longitude/latitude + (http://www.opengis.net/def/crs/OGC/1.3/CRS84) unless a + different coordinate reference system is specified in `crs`. + For WGS 84 longitude/latitude the values are in most cases the + sequence of minimum longitude, minimum latitude, maximum + longitude and maximum latitude. However, in cases where the + box spans the antimeridian the first value (west-most box + edge) is larger than the third value (east-most box edge). If + the vertical axis is included, the third and the sixth number + are the bottom and the top of the 3-dimensional bounding box. + If a feature has multiple spatial geometry properties, it is + the decision of the server whether only a single spatial + geometry property is used to determine the extent or all + relevant geometries. type: array minItems: 4 maxItems: 6 @@ -701,22 +635,16 @@ components: crs: description: >- Coordinate reference system of the coordinates in the spatial - extent - - (property `bbox`). The default reference system is WGS 84 - longitude/latitude. - - In the Core this is the only supported coordinate reference - system. - - Extensions may support additional coordinate reference systems - and add - - additional enum values. + extent (property `bbox`). The default reference system is WGS 84 + longitude/latitude. In the Core this is the only supported + coordinate reference system. Extensions may support additional + coordinate reference systems and add additional enum values. type: string enum: - 'http://www.opengis.net/def/crs/OGC/1.3/CRS84' default: 'http://www.opengis.net/def/crs/OGC/1.3/CRS84' + required: + - bbox temporal: description: The temporal extent of the features in the collection. type: object @@ -724,28 +652,18 @@ components: interval: description: >- One or more time intervals that describe the temporal extent of - the dataset. - - The value `null` is supported and indicates an open time - intervall. - - In the Core only a single time interval is supported. Extensions - may support - - multiple intervals. If multiple intervals are provided, the - union of the - - intervals describes the temporal extent. + the dataset. The value `null` is supported and indicates an open + time intervall. In the Core only a single time interval is + supported. Extensions may support multiple intervals. If + multiple intervals are provided, the union of the intervals + describes the temporal extent. type: array minItems: 1 items: description: >- - Begin and end times of the time interval. The timestamps - - are in the coordinate reference system specified in `trs`. By - default - - this is the Gregorian calendar. + Begin and end times of the time interval. The timestamps are + in the coordinate reference system specified in `trs`. By + default this is the Gregorian calendar. type: array minItems: 2 maxItems: 2 @@ -759,22 +677,19 @@ components: trs: description: >- Coordinate reference system of the coordinates in the temporal - extent - - (property `interval`). The default reference system is the - Gregorian calendar. - - In the Core this is the only supported temporal reference - system. - - Extensions may support additional temporal reference systems and - add - - additional enum values. + extent (property `interval`). The default reference system is + the Gregorian calendar. In the Core this is the only supported + temporal reference system. Extensions may support additional + temporal reference systems and add additional enum values. type: string enum: - 'http://www.opengis.net/def/uom/ISO-8601/0/Gregorian' default: 'http://www.opengis.net/def/uom/ISO-8601/0/Gregorian' + required: + - interval + required: + - spatial + - temporal featureCollectionGeoJSON: type: object required: @@ -788,20 +703,17 @@ components: features: type: array items: - $ref: '#/components/schemas/featureGeoJSON' + $ref: '#/components/schemas/item' links: type: array items: $ref: '#/components/schemas/link' timeStamp: - type: string - format: date-time + $ref: '#/components/schemas/timeStamp' numberMatched: - type: integer - minimum: 0 + $ref: '#/components/schemas/numberMatched' numberReturned: - type: integer - minimum: 0 + $ref: '#/components/schemas/numberReturned' featureGeoJSON: type: object required: @@ -886,6 +798,9 @@ components: type: number link: type: object + required: + - href + - rel properties: href: type: string @@ -896,7 +811,7 @@ components: example: child type: type: string - example: application/json + example: application/geo+json hreflang: type: string example: en @@ -906,10 +821,6 @@ components: length: type: integer title: Link - description: A generic link. - required: - - href - - rel multilinestringGeoJSON: type: object required: @@ -969,6 +880,23 @@ components: minItems: 2 items: type: number + numberMatched: + description: |- + The number of features of the feature type that match the selection + parameters like `bbox`. + type: integer + minimum: 0 + example: 127 + numberReturned: + description: |- + The number of features in the feature collection. + A server may omit this information in a response, if the information + about the number of features is not known or difficult to compute. + If the value is provided, the value shall be identical to the number + of items in the "features" array. + type: integer + minimum: 0 + example: 10 pointGeoJSON: type: object required: @@ -1004,6 +932,77 @@ components: minItems: 2 items: type: number + timeStamp: + description: >- + This property indicates the time and date when the response was + generated. + type: string + format: date-time + example: '2017-08-17T08:05:32Z' + license: + type: string + description: >- + License(s) of the data as a SPDX [License + identifier](https://spdx.org/licenses/) or + [expression](https://spdx.org/spdx-specification-21-web-version#h.jxpfx0ykyb60). + Alternatively, use `proprietary` if the license is not on the SPDX + license list or `various` if multiple licenses apply. In these two cases + links to the license texts SHOULD be added, see the `license` link + relation type. Non-SPDX licenses SHOULD add a link to the license text + with the `license` relation in the links section. The license text MUST + NOT be provided as a value of this field. If there is no public license + URL available, it is RECOMMENDED to host the license text and link to + it. + example: Apache-2.0 + providers: + type: array + description: >- + A list of providers, which may include all organizations capturing or + processing the data or the hosting provider. Providers should be listed + in chronological order with the most recent provider being the last + element of the list. + items: + type: object + title: Provider + required: + - name + properties: + name: + description: The name of the organization or the individual. + type: string + description: + description: >- + Multi-line description to add further provider information such as + processing details for processors and producers, hosting details + for hosts or basic contact information. CommonMark 0.29 syntax MAY + be used for rich text representation. + type: string + roles: + description: >- + Roles of the provider. The provider's role(s) can be one or more + of the following elements: * licensor: The organization that is + licensing the dataset under the license specified in the + collection's license field. * producer: The producer of the data + is the provider that initially captured and processed the source + data, e.g. ESA for Sentinel-2 data. * processor: A processor is + any provider who processed data to a derived product. * host: The + host is the actual provider offering the data on their storage. + There should be no more than one host, specified as last element + of the list. + type: array + items: + type: string + enum: + - producer + - licensor + - processor + - host + url: + description: >- + Homepage on which the provider describes the dataset and publishes + contact information. + type: string + format: url searchBody: description: The search criteria type: object @@ -1026,7 +1025,10 @@ components: default: null limit: type: integer + minimum: 1 example: 10 + default: 10 + maximum: 10000 description: The maximum number of results to return (page size). Defaults to 10 bbox: description: | @@ -1034,26 +1036,21 @@ components: selected. The bounding box is provided as four or six numbers, depending on whether the coordinate reference system includes a vertical axis (elevation or depth): - * Lower left corner, coordinate axis 1 * Lower left corner, coordinate axis 2 * Lower left corner, coordinate axis 3 (optional) * Upper right corner, coordinate axis 1 * Upper right corner, coordinate axis 2 * Upper right corner, coordinate axis 3 (optional) - The coordinate reference system of the values is WGS84 longitude/latitude (http://www.opengis.net/def/crs/OGC/1.3/CRS84) unless a different coordinate reference system is specified in the parameter `bbox-crs`. - For WGS84 longitude/latitude the values are in most cases the sequence of minimum longitude, minimum latitude, maximum longitude and maximum latitude. However, in cases where the box spans the antimeridian the first value (west-most box edge) is larger than the third value (east-most box edge). - - If a feature has multiple spatial geometry properties, it is the decision of the server whether only a single spatial geometry property is used to determine the extent or all relevant geometries. @@ -1084,9 +1081,8 @@ components: type: array description: > Array of Item ids to return. All other filter parameters that further - restrict the number of - - search results (except `next` and `limit`) are ignored + restrict the number of search results (except `next` and `limit`) are + ignored items: type: string datetimeFilter: @@ -1129,36 +1125,19 @@ components: type: string description: >- Either a date-time or an interval, open or closed. Date and time - expressions - - adhere to RFC 3339. Open intervals are expressed using double-dots. - - - Examples: - - - * A date-time: "2018-02-12T23:20:50Z" - - * A closed interval: "2018-02-12T00:00:00Z/2018-03-18T12:31:12Z" - - * Open intervals: "2018-02-12T00:00:00Z/.." or "../2018-03-18T12:31:12Z" - - - Only features that have a temporal property that intersects the value of - - `datetime` are selected. - - - If a feature has multiple temporal properties, it is the decision of the - - server whether only a single temporal property is used to determine - - the extent or all relevant temporal properties. + expressions adhere to RFC 3339. Open intervals are expressed using + double-dots. Examples: * A date-time: "2018-02-12T23:20:50Z" * A closed + interval: "2018-02-12T00:00:00Z/2018-03-18T12:31:12Z" * Open intervals: + "2018-02-12T00:00:00Z/.." or "../2018-03-18T12:31:12Z" Only features + that have a temporal property that intersects the value of `datetime` + are selected. If a feature has multiple temporal properties, it is the + decision of the server whether only a single temporal property is used + to determine the extent or all relevant temporal properties. example: '2018-02-12T00:00:00Z/2018-03-18T12:31:12Z' stac_version: title: STAC version type: string - example: 0.8.0 + example: 0.8.1 stac_extensions: title: STAC extensions type: array @@ -1170,6 +1149,47 @@ components: format: uri - title: Reference to a core extension type: string + summaries: + description: >- + Summaries are either a unique set of all available values *or* + statistics. Statistics by default only specify the range (minimum and + maximum values), but can optionally be accompanied by additional + statistical values. The range can specify the potential range of values, + but it is recommended to be as precise as possible. The set of values + must contain at least one element and it is strongly recommended to list + all values. It is recommended to list as many properties as reasonable + so that consumers get a full overview of the Collection. Properties that + are covered by the Collection specification (e.g. `providers` and + `license`) may not be repeated in the summaries. + type: object + additionalProperties: + oneOf: + - type: array + title: Set of values + items: + description: A value of any type. + - type: object + title: Statistics + description: >- + By default, only ranges with a minimum and a maximum value can be + specified. Ranges can be specified for ordinal values only, which + means they need to have a rank order. Therefore, ranges can only + be specified for numbers and some special types of strings. + Examples: grades (A to F), dates or times. Implementors are free + to add other derived statistical values to the object, for example + `mean` or `stddev`. + required: + - min + - max + properties: + min: + anyOf: + - type: string + - type: number + max: + anyOf: + - type: string + - type: number catalogDefinition: type: object required: @@ -1191,6 +1211,8 @@ components: description: type: string example: Catalog of NAIP Imagery. + summaries: + $ref: '#/components/schemas/summaries' links: type: array items: @@ -1249,12 +1271,15 @@ components: type: integer nullable: true minimum: 0 + example: 5 matched: type: integer minimum: 0 + example: 1 returned: type: integer minimum: 0 + example: 1 item: description: >- A GeoJSON Feature augmented with foreign members that contain values @@ -1291,9 +1316,12 @@ components: assets: $ref: '#/components/schemas/itemAssets' example: - stac_version: 0.8.0 + stac_version: 0.8.1 + stac_extensions: + - eo + - 'https://example.com/cs-extension/1.0/schema.json' type: Feature - id: CS3-20160503_132130_04 + id: CS3-20160503_132131_05 bbox: - -122.59750209 - 37.48803556 @@ -1313,21 +1341,42 @@ components: - - -122.308150179 - 37.488035566 properties: - datetime: '2016-05-03T13:21:30.040Z' + datetime: '2016-05-03T13:22:30.040Z' + title: A CS3 item + license: PDDL-1.0 + providers: + - name: CoolSat + roles: + - producer + - licensor + url: 'https://cool-sat.com/' + 'eo:sun_azimuth': 168.7 + 'eo:cloud_cover': 0.12 + 'eo:off_nadir': 1.4 + 'eo:platform': coolsat2 + 'eo:instrument': cool_sensor_v1 + 'eo:bands': [] + 'eo:sun_elevation': 33.4 + 'eo:gsd': 0.512 + collection: CS3 links: - rel: self - href: >- - http://cool-sat.com/catalog/collections/cs/items/CS3-20160503_132130_04.json + href: 'http://cool-sat.com/collections/CS3/items/20160503_132130_04' + - rel: root + href: 'http://cool-sat.com/collections' + - rel: parent + href: 'http://cool-sat.com/collections/CS3' + - rel: collection + href: 'http://cool-sat.com/collections/CS3' assets: analytic: - title: 4-Band Analytic href: >- - http://cool-sat.com/catalog/collections/cs/items/CS3-20160503_132130_04/analytic.tif + http://cool-sat.com/static-catalog/CS3/20160503_132130_04/analytic.tif + title: 4-Band Analytic thumbnail: - title: Thumbnail href: >- - http://cool-sat.com/catalog/collections/cs/items/CS3-20160503_132130_04/thumb.png - type: image/png + http://cool-sat.com/static-catalog/CS3/20160503_132130_04/thumbnail.png + title: Thumbnail itemId: type: string example: path/to/example.tif @@ -1522,50 +1571,14 @@ components: exclude: - geometry - properties.datetime - partialItem: - type: object - properties: - stac_version: - $ref: '#/components/schemas/stac_version' - stac_extensions: - $ref: '#/components/schemas/stac_extensions' - id: - $ref: '#/components/schemas/itemId' - bbox: - $ref: '#/components/schemas/bbox' - geometry: - $ref: 'https://geojson.org/schema/Geometry.json' - type: - $ref: '#/components/schemas/itemType' - properties: - $ref: '#/components/schemas/partialItemProperties' - links: - type: array - items: - $ref: '#/components/schemas/link' - assets: - $ref: '#/components/schemas/itemAssets' - example: - assets: - analytic: - title: 1-Band Analytic - href: >- - http://cool-sat.com/catalog/collections/cs/items/CS3-201605XX_132130_04/analytic-1.tif - partialItemProperties: - type: object - description: allows for partial collections of metadata fields - additionalProperties: true - properties: - datetime: - $ref: '#/components/schemas/datetime' responses: LandingPage: description: |- - The landing page provides links to the API definition - (link relations `service-desc` and `service-doc`), - the Conformance declaration (path `/conformance`, - link relation `conformance`), and the Feature - Collections (path `/collections`, link relation + The landing page provides links to the API definition + (link relations `service-desc` and `service-doc`), + the Conformance declaration (path `/conformance`, + link relation `conformance`), and the Feature + Collections (path `/collections`, link relation `data`). content: application/json: @@ -1603,10 +1616,9 @@ components: ConformanceDeclaration: description: |- The URIs of all conformance classes supported by the server. - - To support "generic" clients that want to access multiple - OGC API Features implementations - and not "just" a specific - API / server, the server declares the conformance + To support "generic" clients that want to access multiple + OGC API Features implementations - and not "just" a specific + API / server, the server declares the conformance classes it implements and conforms to. content: application/json: @@ -1623,250 +1635,74 @@ components: type: string Collections: description: >- - The feature collections shared by this API. - - - The dataset is organized as one or more feature collections. This - resource - - provides information about and access to the collections. - - - The response contains the list of collections. For each collection, a - link - - to the items in the collection (path - `/collections/{collectionId}/items`, - - link relation `items`) as well as key information about the collection. - - This information includes: - - + The feature collections shared by this API. The dataset is organized as + one or more feature collections. This resource provides information + about and access to the collections. The response contains the list of + collections. For each collection, a link to the items in the collection + (path `/collections/{collectionId}/items`, link relation `items`) as + well as key information about the collection. This information includes: * A local identifier for the collection that is unique for the dataset; - * A list of coordinate reference systems (CRS) in which geometries may be returned by the server. The first CRS is the default coordinate reference system (the default is always WGS 84 with axis order - longitude/latitude); - - * An optional title and description for the collection; - - * An optional extent that can be used to provide an indication of the - spatial and temporal extent of the collection - typically derived from - the data; - - * An optional indicator about the type of the items in the collection - (the default value, if the indicator is not provided, is 'feature'). + longitude/latitude); * An optional title and description for the + collection; * An optional extent that can be used to provide an + indication of the spatial and temporal extent of the collection - + typically derived from the data; * An optional indicator about the type + of the items in the collection (the default value, if the indicator is + not provided, is 'feature'). content: application/json: schema: $ref: '#/components/schemas/collections' - example: - links: - - href: 'http://data.example.org/collections.json' - rel: self - type: application/json - title: this document - - href: 'http://data.example.org/collections.html' - rel: alternate - type: text/html - title: this document as HTML - - href: 'http://schemas.example.org/1.0/buildings.xsd' - rel: describedBy - type: application/xml - title: GML application schema for Acme Corporation building data - - href: 'http://download.example.org/buildings.gpkg' - rel: enclosure - type: application/geopackage+sqlite3 - title: Bulk download (GeoPackage) - length: 472546 - collections: - - id: buildings - title: Buildings - description: Buildings in the city of Bonn. - extent: - spatial: - bbox: - - - 7.01 - - 50.63 - - 7.22 - - 50.78 - temporal: - interval: - - - '2010-02-15T12:34:56Z' - - null - links: - - href: 'http://data.example.org/collections/buildings/items' - rel: items - type: application/geo+json - title: Buildings - - href: 'http://data.example.org/collections/buildings/items.html' - rel: items - type: text/html - title: Buildings - - href: 'https://creativecommons.org/publicdomain/zero/1.0/' - rel: license - type: text/html - title: CC0-1.0 - - href: 'https://creativecommons.org/publicdomain/zero/1.0/rdf' - rel: license - type: application/rdf+xml - title: CC0-1.0 text/html: schema: type: string Collection: description: >- - Information about the feature collection with id `collectionId`. - - - The response contains a linkto the items in the collection - - (path `/collections/{collectionId}/items`,link relation `items`) - - as well as key information about the collection. This information - - includes: - - - * A local identifier for the collection that is unique for the dataset; - - * A list of coordinate reference systems (CRS) in which geometries may - be returned by the server. The first CRS is the default coordinate + Information about the feature collection with id `collectionId`. The + response contains a link to the items in the collection (path + `/collections/{collectionId}/items`, link relation `items`) as well as + key information about the collection. This information includes: * A + local identifier for the collection that is unique for the dataset; * A + list of coordinate reference systems (CRS) in which geometries may be + returned by the server. The first CRS is the default coordinate reference system (the default is always WGS 84 with axis order - longitude/latitude); - - * An optional title and description for the collection; - - * An optional extent that can be used to provide an indication of the - spatial and temporal extent of the collection - typically derived from - the data; - - * An optional indicator about the type of the items in the collection - (the default value, if the indicator is not provided, is 'feature'). + longitude/latitude); * An optional title and description for the + collection; * An optional extent that can be used to provide an + indication of the spatial and temporal extent of the collection - + typically derived from the data; * An optional indicator about the type + of the items in the collection (the default value, if the indicator is + not provided, is 'feature'). content: application/json: schema: $ref: '#/components/schemas/collection' - example: - id: buildings - title: Buildings - description: Buildings in the city of Bonn. - extent: - spatial: - bbox: - - - 7.01 - - 50.63 - - 7.22 - - 50.78 - temporal: - interval: - - - '2010-02-15T12:34:56Z' - - null - links: - - href: 'http://data.example.org/collections/buildings/items' - rel: items - type: application/geo+json - title: Buildings - - href: 'http://data.example.org/collections/buildings/items.html' - rel: items - type: text/html - title: Buildings - - href: 'https://creativecommons.org/publicdomain/zero/1.0/' - rel: license - type: text/html - title: CC0-1.0 - - href: 'https://creativecommons.org/publicdomain/zero/1.0/rdf' - rel: license - type: application/rdf+xml - title: CC0-1.0 text/html: schema: type: string Features: description: >- - The response is a document consisting of features in the collection. - - The features included in the response are determined by the server - - based on the query parameters of the request. To support access to - - larger collections without overloading the client, the API supports - - paged access with links to the next page, if more features are selected - - that the page size. - - - The `bbox` and `datetime` parameter can be used to select only a - - subset of the features in the collection (the features that are in the - - bounding box or time interval). The `bbox` parameter matches all - features - - in the collection that are not associated with a location, too. The - - `datetime` parameter matches all features in the collection that are - - not associated with a time stamp or interval, too. - - - The `limit` parameter may be used to control the subset of the - - selected features that should be returned in the response, the page - size. - - Each page may include information about the number of selected and - - returned features (`numberMatched` and `numberReturned`) as well as - - links to support paging (link relation `next`). + The response is a document consisting of features in the collection. The + features included in the response are determined by the server based on + the query parameters of the request. To support access to larger + collections without overloading the client, the API supports paged + access with links to the next page, if more features are selected that + the page size. The `bbox` and `datetime` parameter can be used to select + only a subset of the features in the collection (the features that are + in the bounding box or time interval). The `bbox` parameter matches all + features in the collection that are not associated with a location, too. + The `datetime` parameter matches all features in the collection that are + not associated with a time stamp or interval, too. The `limit` parameter + may be used to control the subset of the selected features that should + be returned in the response, the page size. Each page may include + information about the number of selected and returned features + (`numberMatched` and `numberReturned`) as well as links to support + paging (link relation `next`). content: application/geo+json: schema: $ref: '#/components/schemas/featureCollectionGeoJSON' - example: - type: FeatureCollection - links: - - href: 'http://data.example.com/collections/buildings/items.json' - rel: self - type: application/geo+json - title: this document - - href: 'http://data.example.com/collections/buildings/items.html' - rel: alternate - type: text/html - title: this document as HTML - - href: >- - http://data.example.com/collections/buildings/items.json&offset=10&limit=2 - rel: next - type: application/geo+json - title: next page - timeStamp: '2018-04-03T14:52:23Z' - numberMatched: 123 - numberReturned: 2 - features: - - type: Feature - id: '123' - geometry: - type: Polygon - coordinates: - - ... - properties: - function: residential - floors: '2' - lastUpdate: '2015-08-01T12:34:56Z' - - type: Feature - id: '132' - geometry: - type: Polygon - coordinates: - - ... - properties: - function: public use - floors: '10' - lastUpdate: '2013-12-03T10:15:37Z' text/html: schema: type: string @@ -1877,34 +1713,7 @@ components: content: application/geo+json: schema: - $ref: '#/components/schemas/featureGeoJSON' - example: - type: Feature - links: - - href: 'http://data.example.com/id/building/123' - rel: canonical - title: canonical URI of the building - - href: 'http://data.example.com/collections/buildings/items/123.json' - rel: self - type: application/geo+json - title: this document - - href: 'http://data.example.com/collections/buildings/items/123.html' - rel: alternate - type: text/html - title: this document as HTML - - href: 'http://data.example.com/collections/buildings' - rel: collection - type: application/geo+json - title: the collection document - id: '123' - geometry: - type: Polygon - coordinates: - - ... - properties: - function: residential - floors: '2' - lastUpdate: '2015-08-01T12:34:56Z' + $ref: '#/components/schemas/item' text/html: schema: type: string @@ -1918,11 +1727,7 @@ components: schema: type: string NotFound: - description: The specified resource was not found - content: - application/json: - schema: - $ref: '#/components/schemas/exception' + description: The requested URI was not found. ServerError: description: A server error occurred. content: @@ -1932,23 +1737,8 @@ components: text/html: schema: type: string - BadRequest: - description: The request was malformed or semantically invalid - content: - application/json: - schema: - $ref: '#/components/schemas/exception' - InternalServerError: - description: >- - The request was syntactically and semantically valid, but an error - occurred while trying to act upon it - content: - application/json: - schema: - $ref: '#/components/schemas/exception' servers: - url: 'http://dev.cool-sat.com' description: Development server - url: 'http://www.cool-sat.com' description: Production server - diff --git a/packages/api/index.js b/packages/api/index.js index fdd4d4b9..1afb63cc 100644 --- a/packages/api/index.js +++ b/packages/api/index.js @@ -1,11 +1,6 @@ -/* eslint-disable new-cap, no-lonely-if */ - -'use strict' - const satlib = require('@sat-utils/api-lib') -module.exports.handler = async (event) => { - // determine endpoint +function determineEndpoint(event) { let endpoint = process.env.SATAPI_URL if (typeof endpoint === 'undefined') { if ('X-Forwarded-Host' in event.headers) { @@ -17,21 +12,10 @@ module.exports.handler = async (event) => { } } } + return endpoint +} - const buildResponse = async (statusCode, result) => { - const response = { - statusCode, - headers: { - 'Content-Type': 'application/json', - 'Access-Control-Allow-Origin': '*', // Required for CORS support to work - 'Access-Control-Allow-Credentials': true - }, - body: result - } - return response - } - - // get payload +function buildRequest(event) { const method = event.httpMethod let query = {} if (method === 'POST' && event.body) { @@ -39,12 +23,26 @@ module.exports.handler = async (event) => { } else if (method === 'GET' && event.queryStringParameters) { query = event.queryStringParameters } - const result = await satlib.api.API(event.path, query, satlib.es, endpoint) - let returnResponse - if (result instanceof Error) { - returnResponse = buildResponse(404, result.message) - } else { - returnResponse = buildResponse(200, JSON.stringify(result)) + return query +} + +function buildResponse(statusCode, result) { + return { + statusCode, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', // Required for CORS support to work + 'Access-Control-Allow-Credentials': true + }, + body: result } - return returnResponse +} + +module.exports.handler = async (event) => { + const endpoint = determineEndpoint(event) + const query = buildRequest(event) + const result = await satlib.api.API(event.path, query, satlib.es, endpoint) + return result instanceof Error ? + buildResponse(404, result.message) : + buildResponse(200, JSON.stringify(result)) } diff --git a/packages/api/package.json b/packages/api/package.json index f93f1903..64d77927 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -33,7 +33,9 @@ "aws-event-mocks": "0.0.0", "proxyquire": "^2.1.0", "sinon": "^7.1.1", - "webpack": "~4.5.0", - "webpack-cli": "~2.0.14" + "webpack": "~4.41", + "webpack-cli": "~3.3", + "zip-webpack-plugin": "^3.0.0", + "copy-webpack-plugin": "^5.1.1" } } diff --git a/packages/api/webpack.config.js b/packages/api/webpack.config.js index 0ca6a60f..0c4e39bf 100644 --- a/packages/api/webpack.config.js +++ b/packages/api/webpack.config.js @@ -1,12 +1,13 @@ +const path = require('path') +const ZipPlugin = require('zip-webpack-plugin') +const CopyPlugin = require('copy-webpack-plugin') -const path = require('path'); +let mode = 'development' +let devtool = 'inline-source-map' -let mode = 'development'; -let devtool = 'inline-source-map'; - -if(process.env.PRODUCTION) { - mode = 'production', - devtool = false +if (process.env.PRODUCTION) { + mode = 'production' + devtool = false } module.exports = { @@ -18,10 +19,22 @@ module.exports = { path: path.resolve(__dirname, 'dist') }, externals: [ - 'aws-sdk', - 'electron', - {'formidable': 'url'} + 'aws-sdk' ], devtool, - target: 'node' -}; \ No newline at end of file + optimization: { + usedExports: true + }, + target: 'node', + plugins: [ + new CopyPlugin([ + { + from: 'api.yaml', + to: 'api.yaml' + } + ]), + new ZipPlugin({ + filename: 'api.zip' + }) + ] +} diff --git a/packages/ingest/index.js b/packages/ingest/index.js index 7aa55710..721c588d 100644 --- a/packages/ingest/index.js +++ b/packages/ingest/index.js @@ -1,11 +1,11 @@ 'use strict' -const AWS = require('aws-sdk') const satlib = require('@sat-utils/api-lib') +const logger = console module.exports.handler = async function handler(event) { - console.log(`Ingest Event: ${JSON.stringify(event)}`) + logger.info(`Ingest Event: ${JSON.stringify(event)}`) try { if (event.Records && (event.Records[0].EventSource === 'aws:sns')) { // event is SNS message of updated file on s3 @@ -24,7 +24,7 @@ module.exports.handler = async function handler(event) { } } = s3Record const url = `https://${bucketName}.s3.amazonaws.com/${key}` - console.log(`Ingesting catalog file ${url}`) + logger.log(`Ingesting catalog file ${url}`) const recursive = false return satlib.ingest.ingest(url, satlib.es, recursive) }) diff --git a/packages/ingest/package.json b/packages/ingest/package.json index db94d377..5b2a0e8c 100644 --- a/packages/ingest/package.json +++ b/packages/ingest/package.json @@ -38,6 +38,7 @@ "proxyquire": "^2.1.0", "sinon": "^7.1.1", "webpack": "~4.5.0", - "webpack-cli": "~2.0.14" + "webpack-cli": "~2.0.14", + "zip-webpack-plugin": "^3.0.0" } } diff --git a/packages/ingest/webpack.config.js b/packages/ingest/webpack.config.js index 0ca6a60f..b06fde72 100644 --- a/packages/ingest/webpack.config.js +++ b/packages/ingest/webpack.config.js @@ -1,12 +1,12 @@ +const path = require('path') +const ZipPlugin = require('zip-webpack-plugin') -const path = require('path'); +let mode = 'development' +let devtool = 'inline-source-map' -let mode = 'development'; -let devtool = 'inline-source-map'; - -if(process.env.PRODUCTION) { - mode = 'production', - devtool = false +if (process.env.PRODUCTION) { + mode = 'production' + devtool = false } module.exports = { @@ -18,10 +18,13 @@ module.exports = { path: path.resolve(__dirname, 'dist') }, externals: [ - 'aws-sdk', - 'electron', - {'formidable': 'url'} + 'aws-sdk' ], devtool, - target: 'node' -}; \ No newline at end of file + target: 'node', + plugins: [ + new ZipPlugin({ + filename: 'ingest.zip' + }) + ] +} diff --git a/serverless.yml b/serverless.yml new file mode 100644 index 00000000..7036cade --- /dev/null +++ b/serverless.yml @@ -0,0 +1,72 @@ +service: stac-api + +provider: + name: aws + runtime: nodejs12.x + stage: alpha + region: us-west-2 + environment: + LOG_LEVEL: DEBUG + ES_HOST: + Fn::GetAtt: [ElasticSearchInstance, DomainEndpoint] + iamRoleStatements: + - Effect: "Allow" + Resource: "arn:aws:es:#{AWS::Region}:#{AWS::AccountId}:domain/*" + Action: "es:*" + +package: + individually: true + +functions: + api: + handler: index.handler + package: + artifact: packages/api/dist/api.zip + environment: + STAC_ID: "stac-api" + STAC_TITLE: "STAC API" + STAC_DESCRIPTION: "STAC API" + STAC_VERSION: 0.8.1 + events: + - http: ANY / + - http: ANY {proxy+} + ingest: + handler: index.handler + timeout: 900 + package: + artifact: packages/ingest/dist/ingest.zip + events: + - sns: + topicName: ${self:service}-${self:provider.stage}-queue + displayName: Ingest STAC Item + # - sqs: + # arn: + # Fn::GetAtt: [ingestQueue, Arn] + #events: + # - sns: + # topicName: ${self:service}-${self:provider.stage}-ingest-queue + # displayName: STAC Item ingest topic + +resources: + Resources: +# ingestQueue: +# Type: AWS::SQS::Queue +# Properties: +# DelaySeconds: 1 +# QueueName: ${self:service}-${self:provider.stage}-ingest-queue + ElasticSearchInstance: + Type: AWS::Elasticsearch::Domain + Properties: + EBSOptions: + EBSEnabled: true + VolumeType: gp2 + VolumeSize: 35 + ElasticsearchClusterConfig: + InstanceType: t2.small.elasticsearch + InstanceCount: 3 + DedicatedMasterEnabled: false + ZoneAwarenessEnabled: false + ElasticsearchVersion: 6.8 + +plugins: + - serverless-pseudo-parameters \ No newline at end of file