Exemple #1
0
 def get(self, zone, *args):
     """
     ---
     summary: Get zone GeoJSON
     description: Get the GeoJSON for a requested climate zone.
     tags:
     - Climate Zones
     parameters:
     - zone
     responses:
       200:
         description: The GeoJSON definition for the climate zone
         content:
           application/geo+json:
             schema:
               type: object
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
          $ref: '#/components/responses/404-NotFound'
     """
     storage = get_storage()
     geojson = storage.read_climate_zone(zone.replace('+', ' '))
     response = make_response(json.dumps(geojson), 200)
     response.mimetype = 'application/geo+json'
     return response
Exemple #2
0
def validate_user_existence():
    """Ensures users exist in both auth0 and the database
    Returns
    -------
    True
        If the user already existed, or was created.
    False
        If the user does not have a verified auth0 account or If
        the /userinfo endpoint fails. See notes for details.
    Notes
    -----
    Since a JWT hold it's own expiration, it is possible for a user
    to be deleted in auth0 and still carry a valid token. In order
    to avoid adding that use back to our database, we return false
    if the request for user info fails.
    """
    from sfa_api.utils.storage import get_storage
    storage = get_storage()
    if not storage.user_exists():
        try:
            info = request_user_info()
        except (requests.exceptions.HTTPError, JSONDecodeError):
            return False
        else:
            if not info.get('email_verified', False):
                # User has a valid token, but their email
                # is yet to be verified
                return False
            storage.create_new_user()
    return True
Exemple #3
0
 def post(self, report_id):
     """
     ---
     summary: Store Raw Report
     tags:
       - Reports
     requestBody:
       description: JSON object containing the raw report.
       content:
         application/json:
           schema:
             $ref: '#/components/schemas/RawReportSchema'
     parameters:
       - report_id
     responses:
       204:
         description: Updated status successfully.
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#components/responses/404-NotFound'
     """
     raw_report_dict = request.get_json()
     raw_report = RawReportSchema().load(raw_report_dict)
     keep_ids = _extract_value_ids(raw_report)
     storage = get_storage()
     storage.store_raw_report(report_id, raw_report, keep_ids)
     return '', 204
Exemple #4
0
 def get(self, object_type):
     """
     ---
     summary: List all permitted actions on all objects of a given type.
     description: |-
       Get a list of object ids and the actions the user is permitted to
       perform on each object.
     parameters:
     - object_type
     tags:
       - Users
     responses:
       200:
         description: List of actions the user can make on the object.
         content:
           application/json:
             schema:
               $ref: '#/components/schemas/ActionsOnTypeList'
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#/components/responses/404-NotFound'
     """
     if object_type not in ALLOWED_OBJECT_TYPES:
         raise BadAPIRequest({
             'object_type':
             'Must be one of: '
             f'{", ".join(ALLOWED_OBJECT_TYPES)}'
         })
     storage = get_storage()
     object_dict = storage.list_actions_on_all_objects_of_type(object_type)
     json_response = {'object_type': object_type, 'objects': object_dict}
     return Response(ActionsOnTypeList().dumps(json_response),
                     mimetype="application/json")
Exemple #5
0
 def get(self, email):
     """
     ---
     summary: Get User Metadata by email.
     parameters:
     - email
     tags:
       - Users-By-Email
     responses:
       200:
         description: User successully retrieved.
         content:
           application/json:
             schema:
               $ref: '#/components/schemas/UserSchema'
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#/components/responses/404-NotFound'
     """
     storage = get_storage()
     auth0_id = get_auth0_id_of_user(email)
     user_id = storage.read_user_id(auth0_id)
     user = storage.read_user(user_id)
     user['email'] = email
     return jsonify(UserSchema().dump(user))
Exemple #6
0
 def get(self, *args):
     """
     ---
     summary: Find zones
     description: Find all zones that the given point falls within
     tags:
     - Climate Zones
     parameters:
     - latitude
     - longitude
     responses:
       200:
         description: Sucessfully retrieved zones.
         content:
           application/json:
             schema:
               type: array
               items:
                 $ref: '#/components/schemas/ZoneMetadata'
       400:
         $ref: '#/components/responses/400-BadRequest'
       401:
         $ref: '#/components/responses/401-Unauthorized'
     """
     lat, lon = validate_latitude_longitude()
     storage = get_storage()
     zones = storage.find_climate_zones(lat, lon)
     return jsonify(ZoneListSchema(many=True).dump(zones))
