def get_one(self, ref_or_id, file_path, requester_user, **kwargs): """ Outputs the content of a specific file in a pack. Handles requests: GET /packs/views/file/<pack_ref_or_id>/<file path> """ pack_db = self._get_by_ref_or_id(ref_or_id=ref_or_id) if not pack_db: msg = 'Pack with ref_or_id "%s" does not exist' % (ref_or_id) raise StackStormDBObjectNotFoundError(msg) if not file_path: raise ValueError('Missing file path') pack_ref = pack_db.ref # Note: Until list filtering is in place we don't require RBAC check for icon file permission_type = PermissionType.PACK_VIEW if file_path not in WHITELISTED_FILE_PATHS: rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=pack_db, permission_type=permission_type) normalized_file_path = get_pack_file_abs_path(pack_ref=pack_ref, file_path=file_path) if not normalized_file_path or not os.path.isfile( normalized_file_path): # Ignore references to files which don't exist on disk raise StackStormDBObjectNotFoundError('File "%s" not found' % (file_path)) file_size, file_mtime = self._get_file_stats( file_path=normalized_file_path) response = Response() if not self._is_file_changed(file_mtime, **kwargs): response.status = http_client.NOT_MODIFIED else: if file_size is not None and file_size > MAX_FILE_SIZE: msg = ('File %s exceeds maximum allowed file size (%s bytes)' % (file_path, MAX_FILE_SIZE)) raise ValueError(msg) content_type = mimetypes.guess_type(normalized_file_path)[0] or \ 'application/octet-stream' response.headers['Content-Type'] = content_type response.body = self._get_file_content( file_path=normalized_file_path) response.headers['Last-Modified'] = format_date_time(file_mtime) response.headers['ETag'] = repr(file_mtime) return response
def get_one(self, ref_or_id, file_path, requester_user, if_none_match=None, if_modified_since=None): """ Outputs the content of a specific file in a pack. Handles requests: GET /packs/views/file/<pack_ref_or_id>/<file path> """ pack_db = self._get_by_ref_or_id(ref_or_id=ref_or_id) if not pack_db: msg = 'Pack with ref_or_id "%s" does not exist' % (ref_or_id) raise StackStormDBObjectNotFoundError(msg) if not file_path: raise ValueError('Missing file path') pack_ref = pack_db.ref # Note: Until list filtering is in place we don't require RBAC check for icon file permission_type = PermissionType.PACK_VIEW if file_path not in WHITELISTED_FILE_PATHS: rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=pack_db, permission_type=permission_type) normalized_file_path = get_pack_file_abs_path(pack_ref=pack_ref, file_path=file_path) if not normalized_file_path or not os.path.isfile(normalized_file_path): # Ignore references to files which don't exist on disk raise StackStormDBObjectNotFoundError('File "%s" not found' % (file_path)) file_size, file_mtime = self._get_file_stats(file_path=normalized_file_path) response = Response() if not self._is_file_changed(file_mtime, if_none_match=if_none_match, if_modified_since=if_modified_since): response.status = http_client.NOT_MODIFIED else: if file_size is not None and file_size > MAX_FILE_SIZE: msg = ('File %s exceeds maximum allowed file size (%s bytes)' % (file_path, MAX_FILE_SIZE)) raise ValueError(msg) content_type = mimetypes.guess_type(normalized_file_path)[0] or \ 'application/octet-stream' response.headers['Content-Type'] = content_type response.body = self._get_file_content(file_path=normalized_file_path) response.headers['Last-Modified'] = format_date_time(file_mtime) response.headers['ETag'] = repr(file_mtime) return response
def get(self, id, requester_user, download=False, compress=False, pretty_format=False): """ Retrieve raw action execution result object as a JSON string or optionally force result download as a (compressed) file. This is primarily to be used in scenarios where executions contain large results and JSON loading and parsing it can be slow (e.g. in the st2web) and we just want to display raw result. :param compress: True to compress the response using gzip (may come handy for executions with large results). :param download: True to force downloading result to a file. :param pretty_format: True to pretty format returned JSON data - this adds quite some overhead compared to the default behavior where we don't pretty format the result. Handles requests: GET /executions/<id>/result[?download=1][&compress=1] TODO: Maybe we should also support pre-signed URLs for sharing externally with other people? It of course won't contain all the exection related data, but just sharing the result can come handy in many situations. :rtype: ``str`` """ # NOTE: Here we intentionally use as_pymongo() to avoid mongoengine layer even for old style # data try: result = (self.access.impl.model.objects.filter( id=id).only("result").as_pymongo()[0]) except IndexError: raise NotFoundException("Execution with id %s not found" % (id)) if isinstance(result["result"], dict): # For backward compatibility we also support old non JSON field storage format if pretty_format: response_body = orjson.dumps(result["result"], option=orjson.OPT_INDENT_2) else: response_body = orjson.dumps(result["result"]) else: # For new JSON storage format we just use raw value since it's already JSON serialized # string response_body = result["result"] if pretty_format: # Pretty format is not a default behavior since it adds quite some overhead (e.g. # 10-30ms for non pretty format for 4 MB json vs ~120 ms for pretty formatted) response_body = orjson.dumps(orjson.loads(result["result"]), option=orjson.OPT_INDENT_2) response = Response() response.headers["Content-Type"] = "text/json" if download: filename = "execution_%s_result.json" % (id) if compress: filename += ".gz" response.headers[ "Content-Disposition"] = "attachment; filename=%s" % (filename) if compress: response.headers["Content-Type"] = "application/x-gzip" response.headers["Content-Encoding"] = "gzip" response_body = gzip.compress(response_body) response.body = response_body return response