Example #1
0
def root(request, format=None, **kwargs):
    """
    The documentation for the Open Science Framework API can be found at [developer.osf.io](https://developer.osf.io).
    """
    if request.user and not request.user.is_anonymous:
        user = request.user
        current_user = UserSerializer(user, context={'request': request}).data
    else:
        current_user = None
    kwargs = request.parser_context['kwargs']
    return_val = {
        'meta': {
            'message': 'Welcome to the OSF API.',
            'version': request.version,
            'current_user': current_user,
        },
        'links': {
            'nodes': utils.absolute_reverse('nodes:node-list', kwargs=kwargs),
            'users': utils.absolute_reverse('users:user-list', kwargs=kwargs),
            'collections': utils.absolute_reverse('collections:collection-list', kwargs=kwargs),
            'registrations': utils.absolute_reverse('registrations:registration-list', kwargs=kwargs),
            'institutions': utils.absolute_reverse('institutions:institution-list', kwargs=kwargs),
            'licenses': utils.absolute_reverse('licenses:license-list', kwargs=kwargs),
            'metaschemas': utils.absolute_reverse('metaschemas:metaschema-list', kwargs=kwargs),
            'addons': utils.absolute_reverse('addons:addon-list', kwargs=kwargs),
        }
    }

    if utils.has_admin_scope(request):
        return_val['meta']['admin'] = True

    return Response(return_val)
Example #2
0
def root(request, format=None, **kwargs):
    """
    The documentation for the Open Science Framework API can be found at [developer.osf.io](https://developer.osf.io).
    The contents of this endpoint are variable and subject to change without notification.
    """
    if request.user and not request.user.is_anonymous:
        user = request.user
        current_user = UserSerializer(user, context={'request': request}).data
    else:
        current_user = None
    flags = [name for name in Flag.objects.values_list('name', flat=True) if flag_is_active(request, name)]
    kwargs = request.parser_context['kwargs']
    return_val = {
        'meta': {
            'message': 'Welcome to the OSF API.',
            'version': request.version,
            'current_user': current_user,
            'active_flags': flags,
        },
        'links': {
            'nodes': utils.absolute_reverse('nodes:node-list', kwargs=kwargs),
            'users': utils.absolute_reverse('users:user-list', kwargs=kwargs),
            'collections': utils.absolute_reverse('collections:collection-list', kwargs=kwargs),
            'registrations': utils.absolute_reverse('registrations:registration-list', kwargs=kwargs),
            'institutions': utils.absolute_reverse('institutions:institution-list', kwargs=kwargs),
            'licenses': utils.absolute_reverse('licenses:license-list', kwargs=kwargs),
            'schemas': utils.absolute_reverse('schemas:registration-schema-list', kwargs=kwargs),
            'addons': utils.absolute_reverse('addons:addon-list', kwargs=kwargs),
        },
    }

    if utils.has_admin_scope(request):
        return_val['meta']['admin'] = True

    return Response(return_val)
Example #3
0
 def get_absolute_url(self, obj):
     return absolute_reverse(
         'logs:log-detail',
         kwargs={
             'log_id': obj._id,
         }
     )
Example #4
0
 def self_url(self, obj):
     return absolute_reverse(
         'identifiers:identifier-detail', kwargs={
             'identifier_id': obj._id,
             'version': self.context['request'].parser_context['kwargs']['version'],
         },
     )
Example #5
0
 def get_self_url(self, obj):
     return absolute_reverse(
         'view-only-links:view-only-link-nodes',
         kwargs={
             'link_id': obj['self']._id
         }
     )
Example #6
0
def serialize_draft_registration(draft, auth=None):
    from website.project.utils import serialize_node  # noqa
    from api.base.utils import absolute_reverse

    node = draft.branched_from

    return {
        'pk': draft._id,
        'branched_from': serialize_node(node, auth),
        'initiator': serialize_initiator(draft.initiator),
        'registration_metadata': draft.registration_metadata,
        'registration_schema': serialize_meta_schema(draft.registration_schema),
        'initiated': utils.iso8601format(draft.datetime_initiated),
        'updated': utils.iso8601format(draft.datetime_updated),
        'flags': draft.flags,
        'urls': {
            'edit': node.web_url_for('edit_draft_registration_page', draft_id=draft._id, _guid=True),
            'submit': node.api_url_for('submit_draft_for_review', draft_id=draft._id),
            'before_register': node.api_url_for('project_before_register'),
            'register': absolute_reverse('nodes:node-registrations', kwargs={'node_id': node._id, 'version': 'v2'}),
            'register_page': node.web_url_for('draft_before_register_page', draft_id=draft._id, _guid=True),
            'registrations': node.web_url_for('node_registrations', _guid=True)
        },
        'requires_approval': draft.requires_approval,
        'is_pending_approval': draft.is_pending_review,
        'is_approved': draft.is_approved,
    }
 def get_wiki_content(self, obj):
     return absolute_reverse(
         'wikis:wiki-content', kwargs={
             'wiki_id': obj._id,
             'version': self.context['request'].parser_context['kwargs']['version'],
         },
     )