Exemple #7
0
    def get(self):
        """
        ---
        summary: List object types user can create.
        description: |-
          Get a list of object types the user can create.
        tags:
          - Users
        responses:
          200:
            description: List of object types the user has permission to create
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/UserCreatePerms'
          401:
            $ref: '#/components/responses/401-Unauthorized'
          404:
            $ref: '#/components/responses/404-NotFound'
        """

        storage = get_storage()
        object_types = storage.get_user_creatable_types()
        return Response(UserCreatePerms().dumps({'can_create': object_types}),
                        mimetype="application/json")
Exemple #8
0
 def delete(self, email, role_id):
     """
     ---
     summary: Remove a role from a User by email.
     parameters:
     - email
     - role_id
     tags:
       - Users-By-Email
       - Roles
     responses:
       204:
         description: Role removed successfully..
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#/components/responses/404-NotFound'
     """
     storage = get_storage()
     auth0_id = get_auth0_id_of_user(email)
     try:
         user_id = storage.read_user_id(auth0_id)
     except StorageAuthError:
         return '', 204
     return super().delete(user_id, role_id)
 def get(self, observation_id, *args):
     """
     ---
     summary: Get the time range of an Observation.
     description: |
       Get the minimum and maximum timestamps of Observation
       values stored in the Arbiter.
     tags:
     - Observations
     parameters:
       - observation_id
     responses:
       200:
         description: Observation time range retrieved successfully.
         content:
           application/json:
             schema:
               $ref: '#/components/schemas/ObservationTimeRange'
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#/components/responses/404-NotFound'
     """
     storage = get_storage()
     timerange = storage.read_observation_time_range(observation_id)
     timerange['observation_id'] = observation_id
     data = ObservationTimeRangeSchema().dump(timerange)
     return jsonify(data)
Exemple #10
0
 def post(self, report_id, status):
     """
     ---
     summary: Update the report status
     tags:
       - Reports
     parameters:
       - report_id
       - status
     responses:
       204:
         description: Updated status successfully.
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#components/responses/404-NotFound'
     """
     if status not in REPORT_STATUS_OPTIONS:
         raise BadAPIRequest({
             'status':
             'Must be one of '
             '{",".join(REPORT_STATUS_OPTIONS)}.'
         })
     storage = get_storage()
     storage.store_report_status(report_id, status)
     return '', 204
 def get(self, observation_id, *args):
     """
     ---
     summary: Get the gaps in Observation data.
     description: |
       Get the timestamps indicating where gaps in Observation
       data between start and end.
     tags:
     - Observations
     parameters:
       - observation_id
       - start_time
       - end_time
     responses:
       200:
         description: Observation value gap retrieved successfully.
         content:
           application/json:
             schema:
               $ref: '#/components/schemas/ObservationValueGap'
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#/components/responses/404-NotFound'
     """
     start, end = validate_start_end()
     storage = get_storage()
     out = {
         'gaps': storage.find_observation_gaps(observation_id, start, end),
         'observation_id': observation_id
     }
     data = ObservationGapSchema().dump(out)
     return jsonify(data)
