示例#1
0
    def test_non_idempotent_uses_POST(self):
        # If a function is declared as not idempotent the export signature
        # includes the HTTP POST method.
        def func():
            pass

        self.assertEqual(("POST", func.__name__),
                         operation(idempotent=False)(func).export)
示例#2
0
    def test_idempotent_uses_GET(self):
        # If a function is declared as idempotent the export signature
        # includes the HTTP GET method.
        def func():
            pass

        self.assertEqual(("GET", func.__name__),
                         operation(idempotent=True)(func).export)
示例#3
0
    def testexported_as_is_optional(self):
        # If exported_as is not passed then we expect the function to be
        # exported in the API using the actual function name itself.

        def exported_function():
            pass

        decorate = operation(idempotent=True)
        decorated = decorate(exported_function)
        self.assertEqual("exported_function", decorated.export[1])
示例#4
0
class AnonFilesHandler(AnonymousOperationsHandler):
    """Anonymous file operations.

    This is needed for Juju. The story goes something like this:

    - The Juju provider will upload a file using an "unguessable" name.

    - The name of this file (or its URL) will be shared with all the agents in
      the environment. They cannot modify the file, but they can access it
      without credentials.

    """
    create = read = update = delete = None

    get_by_key = operation(
        idempotent=True, exported_as='get_by_key')(get_file_by_key)

    @classmethod
    def resource_uri(cls, *args, **kwargs):
        return ('files_handler', [])
示例#5
0
class FilesHandler(OperationsHandler):
    """Manage the collection of all the files in this MAAS."""
    api_doc_section_name = "Files"
    update = None
    anonymous = AnonFilesHandler

    get_by_name = operation(
        idempotent=True, exported_as='get')(get_file_by_name)
    get_by_key = operation(
        idempotent=True, exported_as='get_by_key')(get_file_by_key)

    def create(self, request):
        """Add a new file to the file storage.

        :param filename: The file name to use in the storage.
        :type filename: string
        :param file: Actual file data with content type
            application/octet-stream

        Returns 400 if any of these conditions apply:
         - The filename is missing from the parameters
         - The file data is missing
         - More than one file is supplied
        """
        filename = request.data.get("filename", None)
        if not filename:
            raise MAASAPIBadRequest("Filename not supplied")
        files = request.FILES
        if not files:
            raise MAASAPIBadRequest("File not supplied")
        if len(files) != 1:
            raise MAASAPIBadRequest("Exactly one file must be supplied")
        uploaded_file = files['file']

        # As per the comment in FileStorage, this ought to deal in
        # chunks instead of reading the file into memory, but large
        # files are not expected.
        FileStorage.objects.save_file(filename, uploaded_file, request.user)
        return HttpResponse('', status=int(http.client.CREATED))

    def read(self, request):
        """List the files from the file storage.

        The returned files are ordered by file name and the content is
        excluded.

        :param prefix: Optional prefix used to filter out the returned files.
        :type prefix: string
        """
        prefix = request.GET.get("prefix", None)
        files = FileStorage.objects.filter(owner=request.user)
        if prefix is not None:
            files = files.filter(filename__startswith=prefix)
        files = files.order_by('filename')
        return files

    def delete(self, request, **kwargs):
        """Delete a FileStorage object.

        :param filename: The filename of the object to be deleted.
        :type filename: unicode
        """
        # It is valid for a path in a POST, PUT, or DELETE (or any other HTTP
        # method) to contain a query string. However, Django only makes
        # parameters from the query string available in the badly named
        # request.GET object.
        filename = get_mandatory_param(request.GET, 'filename')
        stored_file = get_object_or_404(
            FileStorage, filename=filename, owner=request.user)
        stored_file.delete()
        return rc.DELETED

    @classmethod
    def resource_uri(cls, *args, **kwargs):
        return ('files_handler', [])
示例#6
0
 def test_can_passexported_as(self):
     # Test that passing the optional "exported_as" works as expected.
     randomexported_name = factory.make_name("exportedas", sep="")
     decorate = operation(idempotent=False, exported_as=randomexported_name)
     decorated = decorate(lambda: None)
     self.assertEqual(randomexported_name, decorated.export[1])
示例#7
0
 def test_valid_decoration(self):
     value = "value" + factory.make_string()
     decorate = operation(idempotent=False)
     decorated = decorate(lambda: value)
     self.assertEqual(value, decorated())
示例#8
0
class FilesHandler(OperationsHandler):
    """Manage the collection of all the files in this MAAS."""

    api_doc_section_name = "Files"
    update = None
    anonymous = AnonFilesHandler

    get_by_name = operation(idempotent=True,
                            exported_as="get")(get_file_by_name)
    get_by_key = operation(idempotent=True,
                           exported_as="get_by_key")(get_file_by_key)

    def create(self, request):
        """@description-title Add a new file
        @description Add a new file to the file storage.

        @param (string) "filename" [required=true] The file name to use in
        storage.

        @param (string) "file" [required=true] File data. Content type must be
        ``application/octet-stream``.

        @success (http-status-code) "server-success" 200
        @success (json) "success-json" A JSON object containing the new file.
        @success-example "success-json" [exkey=files-placeholder] placeholder
        text

        @error (http-status-code) "400" 400
        @error (content) "arg-prob" The filename is missing, the file data is
        missing or more than one file is supplied.
        """
        filename = request.data.get("filename", None)
        if not filename:
            raise MAASAPIBadRequest("Filename not supplied")
        files = request.FILES
        if not files:
            raise MAASAPIBadRequest("File not supplied")
        if len(files) != 1:
            raise MAASAPIBadRequest("Exactly one file must be supplied")
        uploaded_file = files["file"]

        # As per the comment in FileStorage, this ought to deal in
        # chunks instead of reading the file into memory, but large
        # files are not expected.
        FileStorage.objects.save_file(filename, uploaded_file, request.user)
        return HttpResponse("", status=int(http.client.CREATED))

    def read(self, request):
        """@description-title List files
        @description List the files from the file storage.

        The returned files are ordered by file name and the content is
        excluded.

        @param (string) "prefix" [required=false] Prefix used to filter
        returned files.

        @success (http-status-code) "server-success" 200
        @success (json) "success-json" A JSON object containing a list of the
        reqeusted file names.
        @success-example "success-json" [exkey=files-placeholder] placeholder
        text
        """
        prefix = request.GET.get("prefix", None)
        files = FileStorage.objects.filter(owner=request.user)
        if prefix is not None:
            files = files.filter(filename__startswith=prefix)
        files = files.order_by("filename")
        return files

    def delete(self, request, **kwargs):
        """@description-title Delete a file
        @description Delete a stored file.

        @param (string) "filename" [required=true] The filename of the object
        to be deleted.

        @success (http-status-code) "server-success" 204

        @error (http-status-code) "404" 404
        @error (content) "not-found" The requested file is not found.
        @error-example "not-found"
            Not Found
        """
        # It is valid for a path in a POST, PUT, or DELETE (or any other HTTP
        # method) to contain a query string. However, Django only makes
        # parameters from the query string available in the badly named
        # request.GET object.
        filename = get_mandatory_param(request.GET, "filename")
        stored_file = get_object_or_404(FileStorage,
                                        filename=filename,
                                        owner=request.user)
        stored_file.delete()
        return rc.DELETED

    @classmethod
    def resource_uri(cls, *args, **kwargs):
        return ("files_handler", [])