Example #8
0
 def get_absolute_url(self, obj):
     return absolute_reverse(
         'nodes:node-view-only-link-detail',
         kwargs={
             'link_id': obj._id
         }
     )
 def get_absolute_url(self, obj):
     return absolute_reverse(
         'users:user-detail', kwargs={
             'user_id': obj._id,
             'version': self.context['request'].parser_context['kwargs']['version'],
         },
     )
 def reset_url(self, obj):
     return absolute_reverse(
         'applications:application-reset', kwargs={
             'client_id': obj.client_id,
             'version': self.context['request'].parser_context['kwargs']['version'],
         },
     )
 def get_preprints_url(self, obj):
     return absolute_reverse(
         'providers:preprint-providers:preprints-list', kwargs={
             'provider_id': obj._id,
             'version': self.context['request'].parser_context['kwargs']['version'],
         },
     )
 def get_related_url(self, obj):
     return absolute_reverse(
         'users:user-institutions', kwargs={
             'user_id': obj['self']._id,
             'version': self.context['request'].parser_context['kwargs']['version'],
         },
     )
Example #13
0
 def get_absolute_url(self, obj):
     return absolute_reverse(
         view_name='search:search-search',
         kwargs={
             'version': self.context['request'].parser_context['kwargs']['version']
         }
     )
 def get_registration_url(self, obj):
     return absolute_reverse(
         'registrations:registration-detail', kwargs={
             'node_id': obj._id,
             'version': self.context['request'].parser_context['kwargs']['version'],
         },
     )
 def get_export_link(self, obj):
     return absolute_reverse(
         'users:user-account-export',
         kwargs={
             'user_id': obj._id,
             'version': self.context['request'].parser_context['kwargs']['version'],
         },
     )
Example #16
0
 def get_absolute_url(self, obj):
     return absolute_reverse(
         'nodes:node-view-only-link-detail',
         kwargs={
             'link_id': obj._id,
             'version': self.context['request'].parser_context['kwargs']['version']
         }
     )
Example #17
0
 def get_self_url(self, obj):
     return absolute_reverse(
         'view-only-links:view-only-link-nodes',
         kwargs={
             'link_id': obj['self']._id,
             'version': self.context['request'].parser_context['kwargs']['version']
         }
     )
Example #18
0
 def reset_url(self, obj):
     return absolute_reverse(
         "applications:application-reset",
         kwargs={
             "client_id": obj["client_id"],
             "version": self.context["request"].parser_context["kwargs"]["version"],
         },
     )
 def get_download_link(self, obj):
     return absolute_reverse(
         'files:metadata-record-download', kwargs={
             'file_id': obj.file._id,
             'record_id': obj._id,
             'version': self.context['request'].parser_context['kwargs']['version'],
         },
     )
Example #20
0
 def get_absolute_url(self, obj):
     kwargs = self.context['request'].parser_context['kwargs']
     kwargs.update({'account_id': obj._id})
     return absolute_reverse(
         'users:user-external_account-detail',
         kwargs=kwargs
     )
     return obj.get_absolute_url()
Example #21
0
 def get_absolute_url(self, obj):
     return absolute_reverse(
         'nodes:node-provider-detail',
         kwargs={
             'node_id': obj.node._id,
             'provider': obj.provider
         }
     )
Example #22
0
 def get_url(self, obj, view_name, request, format):
     if obj is None:
         return {}
     lookup_value = getattr(obj, self.lookup_field)
     return absolute_reverse(self.view_name, kwargs={
         self.lookup_url_kwarg: lookup_value,
         'version': self.context['request'].parser_context['kwargs']['version']
     })
 def self_url(self, obj):
     return absolute_reverse(
         'wikis:wiki-version-detail', kwargs={
             'version_id': obj.identifier,
             'wiki_id': obj.wiki_page._id,
             'version': self.context['request'].parser_context['kwargs']['version'],
         },
     )
Example #24
0
    def resolve_url(self, obj):
        kwarg_values = {key: _get_attr_from_tpl(attr_tpl, obj) for key, attr_tpl in self.kwargs.items()}
        arg_values = [_get_attr_from_tpl(attr_tpl, obj) for attr_tpl in self.args]
        query_kwarg_values = {key: _get_attr_from_tpl(attr_tpl, obj) for key, attr_tpl in self.query_kwargs.items()}

        return absolute_reverse(
            self.endpoint, args=arg_values, kwargs=kwarg_values, query_kwargs=query_kwarg_values, **self.reverse_kwargs
        )
Example #25
0
 def self_url(self, obj):
     return absolute_reverse(
         'files:version-detail', kwargs={
             'version_id': obj.identifier,
             'file_id': self.context['view'].kwargs['file_id'],
             'version': self.context['request'].parser_context['kwargs']['version'],
         },
     )
Example #26
0
 def get_absolute_url(self, obj):
     node_id = self.context['request'].parser_context['kwargs']['node_id']
     return absolute_reverse(
         'nodes:node-pointer-detail',
         kwargs={
             'node_id': node_id,
             'node_link_id': obj._id
         }
     )