Exemple #12
0
 def get(self, object_id):
     """
     ---
     summary: Available actions on object.
     description: |-
       Get a list of actions the current user is allowed to perform
       on an object.
     parameters:
     - object_id
     tags:
       - Users
     responses:
       200:
         description: List of actions the user can make on the object.
         content:
           application/json:
             schema:
               $ref: '#/components/schemas/ActionList'
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#/components/responses/404-NotFound'
     """
     storage = get_storage()
     actions = storage.get_user_actions_on_object(object_id)
     json_response = {'object_id': object_id, 'actions': actions}
     return jsonify(ActionList().dump(json_response))
 def get(self, observation_id, *args):
     """
     ---
     summary: Get latest Observation data.
     description: |
       Get the most recent timeseries value from the Observation
       entry.
     tags:
     - Observations
     parameters:
       - observation_id
     responses:
       200:
         description: Observation latest value retrieved successfully.
         content:
           application/json:
             schema:
               $ref: '#/components/schemas/ObservationValues'
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#/components/responses/404-NotFound'
     """
     storage = get_storage()
     values = storage.read_latest_observation_value(observation_id)
     data = ObservationValuesSchema().dump({
         "observation_id": observation_id,
         "values": values
     })
     return jsonify(data)
 def get(self, zone, *args):
     """
     ---
     summary: List sites in a climate zone
     description: List all sites that the user has access to within
                  the climate zone.
     tags:
     - Sites
     - Climate Zones
     parameters:
     - zone
     responses:
       200:
         description: A list of sites
         content:
           application/json:
             schema:
               type: array
               items:
                 $ref: '#/components/schemas/SiteMetadata'
       401:
         $ref: '#/components/responses/401-Unauthorized'
     """
     storage = get_storage()
     sites = storage.list_sites_in_zone(zone.replace('+', ' '))
     return jsonify(SiteResponseSchema(many=True).dump(sites))
 def get(self, aggregate_id):
     """
     ---
     summary: Get aggregate probabilistic forecasts.
     description: >
       Get metadata for all Probabilistic Forecasts associated with
       the aggregate that the user has access to.
     tags:
     - Aggregates
     parameters:
     - aggregate_id
     responses:
       200:
         description: Successfully retrieved aggregate cdf forecast groups.
         content:
           application/json:
             schema:
               type: array
               items:
                 $ref: '#/components/schemas/CDFForecastGroupMetadata'
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
          $ref: '#/components/responses/404-NotFound'
     """
     storage = get_storage()
     forecasts = storage.list_cdf_forecast_groups(aggregate_id=aggregate_id)
     return jsonify(CDFForecastGroupSchema(many=True).dump(forecasts))
 def get(self, site_id, *args):
     """
     ---
     summary: Get site forecasts
     description: >
       Get metadata for all forecasts associated with site that
       user has access to.
     tags:
     - Sites
     parameters:
     - site_id
     responses:
       200:
         description: Successfully retrieved site forecasts
         content:
           application/json:
             schema:
               type: array
               items:
                 $ref: '#/components/schemas/ForecastMetadata'
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
          $ref: '#/components/responses/404-NotFound'
     """
     storage = get_storage()
     forecasts = storage.list_forecasts(site_id=site_id)
     return jsonify(ForecastSchema(many=True).dump(forecasts))
 def get(self, observation_id, *args):
     """
     ---
     summary: Get dates where flag not present.
     description: |
       Get the dates where and Observation data is NOT
       flagged with the given flag.
     tags:
     - Observations
     parameters:
       - observation_id
       - start_time
       - end_time
       - flag
       - timezone
     responses:
       200:
         description: Unflagged observation values retrieved successfully.
         content:
           application/json:
             schema:
               $ref: '#/components/schemas/ObservationUnflagged'
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#/components/responses/404-NotFound'
     """
     errors = {}
     try:
         start, end = validate_start_end()
     except BadAPIRequest as err:
         errors = err.errors
     tz = request.args.get('timezone', 'UTC')
     flag = request.args.get('flag', None)
     if tz not in ALLOWED_TIMEZONES:
         errors['timezone'] = f'Unknown timezone {tz}'
     if flag is None:
         errors['flag'] = 'Must provide the flag parameter'
     else:
         try:
             int(flag)
         except ValueError:
             errors['flag'] = 'Flag must be an integer'
         else:
             if int(flag) > (2**16 - 1) or int(flag) < 0:
                 errors['flag'] = ('Flag must be a 2 byte unsigned '
                                   'integer between 0 and 65535')
     if errors:
         raise BadAPIRequest(errors)
     storage = get_storage()
     out = {
         'dates':
         storage.find_unflagged_observation_dates(observation_id, start,
                                                  end, flag, tz),
         'observation_id':
         observation_id
     }
     data = ObservationUnflaggedSchema().dump(out)
     return jsonify(data)
 def get(self, observation_id, *args):
     """
     ---
     summary: Get Observation data.
     description: Get the timeseries values from the Observation entry.
     tags:
     - Observations
     parameters:
       - observation_id
       - start_time
       - end_time
       - accepts
     responses:
       200:
         description: Observation values retrieved successfully.
         content:
           application/json:
             schema:
               $ref: '#/components/schemas/ObservationValues'
           text/csv:
             schema:
               type: string
             example: |-
               timestamp,value,quality_flag
               2018-10-29T12:00:00Z,32.93,0
               2018-10-29T13:00:00Z,25.17,0
       400:
         $ref: '#/components/responses/400-TimerangeTooLarge'
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#/components/responses/404-NotFound'
     """
     start, end = validate_start_end()
     storage = get_storage()
     values = storage.read_observation_values(observation_id, start, end)
     accepts = request.accept_mimetypes.best_match(
         ['application/json', 'text/csv'])
     if accepts == 'application/json':
         data = ObservationValuesSchema().dump({
             "observation_id": observation_id,
             "values": values
         })
         return jsonify(data)
     else:
         meta_url = url_for('observations.metadata',
                            observation_id=observation_id,
                            _external=True)
         csv_header = f'# observation_id: {observation_id}\n# metadata: {meta_url}\n'  # NOQA
         csv_values = values.to_csv(columns=['value', 'quality_flag'],
                                    index_label='timestamp',
                                    date_format='%Y%m%dT%H:%M:%S%z')
         csv_data = csv_header + csv_values
         response = make_response(csv_data, 200)
         response.mimetype = 'text/csv'
         return response
 def post(self, site_id, *args):
     """
     ---
     summary: Update Site metadata.
     tags:
     - Sites
     parameters:
     - site_id
     requestBody:
       description: >-
         JSON object of site metadata to update.
         If modeling parameters are to be updated,
         all parameters for a given tracking_type are
         required even if most values are the same to
         ensure proper validation. An empty object
         for modeling_parameters will have the effect of
         clearing all modeling parameters.
       required: True
       content:
         application/json:
             schema:
               $ref: '#/components/schemas/SiteUpdate'
     responses:
       200:
         description: Site updated successfully
         content:
           application/json:
             schema:
               type: string
               format: uuid
               description: The uuid of the updated site.
         headers:
           Location:
             schema:
               type: string
               format: uri
               description: Url of the updated site.
       400:
         $ref: '#/components/responses/400-BadRequest'
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#/components/responses/404-NotFound'
     """
     data = request.get_json()
     try:
         changes = SiteUpdateSchema().load(data)
     except ValidationError as err:
         raise BadAPIRequest(err.messages)
     storage = get_storage()
     changes.update(changes.pop('modeling_parameters', {}))
     storage.update_site(site_id, **changes)
     response = make_response(site_id, 200)
     response.headers['Location'] = url_for('sites.single', site_id=site_id)
     return response
 def post(self, observation_id, *args):
     """
     ---
     summary: Update Observation metadata.
     tags:
     - Observations
     parameters:
     - observation_id
     requestBody:
       description: >-
         JSON object of observation metadata to update.
         If 'uncertainty' is explicitly set to null, the value
         will be cleared from the stored metadata.
       required: True
       content:
         application/json:
             schema:
               $ref: '#/components/schemas/ObservationUpdate'
     responses:
       200:
         description: Observation updated successfully
         content:
           application/json:
             schema:
               type: string
               format: uuid
               description: The uuid of the updated observation.
         headers:
           Location:
             schema:
               type: string
               format: uri
               description: Url of the updated observation.
       400:
         $ref: '#/components/responses/400-BadRequest'
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#/components/responses/404-NotFound'
     """
     data = request.get_json()
     try:
         changes = ObservationUpdateSchema().load(data)
     except ValidationError as err:
         raise BadAPIRequest(err.messages)
     storage = get_storage()
     storage.update_observation(observation_id, **changes)
     response = make_response(observation_id, 200)
     response.headers['Location'] = url_for('observations.single',
                                            observation_id=observation_id)
     return response
 def post(self, *args):
     """
     ---
     summary: Create observation.
     tags:
       - Observations
     description: >-
       Create a new Observation by posting metadata. Note that POST
       requests to this endpoint without a trailing slash will result
       in a redirect response.
     requestBody:
       description: JSON representation of an observation.
       required: True
       content:
         application/json:
             schema:
               $ref: '#/components/schemas/ObservationDefinition'
     responses:
       201:
         description: Observation created successfully
         content:
           application/json:
             schema:
               type: string
               format: uuid
               description: The uuid of the created observation.
         headers:
           Location:
             schema:
               type: string
               format: uri
               description: Url of the created observation.
       400:
         $ref: '#/components/responses/400-BadRequest'
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#/components/responses/404-NotFound'
     """
     data = request.get_json()
     try:
         observation = ObservationPostSchema().load(data)
     except ValidationError as err:
         raise BadAPIRequest(err.messages)
     storage = get_storage()
     observation_id = storage.store_observation(observation)
     response = make_response(observation_id, 201)
     response.headers['Location'] = url_for('observations.single',
                                            observation_id=observation_id)
     return response
 def post(self, *args):
     """
     ---
     summary: Create site
     tags:
       - Sites
     description: >-
       Create a new Site by posting metadata. Note that POST
       requests to this endpoint without a trailing slash will
       result in a redirect response.
     requestBody:
       description: JSON respresentation of an site.
       required: True
       content:
         application/json:
             schema:
               $ref: '#/components/schemas/SiteDefinition'
     responses:
       201:
         description: Site created successfully
         content:
           application/json:
             schema:
               type: string
               format: uuid
               description: The uuid of the created site.
         headers:
           Location:
             schema:
               type: string
               format: uri
               description: Url of the created site.
       400:
         $ref: '#/components/responses/400-BadRequest'
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#/components/responses/404-NotFound'
     """
     data = request.get_json()
     try:
         site = SiteSchema().load(data)
     except ValidationError as err:
         return jsonify({'errors': err.messages}), 400
     storage = get_storage()
     site_id = storage.store_site(site)
     response = make_response(site_id, 201)
     response.headers['Location'] = url_for('sites.single', site_id=site_id)
     return response
