Esempio n. 1
0
class ApiView(View):
    """Base class extended by ObjectView and ObjectsView."""

    # urls extended by subclasses to add urls to marshalled objects
    urls = {}

    @method_decorator(login_required(useragent="OMERO.webapi"))
    @method_decorator(json_response())
    def dispatch(self, *args, **kwargs):
        """Wrap other methods to add decorators."""
        return super(ApiView, self).dispatch(*args, **kwargs)

    def add_data(self, marshalled, request, conn, urls=None, **kwargs):
        """
        Post-process marshalled object to add any extra data.

        Used to add urls to marshalled json.
        Subclasses can configure self.urls to specify urls to add.
        See ProjectsView urls as example
        """
        object_id = marshalled["@id"]
        version = kwargs["api_version"]
        if urls is not None:
            for key, args in urls.items():
                name = args["name"]
                kwargs = args["kwargs"].copy()
                # If kwargs has 'OBJECT_ID' placeholder, we replace with id
                for k, v in kwargs.items():
                    if v == "OBJECT_ID":
                        kwargs[k] = object_id
                url = build_url(request, name, version, **kwargs)
                marshalled[key] = url
        return marshalled
Esempio n. 2
0
class SaveView(View):
    """
    This view provides 'Save' functionality for all types of objects.

    POST to create a new Object and PUT to replace existing one.
    """

    CAN_PUT = ["Project", "Dataset", "Screen"]

    CAN_POST = ["Project", "Dataset", "Screen"]

    @method_decorator(login_required(useragent="OMERO.webapi"))
    @method_decorator(json_response())
    def dispatch(self, *args, **kwargs):
        """Apply decorators for class methods below."""
        return super(SaveView, self).dispatch(*args, **kwargs)

    def get_type_name(self, marshalled):
        """Get the '@type' name from marshalled data."""
        if "@type" not in marshalled:
            raise BadRequestError("Need to specify @type attribute")
        schema_type = marshalled["@type"]
        if "#" not in schema_type:
            return None
        return schema_type.split("#")[1]

    def put(self, request, conn=None, **kwargs):
        """
        PUT handles saving of existing objects.

        Therefore '@id' should be set.
        """
        object_json = json.loads(request.body)
        obj_type = self.get_type_name(object_json)
        if obj_type not in self.CAN_PUT:
            raise MethodNotSupportedError("Update of %s not supported" %
                                          obj_type)
        if "@id" not in object_json:
            raise BadRequestError(
                "No '@id' attribute. Use POST to create new objects")
        return self._save_object(request, conn, object_json, **kwargs)

    def post(self, request, conn=None, **kwargs):
        """
        POST handles saving of NEW objects.

        Therefore '@id' should not be set.
        """
        object_json = json.loads(request.body)
        obj_type = self.get_type_name(object_json)
        if obj_type not in self.CAN_POST:
            raise MethodNotSupportedError("Creation of %s not supported" %
                                          obj_type)
        if "@id" in object_json:
            raise BadRequestError(
                "Object has '@id' attribute. Use PUT to update objects")
        rsp = self._save_object(request, conn, object_json, **kwargs)
        # will return 201 ('Created')
        raise CreatedObject(rsp)

    def _save_object(self, request, conn, object_json, **kwargs):
        """Here we handle the saving for PUT and POST."""
        # Try to get group from request, OR from details below...
        group = getIntOrDefault(request, "group", None)
        decoder = None
        objType = object_json["@type"]
        decoder = get_decoder(objType)
        # If we are passed incomplete object, or decoder couldn't be found...
        if decoder is None:
            raise BadRequestError("No decoder found for type: %s" % objType)

        # Any marshal errors most likely due to invalid input. status=400
        try:
            obj = decoder.decode(object_json)
        except Exception:
            msg = "Error in decode of json data by omero_marshal"
            raise BadRequestError(msg, traceback.format_exc())

        if group is None:
            try:
                # group might be None or unloaded
                group = obj.getDetails().group.id.val
            except AttributeError:
                # Instead of default stack trace, give nicer message:
                msg = "Specify Group in omero:details or " "query parameters ?group=:id"
                raise BadRequestError(msg)

        # If owner was unloaded (E.g. from get() above) or if missing
        # ome.model.meta.Experimenter.ldap (not supported by omero_marshal)
        # then saveObject() will give ValidationException.
        # Therefore we ignore any details for now:
        obj.unloadDetails()

        # TODO: Unlink children for Projects, Datasets and Screens to avoid
        # breaking links. See PR #4930
        if hasattr(obj, "unloadDatasetLinks"):
            obj.unloadDatasetLinks()
        if hasattr(obj, "unloadImageLinks"):
            obj.unloadImageLinks()
        if hasattr(obj, "unloadPlateLinks"):
            obj.unloadPlateLinks()

        conn.SERVICE_OPTS.setOmeroGroup(group)
        obj = conn.getUpdateService().saveAndReturnObject(
            obj, conn.SERVICE_OPTS)
        encoder = get_encoder(obj.__class__)
        return {"data": encoder.encode(obj)}