Example #27
0
 def get_absolute_url(self, obj):
     comment_id = self.context['request'].parser_context['kwargs']['comment_id']
     return absolute_reverse(
         'comments:report-detail',
         kwargs={
             'comment_id': comment_id,
             'user_id': obj._id
         }
     )
Example #28
0
 def get_absolute_url(self, obj):
     return absolute_reverse(
         'registrations:registration-contributor-detail',
         kwargs={
             'user_id': obj.user._id,
             'node_id': self.context['request'].parser_context['kwargs']['node_id'],
             'version': self.context['request'].parser_context['kwargs']['version']
         }
     )
Example #29
0
 def self_url(self, obj):
     return absolute_reverse(
         "files:version-detail",
         kwargs={
             "version_id": obj.identifier,
             "file_id": self.context["view"].kwargs["file_id"],
             "version": self.context["request"].parser_context["kwargs"]["version"],
         },
     )
 def get_absolute_url(self, obj):
     return absolute_reverse(
         'users:user-addon-detail',
         kwargs={
             'provider': obj.config.short_name,
             'user_id': self.context['request'].parser_context['kwargs']['user_id'],
             'version': self.context['request'].parser_context['kwargs']['version'],
         },
     )
Example #31
0
    def perform_create(self, serializer):
        target = serializer.validated_data['target']
        self.check_object_permissions(self.request, target)

        if not target.provider.is_reviewed:
            raise Conflict(
                '{} is an unmoderated provider. If you are an admin, set up moderation by setting `reviews_workflow` at {}'
                .format(
                    target.provider.name,
                    absolute_reverse(
                        'providers:preprint-providers:preprint-provider-detail',
                        kwargs={
                            'provider_id':
                            target.provider._id,
                            'version':
                            self.request.parser_context['kwargs']['version'],
                        },
                    ),
                ))

        serializer.save(user=self.request.user)
Example #32
0
 def resolve_url(self, obj):
     kwarg_values = {
         key: _get_attr_from_tpl(attr_tpl, obj)
         for key, attr_tpl in self.kwargs.items()
     }
     arg_values = [
         _get_attr_from_tpl(attr_tpl, obj) for attr_tpl in self.args
     ]
     query_kwarg_values = {
         key: _get_attr_from_tpl(attr_tpl, obj)
         for key, attr_tpl in self.query_kwargs.items()
     }
     # Presumably, if you have are expecting a value but the value is empty, then the link is invalid.
     for item in kwarg_values:
         if kwarg_values[item] is None:
             return None
     return utils.absolute_reverse(self.endpoint,
                                   args=arg_values,
                                   kwargs=kwarg_values,
                                   query_kwargs=query_kwarg_values,
                                   **self.reverse_kwargs)
Example #33
0
    def reverse(self,
                viewname,
                args=None,
                kwargs=None,
                request=None,
                format=None,
                **extra):
        url_path_version = self.get_url_path_version(kwargs)
        query_parameter_version = self.get_query_param_version(request)

        kwargs = {} if (kwargs is None) else kwargs
        kwargs[self.version_param] = decimal_version_to_url_path(
            url_path_version)
        query_kwargs = {
            'version': query_parameter_version
        } if query_parameter_version else None

        return utils.absolute_reverse(viewname,
                                      query_kwargs=query_kwargs,
                                      args=args,
                                      kwargs=kwargs)
def serialize_draft_registration(draft, auth=None):
    from website.project.utils import serialize_node  # noqa
    from api.base.utils import absolute_reverse

    node = draft.branched_from

    return {
        'pk': draft._id,
        'branched_from': serialize_node(node, auth),
        'initiator': serialize_initiator(draft.initiator),
        'registration_metadata': draft.registration_metadata,
        'registration_schema':
        serialize_meta_schema(draft.registration_schema),
        'initiated': utils.iso8601format(draft.datetime_initiated),
        'updated': utils.iso8601format(draft.datetime_updated),
        'flags': draft.flags,
        'urls': {
            'edit':
            node.web_url_for('edit_draft_registration_page',
                             draft_id=draft._id,
                             _guid=True),
            'before_register':
            node.api_url_for('project_before_register'),
            'register':
            absolute_reverse('nodes:node-registrations',
                             kwargs={
                                 'node_id': node._id,
                                 'version': 'v2'
                             }),
            'register_page':
            node.web_url_for('draft_before_register_page',
                             draft_id=draft._id,
                             _guid=True),
            'registrations':
            node.web_url_for('node_registrations', _guid=True)
        },
        'requires_approval': draft.requires_approval,
        'is_pending_approval': draft.is_pending_review,
        'is_approved': draft.is_approved,
    }