Exemple #23
0
 def post(self):
     """
     ---
     summary: Create a new report.
     tags:
       - Reports
     requestBody:
       description: Metadata of the report to create.
       content:
         application/json:
           schema:
             $ref: '#/components/schemas/ReportMetadata'
     responses:
       201:
         description: Report created successfully.
         content:
           application/json:
             schema:
               type: string
               format: uuid
               description: The uuid of the created report.
         headers:
           Location:
             schema:
               type: string
               format: uri
               description: Url of the created report.
       400:
         $ref: '#/components/responses/400-BadRequest'
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#/components/responses/404-NotFound'
     """
     report = request.get_json()
     # report already valid json, so just store that
     # if validates properly
     errs = ReportPostSchema().validate(report)
     if errs:
         raise BadAPIRequest(errs)
     storage = get_storage()
     report_id = storage.store_report(report)
     response = make_response(report_id, 201)
     response.headers['Location'] = url_for('reports.single',
                                            report_id=report_id)
     enqueue_report(report_id, request.url_root.rstrip('/'))
     return response
Exemple #24
0
 def delete(self, report_id):
     """
     ---
     summary: Delete a report.
     tags:
       - Reports
     parameters:
     - report_id
     responses:
       204:
         description: Deleted report successfully.
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#components/responses/404-NotFound'
     """
     storage = get_storage()
     storage.delete_report(report_id)
     return '', 204
 def delete(self, role_id):
     """
     ---
     summary: Delete a Role
     parameters:
     - role_id
     tags:
       - Roles
     responses:
       204:
         description: Role deleted successfully.
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#/components/responses/404-NotFound'
     """
     storage = get_storage()
     storage.delete_role(role_id)
     return '', 204
 def delete(self, aggregate_id, *args):
     """
     ---
     summary: Delete aggregate.
     description: Delete an Aggregate, including its values and metadata.
     tags:
       - Aggregates
     parameters:
     - aggregate_id
     responses:
       204:
         description: Aggregate deleted successfully.
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#/components/responses/404-NotFound'
     """
     storage = get_storage()
     storage.delete_aggregate(aggregate_id)
     return '', 204
 def delete(self, observation_id, *args):
     """
     ---
     summary: Delete observation.
     description: Delete an Observation, including its values and metadata.
     tags:
       - Observations
     parameters:
     - observation_id
     responses:
       204:
         description: Observation deleted successfully.
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#/components/responses/404-NotFound'
     """
     storage = get_storage()
     storage.delete_observation(observation_id)
     return '', 204
 def delete(self, role_id, permission_id):
     """
     ---
     summary: Remove a permission from a role
     parameters:
     - role_id
     - permission_id
     tags:
       - Roles
       - Permissions
     responses:
       204:
         description: Removed permission successfully.
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#/components/responses/404-NotFound'
     """
     storage = get_storage()
     storage.remove_permission_from_role(role_id, permission_id)
     return '', 204
 def delete(self, aggregate_id, observation_id, *args):
     """
     ---
     summary: Delete an observation from an aggregate.
     description: Delete all instances of an observation from an aggregate.
     tags:
       - Aggregates
     parameters:
     - aggregate_id
     - observation_id
     responses:
       204:
         description: Observation deleted from aggregate successfully.
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#/components/responses/404-NotFound'
     """
     storage = get_storage()
     storage.delete_observation_from_aggregate(aggregate_id, observation_id)
     return '', 204
Exemple #30
0
 def post(self, user_id, role_id):
     """
     ---
     summary: Add a Role to a User.
     parameters:
     - user_id
     - role_id
     tags:
       - Users
       - Roles
     responses:
       204:
         description: Role Added Successfully.
       401:
         $ref: '#/components/responses/401-Unauthorized'
       404:
         $ref: '#/components/responses/404-NotFound'
     """
     storage = get_storage()
     storage.add_role_to_user(user_id, role_id)
     return '', 204