Example #35
0
    def reverse(self,
                viewname,
                args=None,
                kwargs=None,
                request=None,
                format=None,
                **extra):
        """ Overrides BaseVersioning.reverse to maybe ignore 'version' arg

        Requests to private views in the '_' namespace don't have any version associated
        with them. Related fields in their serializers that point to views in a versioned
        namespace require a version kwarg to be reversed correctly. This first tries the
        reverse with one, then without if that fails.
        """
        try:
            return super(PrivateVersioning, self).reverse(viewname,
                                                          args=args,
                                                          kwargs=kwargs,
                                                          request=request,
                                                          format=format,
                                                          **extra)
        except NoReverseMatch:
            kwargs = kwargs or {}
            if kwargs.get('version', False):
                kwargs.pop('version')
                return utils.absolute_reverse(
                    viewname,
                    query_kwargs=None,
                    args=args,
                    kwargs=kwargs,
                )
            kwargs['version'] = get_latest_sub_version('2')
            return super(PrivateVersioning, self).reverse(viewname,
                                                          args=args,
                                                          kwargs=kwargs,
                                                          request=request,
                                                          format=format,
                                                          **extra)
Example #36
0
 def get_absolute_url(self, obj):
     return absolute_reverse('meetings:meeting-detail', kwargs={'meeting_id': obj.endpoint})
Example #37
0
def root(request, format=None, **kwargs):
    """Welcome to the V2 Open Science Framework API. With this API you can access users, projects, components, logs, and files
    from the [Open Science Framework](https://osf.io/). The Open Science Framework (OSF) is a free, open-source service
    maintained by the [Center for Open Science](http://cos.io/).

    The OSF serves as a repository and archive for study designs, materials, data, manuscripts, or anything else
    associated with your research during the research process. Every project and file on the OSF has a permanent unique
    identifier, and every registration (a permanent, time-stamped version of your projects and files) can be assigned a
    DOI/ARK. You can use the OSF to measure your impact by monitoring the traffic to projects and files you make
    public. With the OSF you have full control of what parts of your research are public and what remains private.

    Beta notice: This API is currently a beta service.  You are encouraged to use the API and will receive support
    when doing so, however, while the API remains in beta status, it may change without notice as a result of
    product updates. The temporary beta status of the API will remain in place while it matures. In a future
    release, the beta status will be removed, at which point we will provide details on how long we will support
    the API V2 and under what circumstances it might change.

    #General API Usage

    The OSF API generally conforms to the [JSON-API v1.0 spec](http://jsonapi.org/format/1.0/).  Where exceptions
    exist, they will be noted.  Each endpoint will have its own documentation, but there are some general principles.

    Assume undocumented routes/features/fields are unstable.

    ##Requests

    ###Canonical URLs

    All canonical URLs have trailing slashes.  A request to an endpoint without a trailing slash will result in a 301
    redirect to the canonical URL.  There are some exceptions when working with the Files API, so if a URL in a response
    does not have a slash, do not append one.

    ###Plurals

    Endpoints are always pluralized.  `/users/`, not `/user/`, `/nodes/`, not `/node/`.

    ###Common Actions

    Every endpoint in the OSF API responds to `GET`, `HEAD`, and `OPTION` requests.  You must have adequate permissions
    to interact with the endpoint.  Unauthorized use will result in 401 Unauthorized or 403 Forbidden responses.  Use
    `HEAD` to probe an endpoint and make sure your headers are well-formed.  `GET` will return a representation of the
    entity or entity collection referenced by the endpoint.  An `OPTIONS` request will return a JSON object that describes the
    endpoint, including the name, a description, the acceptable request formats, the allowed response formats, and any
    actions available via the endpoint.

    ###Versioning
    Versioning can be specified in three different ways:

    1. URL Path Versioning, e.g. `/v2/` or `/v3/`

        + A version specified via the URL path is a **required** part of the URL.

        + Only a major version can be specified via the URL path, i.e. `/v2.0.6/` is invalid,
        additionally, paths such as `/v2.0/` are invalid.

        + If the default version of the API is within the major version specified in the URL path,
        the default version will be applied (i.e. if the default version is `2.3` and the URL path is `/v2/`,
        then version returned will be `2.3`).

        + If the default version of the API is not within the major version specified in the URL path,
        the URL path version will be applied (i.e. if the default version is `3.0` and the URL path is `/v2/`,
        then the version returned will be `2.0`)

    2. Query Parameter Versioning, e.g. `/v2/nodes/?version=2.1.6`

        + Pinning to a specific version via a query parameter is **optional**.

        + A specific version (major, minor, or patch) for a single request can be specified via the `version`
        query parameter, as long as it is an allowed version.

        + If the version specified in the query parameter does not fall within the same major version
         specified in the URL path, i.e `/v2/nodes/?version=3.1.4` a `409 Conflict` response will be returned.

    3.  Header Versioning, e.g. `Accept-Header=application/vnd.api+json;version=3.0.1`

        + Pinning to a specific version via request header is **optional**.

        + A specific version (major, minor, or patch) for a single request can be specified
         via the `Accept Header` of the request, as long as it is an allowed version.

        + If the version specified in the header does not fall within the same major version specified
         in the URL path a `409 Conflict` response will be returned.

        + If both a header version and query parameter version are specified, the versions must match exactly
          or a `409 Conflict` response will be returned (i.e. one does not take precedence over the other).

    ###Filtering

    Entity collections can be filtered by adding a query parameter in the form:

        filter[<fieldname>]=<matching information>

    String queries are filtered using substring matching. For example, if you were trying to find [Lise
    Meitner](http://en.wikipedia.org/wiki/Lise_Meitner):

        /users/?filter[full_name]=meitn

    You can filter on multiple fields, or the same field in different ways, by &-ing the query parameters together.

        /users/?filter[full_name]=lise&filter[family_name]=mei

    Boolean fields should be queried with `true` or `false`.

        /nodes/?filter[registered]=true

    You can request multiple resources by filtering on id and placing comma-separated values in your query parameter.

        /nodes/?filter[id]=aegu6,me23a

    You can filter with case-sensitivity or case-insensitivity by using `contains` and `icontains`, respectively.

        /nodes/?filter[tags][icontains]=help

    ###Embedding

    All related resources that appear in the `relationships` attribute are embeddable, meaning that
    by adding a query parameter like:

        /nodes/?embed=contributors

    it is possible to fetch a Node and its contributors in a single request. The embedded results will have the following
    structure:

        {relationship_name}: {full_embedded_response}

    Where `full_embedded_response` means the full API response resulting from a GET request to the `href` link of the
    corresponding related resource. This means if there are no errors in processing the embedded request the response will have
    the format:

        data: {response}

    And if there are errors processing the embedded request the response will have the format:

        errors: {errors}

    Multiple embeds can be achieved with multiple query parameters separated by "&".

        /nodes/?embed=contributors&embed=comments

    Some endpoints are automatically embedded.

    ###Pagination

    All entity collection endpoints respond to the `page` query parameter behavior as described in the [JSON-API
    pagination spec](http://jsonapi.org/format/1.0/#crud).  However, pagination links are provided in the response, and
    you are encouraged to use that rather than adding query parameters by hand.

    ###Formatting POST/PUT/PATCH request bodies

    The OSF API follows the JSON-API spec for [create and update requests](http://jsonapi.org/format/1.0/#crud).  This means
    all request bodies must be wrapped with some metadata.  Each request body must be an object with a `data` key
    containing at least a `type` member.  The value of the `type` member must agree with the `type` of the entities
    represented by the endpoint.  If not, a 409 Conflict will be returned.  The request should also contain an
    `attributes` member with an object containing the key-value pairs to be created/updated.  PUT/PATCH requests must
    also have an `id` key that matches the id part of the endpoint.  If the `id` key does not match the id path part, a
    409 Conflict error will be returned.

    ####Example 1: Creating a Node via POST

        POST /v2/nodes/
        {
          "data": {
            "type": "nodes",
            "attributes": {
              "title" : "A Phylogenetic Tree of Famous Internet Cats",
              "category" : "project",
              "description" : "How closely related are Grumpy Cat and C.H. Cheezburger? Is memefulness inheritable?"
            }
          }
        }

    ####Example 2: Updating a User via PUT

        PUT /v2/users/me/
        {
          "data": {
            "id": "3rqxc",
            "type": "users",
            "attributes": {
              "full_name" : "Henrietta Swan Leavitt",
              "given_name" : "Henrietta",
              "middle_names" : "Swan",
              "family_name" : "Leavitt"
            }
          }
        }

    **NB:** If you PUT/PATCH to the `/users/me/` endpoint, you must still provide your full user id in the `id` field of
    the request.  We do not support using the `me` alias in request bodies at this time.

    ###PUT vs. PATCH

    For most endpoints that support updates via PUT requests, we also allow PATCH updates. The only difference is that
    PUT requests require all mandatory attributes to be set, even if their value is unchanged. PATCH requests may omit
    mandatory attributes, whose value will be unchanged.

    ###Attribute Validation

    Endpoints that allow creation or modification of entities generally limit updates to certain attributes of the
    entity.  If you attempt to set an attribute that does not permit updates (such as a `date_created` timestamp), the
    API will silently ignore that attribute.  This will not affect the response from the API: if the request would have
    succeeded without the updated attribute, it will still report as successful.  Likewise, if the request would have
    failed without the attribute update, the API will still report a failure.

    Typoed or non-existent attributes will behave the same as non-updatable attributes and be silently ignored. If a
    request is not working the way you expect, make sure to double check your spelling.

    ##Responses

    ###Entities

    An entity is a single resource that has been retrieved from the API, usually from an endpoint with the entity's id
    as the final path part.  A successful response from an entity request will be a JSON object with a top level `data`
    key pointing to a sub-object with the following members:

    + `id`

    The identifier for the entity.  This MUST be included with [PUT and PATCH
    requests](#formatting-postputpatch-request-bodies).

    + `type`

    The type identifier of this entity.  This MUST be included with [all create/update
    requests](#formatting-postputpatch-request-bodies).

    + `attributes`

    The properties of the entity.  Names, descriptions, etc.

    + `relationships`

    Relationships are urls to other entities or entity collections that have a relationship to the entity. For example,
    the node entity provides a `contributors` relationship that points to the endpoint to retrieve all contributors to
    that node.  It is recommended to use these links rather than to id-filter general entity collection endpoints.
    They'll be faster, easier, and less error-prone.  Generally a relationship will have the following structure:

        {relationship_name}: {
            "links": {
                "related": {
                    "href": {url_to_related_entity_or_entity_collection},
                    "meta": {}
                }
            }
        }

    If there are no related entities, `href` will be null.

    + `embeds`

    Please see `Embedding` documentation under `Requests`.

    + `links`

    Links are urls to alternative representations of the entity or actions that may be performed on the entity.  Most
    entities will provide a `self` link that is the canonical endpoint for the entity where update and delete requests
    should be sent.  In-depth documentation of actions is available by navigating to the `self` link in the Browsable
    API.  Most entities will also provide an `html` link that directs to the entity's page on the [OSF](http://osf.io/).

    ###Entity Collections

    Entity collection endpoints return a list of entities and an additional data structure with pagination links, such as
    "next", "prev", "first", and "last". The OSF API limits all entity collection responses to a maximum of 10 entities.
    The response object has two keys:

    + `data`

    `data` is an array of entities that match the query.  Each entity in the array is the same representation that is
    returned from that entity's `self` link, meaning that refetching the entity is unnecessary.

    + `links`

    `links` contains pagination information, including links to the previous, next, first, and last pages of results.
    The meta key contains the total number of entities available, as well as the current number of results displayed per
    page.  If there are only enough results to fill one page, the `first`, `last`, `prev`, and `next` values will be
    null.

    ###Errors

    When a request fails for whatever reason, the OSF API will return an appropriate HTTP error code and include a
    descriptive error in the body of the response.  The response body will be an object with a key, `errors`, pointing
    to an array of error objects.  Generally, these error objects will consist of a `detail` key with a detailed error
    message and a `source` object that may contain a field `pointer` that is a [JSON
    Pointer](https://tools.ietf.org/html/rfc6901) to the error-causing attribute. The `error` objects may include
    additional information in accordance with the [JSON-API error spec](http://jsonapi.org/format/1.0/#error-objects).

    ####Example: Error response from an incorrect create node request

        {
          "errors": [
            {
              "source": {
                "pointer": "/data/attributes/category"
              },
              "detail": "This field is required."
            },
            {
              "source": {
                "pointer": "/data/type"
              },
              "detail": "This field may not be null."
            },
            {
              "source": {
                "pointer": "/data/attributes/title"
              },
              "detail": "This field is required."
            }
          ]
        }

    ##OSF Enum Fields

    Some entities in the OSF API have fields that only take a restricted set of values.  Those fields are listed here
    for reference.  Fuller descriptions are available on the relevant entity pages.

    ###OSF Node Categories

        value                 description
        ==========================================
        project               Project
        hypothesis            Hypothesis
        methods and measures  Methods and Measures
        procedure             Procedure
        instrumentation       Instrumentation
        data                  Data
        analysis              Analysis
        communication         Communication
        other                 Other

    ###OSF Node Permission keys

        value        description
        ==========================================
        read         Read-only access
        write        Write access (make changes, cannot delete)
        admin        Admin access (full write, create, delete, contributor add)

    ###Storage Providers

    Valid storage providers are:

        value        description
        ==========================================
        bitbucket    Bitbucket
        box          Box.com
        dataverse    Dataverse
        dropbox      Dropbox
        figshare     figshare
        github       GitHub
        googledrive  Google Drive
        osfstorage   OSF Storage
        s3           Amazon S3

    """
    if request.user and not request.user.is_anonymous:
        user = request.user
        current_user = UserSerializer(user, context={'request': request}).data
    else:
        current_user = None
    kwargs = request.parser_context['kwargs']
    return_val = {
        'meta': {
            'message': 'Welcome to the OSF API.',
            'version': request.version,
            'current_user': current_user,
        },
        'links': {
            'nodes':
            utils.absolute_reverse('nodes:node-list', kwargs=kwargs),
            'users':
            utils.absolute_reverse('users:user-list', kwargs=kwargs),
            'collections':
            utils.absolute_reverse('collections:collection-list',
                                   kwargs=kwargs),
            'registrations':
            utils.absolute_reverse('registrations:registration-list',
                                   kwargs=kwargs),
            'institutions':
            utils.absolute_reverse('institutions:institution-list',
                                   kwargs=kwargs),
            'licenses':
            utils.absolute_reverse('licenses:license-list', kwargs=kwargs),
            'metaschemas':
            utils.absolute_reverse('metaschemas:metaschema-list',
                                   kwargs=kwargs),
            'addons':
            utils.absolute_reverse('addons:addon-list', kwargs=kwargs),
        }
    }

    if utils.has_admin_scope(request):
        return_val['meta']['admin'] = True

    return Response(return_val)
Example #38
0
 def get_wiki_content(self, obj):
     return absolute_reverse('wikis:wiki-content',
                             kwargs={
                                 'wiki_id': obj._id,
                             })
Example #39
0
 def get_absolute_url(self, obj):
     return absolute_reverse('collections:collection-detail',
                             kwargs={'collection_id': obj._id})
Example #40
0
 def get_storage_addons_url(self, obj):
     return absolute_reverse('addons:addon-list',
                             query_kwargs={'filter[categories]': 'storage'})
Example #41
0
 def get_action_url(self, obj):
     return utils.absolute_reverse('actions:action-detail', kwargs={'action_id': obj._id, 'version': self.context['request'].parser_context['kwargs']['version']})
Example #42
0
 def get_preprints_url(self, obj):
     return absolute_reverse('preprint_providers:preprints-list', kwargs={
         'provider_id': obj._id,
         'version': self.context['request'].parser_context['kwargs']['version']
     })
Example #43
0
 def self_url(self, obj):
     return absolute_reverse('identifiers:identifier-detail',
                             kwargs={
                                 'identifier_id': obj._id,
                             })
Example #44
0
    def update(self, preprint, validated_data):
        assert isinstance(
            preprint,
            Preprint), 'You must specify a valid preprint to be updated'

        auth = get_user_auth(self.context['request'])
        if not preprint.has_permission(auth.user, osf_permissions.WRITE):
            raise exceptions.PermissionDenied(
                detail=
                'User must have admin or write permissions to update a preprint.'
            )

        published = validated_data.pop('is_published', None)
        if published and preprint.provider.is_reviewed:
            raise Conflict(
                '{} uses a moderation workflow, so preprints must be submitted for review instead of published directly. Submit a preprint by creating a `submit` Action at {}'
                .format(
                    preprint.provider.name,
                    absolute_reverse(
                        'preprints:preprint-review-action-list',
                        kwargs={
                            'version':
                            self.context['request'].parser_context['kwargs']
                            ['version'],
                            'preprint_id':
                            preprint._id,
                        },
                    ),
                ))

        save_preprint = False
        recently_published = False

        primary_file = validated_data.pop('primary_file', None)
        if primary_file:
            self.set_field(preprint.set_primary_file, primary_file, auth)
            save_preprint = True

        old_tags = set(preprint.tags.values_list('name', flat=True))
        if 'tags' in validated_data:
            current_tags = set(validated_data.pop('tags', []))
        elif self.partial:
            current_tags = set(old_tags)
        else:
            current_tags = set()

        for new_tag in (current_tags - old_tags):
            preprint.add_tag(new_tag, auth=auth)
        for deleted_tag in (old_tags - current_tags):
            preprint.remove_tag(deleted_tag, auth=auth)

        if 'node' in validated_data:
            node = validated_data.pop('node', None)
            self.set_field(preprint.set_supplemental_node, node, auth)
            save_preprint = True

        if 'subjects' in validated_data:
            subjects = validated_data.pop('subjects', None)
            self.set_field(preprint.set_subjects, subjects, auth)
            save_preprint = True

        if 'title' in validated_data:
            title = validated_data['title']
            self.set_field(preprint.set_title, title, auth)
            save_preprint = True

        if 'description' in validated_data:
            description = validated_data['description']
            self.set_field(preprint.set_description, description, auth)
            save_preprint = True

        if 'article_doi' in validated_data:
            preprint.article_doi = validated_data['article_doi']
            save_preprint = True

        if 'license_type' in validated_data or 'license' in validated_data:
            license_details = get_license_details(preprint, validated_data)
            self.set_field(preprint.set_preprint_license, license_details,
                           auth)
            save_preprint = True

        if 'original_publication_date' in validated_data:
            preprint.original_publication_date = validated_data[
                'original_publication_date'] or None
            save_preprint = True

        if published is not None:
            if not preprint.primary_file:
                raise exceptions.ValidationError(
                    detail=
                    'A valid primary_file must be set before publishing a preprint.'
                )
            self.set_field(preprint.set_published, published, auth)
            save_preprint = True
            recently_published = published
            preprint.set_privacy('public', log=False, save=True)

        if save_preprint:
            preprint.save()

        if recently_published:
            for author in preprint.contributors:
                if author != auth.user:
                    project_signals.contributor_added.send(
                        preprint,
                        contributor=author,
                        auth=auth,
                        email_template='preprint')

        return preprint
Example #45
0
 def get_related_url(self, obj):
     return absolute_reverse('users:user-institutions',
                             kwargs={'user_id': obj['self']._id})
Example #46
0
 def self_url(self, obj):
     return absolute_reverse('files:version-detail', kwargs={
         'version_id': obj.identifier,
         'file_id': self.context['view'].kwargs['file_id'],
         'version': self.context['request'].parser_context['kwargs']['version']
     })
Example #47
0
    def update(self, preprint, validated_data):
        assert isinstance(
            preprint,
            PreprintService), 'You must specify a valid preprint to be updated'
        assert isinstance(
            preprint.node, Node
        ), 'You must specify a preprint with a valid node to be updated.'

        auth = get_user_auth(self.context['request'])
        if not preprint.node.has_permission(auth.user, 'admin'):
            raise exceptions.PermissionDenied(
                detail='User must be an admin to update a preprint.')

        published = validated_data.pop('is_published', None)
        if published and preprint.provider.is_reviewed:
            raise Conflict(
                '{} uses a moderation workflow, so preprints must be submitted for review instead of published directly. Submit a preprint by creating a `submit` Action at {}'
                .format(
                    preprint.provider.name,
                    absolute_reverse(
                        'actions:create-action',
                        kwargs={
                            'version':
                            self.context['request'].parser_context['kwargs']
                            ['version']
                        })))

        save_node = False
        save_preprint = False
        recently_published = False
        primary_file = validated_data.pop('primary_file', None)
        if primary_file:
            self.set_field(preprint.set_primary_file, primary_file, auth)
            save_node = True

        old_tags = set(preprint.node.tags.values_list('name', flat=True))
        if validated_data.get('node') and 'tags' in validated_data['node']:
            current_tags = set(validated_data['node'].pop('tags', []))
        elif self.partial:
            current_tags = set(old_tags)
        else:
            current_tags = set()

        for new_tag in (current_tags - old_tags):
            preprint.node.add_tag(new_tag, auth=auth)
        for deleted_tag in (old_tags - current_tags):
            preprint.node.remove_tag(deleted_tag, auth=auth)

        if 'node' in validated_data:
            preprint.node.update(fields=validated_data.pop('node'))
            save_node = True

        if 'subjects' in validated_data:
            subjects = validated_data.pop('subjects', None)
            self.set_field(preprint.set_subjects, subjects, auth)
            save_preprint = True

        if 'article_doi' in validated_data:
            preprint.node.preprint_article_doi = validated_data['article_doi']
            save_node = True

        if 'license_type' in validated_data or 'license' in validated_data:
            license_details = get_license_details(preprint, validated_data)
            self.set_field(preprint.set_preprint_license, license_details,
                           auth)
            save_preprint = True

        if 'original_publication_date' in validated_data:
            preprint.original_publication_date = validated_data[
                'original_publication_date']
            save_preprint = True

        if published is not None:
            if not preprint.primary_file:
                raise exceptions.ValidationError(
                    detail=
                    'A valid primary_file must be set before publishing a preprint.'
                )
            self.set_field(preprint.set_published, published, auth)
            save_preprint = True
            recently_published = published
            preprint.node.set_privacy('public')
            save_node = True

        if save_node:
            try:
                preprint.node.save()
            except ValidationError as e:
                # Raised from invalid DOI
                raise exceptions.ValidationError(detail=e.messages[0])

        if save_preprint:
            preprint.save()

        # Send preprint confirmation email signal to new authors on preprint! -- only when published
        # TODO: Some more thought might be required on this; preprints made from existing
        # nodes will send emails making it seem like a new node.
        if recently_published:
            for author in preprint.node.contributors:
                if author != auth.user:
                    project_signals.contributor_added.send(
                        preprint.node,
                        contributor=author,
                        auth=auth,
                        email_template='preprint')

        return preprint
Example #48
0
 def get_root_folder(self, obj):
     return absolute_reverse(
         'nodes:node-addon-folders',
         kwargs=self.context['request'].parser_context['kwargs'],
     )
Example #49
0
 def get_absolute_url(self, obj):
     return absolute_reverse('guids:guid-detail', kwargs={
         'guids': obj._id,
         'version': self.context['request'].parser_context['kwargs']['version']
     })
Example #50
0
 def get_wiki_content(self, obj):
     return absolute_reverse('wikis:wiki-content', kwargs={
         'wiki_id': obj._id,
         'version': self.context['request'].parser_context['kwargs']['version']
     })
Example #51
0
 def get_registration_url(self, obj):
     return absolute_reverse('registrations:registration-detail',
                             kwargs={'node_id': obj._id})
Example #52
0
 def get_related_url(self, obj):
     return absolute_reverse('users:user-institutions', kwargs={
         'user_id': obj['self']._id,
         'version': self.context['request'].parser_context['kwargs']['version']
     })
Example #53
0
 def get_absolute_url(self, obj):
     return absolute_reverse('nodes:node-provider-detail',
                             kwargs={
                                 'node_id': obj.node._id,
                                 'provider': obj.provider
                             })
Example #54
0
 def get_search_field_url(self, field, query):
     view_name = 'search:search-{}'.format(field)
     return absolute_reverse(view_name, query_kwargs={'q': query})
Example #55
0
 def absolute_api_v2_url(self):
     from api.base.utils import absolute_reverse  # Avoid circular dependency
     return absolute_reverse('users:user-detail',
                             kwargs={'user_id': self.pk})
Example #56
0
 def get_absolute_url(self, obj):
     return absolute_reverse(
         'addons:addon-list',
         kwargs=self.context['request'].parser_context['kwargs'],
     )
Example #57
0
 def get_absolute_url(self, obj):
     return absolute_reverse('moderators:provider-moderator-detail', kwargs={
         'provider_id': self.context['request'].parser_context['kwargs']['version'],
         'moderator_id': obj._id,
         'version': self.context['request'].parser_context['kwargs']['version']})
Example #58
0
 def absolute_api_v2_url(self):
     from api.base.utils import absolute_reverse
     return absolute_reverse('institutions:institution-detail', kwargs={'institution_id': self._id, 'version': 'v2'})
Example #59
0
 def get_absolute_url(self, obj):
     return absolute_reverse('users:user-detail',
                             kwargs={'user_id': obj._id})
Example #60
0
 def get_absolute_url(self, obj):
     return absolute_reverse('licenses:license-detail', kwargs={
         'license_id': obj._id,
         'version': self.context['request'].parser_context['kwargs']['version']
     })