Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#18: Allow embed=[] in methods and embed resources in response #20

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

kshepherd
Copy link
Collaborator

@alanorth This is one way of fixing #18 -- I am interested to hear if you think it's the right approach.

We can request linked HAL stuff to be embedded in the parent object using ?embed=... in the REST API call. So this change allows a client to say bitstreams = d.get_bitstreams(bundle=my_bundle, embeds=['format']), and then in the resulting Bitstream objects you can get format out like format = BitstreamFormat(bitstream.embedded['format']) without any additional API calls.

The only thing I wasn't sure about is if we should handle that in the client lib instead of making the actual client do it... this way keeps things relatively simple in the library, we don't have to know anything in the bitstream model because it'll just copy all _embedded JSON into a dict. If we wanted to make that model more 'smart' we'd tell it what kind of embeds to expect, and we could parse and instantiate the BitstreamFormat objects before handing back in the get_bitstreams() return value.

Here is my example script to test it out:

import pprint

from dspace_rest_client.client import DSpaceClient
from dspace_rest_client.models import BitstreamFormat

# Authenticate against the DSpace client
authenticated = d.authenticate()
if not authenticated:
    print('Error logging in! Giving up.')
    exit(1)

print('\nExample of ORIGINAL bundle output with format embedded.\n')
# Get top communities
top_communities = d.get_communities(top=True)
for top_community in top_communities:
    # Get all collections in this community
    collections = d.get_collections(community=top_community)
    for collection in collections:
        # Get all items in this collection - see that the recommended method is a search, scoped to this collection
        # (there is no collection/items endpoint, though there is a /mappedItems endpoint, not yet implemented here)
        items = d.search_objects(query='*:*', scope=collection.uuid, dso_type='item')
        for item in items:
            print(f'{item.name} ({item.uuid})')
            # Get all bundles in this item
            bundles = d.get_bundles(parent=item)
            for bundle in bundles:
                if bundle.name == 'ORIGINAL':
                    print(f'\n\nBUNDLE {bundle.name} ({bundle.uuid})')
                    # Get all bitstreams in this bundle
                    bitstreams = d.get_bitstreams(bundle=bundle, embeds=['format'])
                    for bitstream in bitstreams:
                        print(f'{bitstream.name} ({bitstream.uuid})')
                        if 'format' in bitstream.embedded:
                            format = BitstreamFormat(bitstream.embedded['format'])
                            pprint.pp(format.as_dict())

@kshepherd kshepherd self-assigned this Jun 11, 2024
@kshepherd
Copy link
Collaborator Author

(i should note, you can also just bypass the python obj and use the dict directly like bitstream.embedded['format']['mimetype'] -- it's hard to predict with the different ways and context we use libs like this, whether the "knowledge" of how the data looks should be in the client impl, or further up in the library

@kshepherd
Copy link
Collaborator Author

@dpk interested in your thoughts on this PR / approaches like it

@@ -16,6 +16,7 @@
"""
import json
import logging
import pprint
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This import doesn’t seem to be used

@@ -595,6 +596,8 @@ def get_bitstreams(self, uuid=None, bundle=None, page=0, size=20, sort=None):
@param size: Size of results per page (default: 20)
@return: list of python Bitstream objects
"""
if embeds is None:
embeds = []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason not to request the format by default? It seems a very weird default of DSpace not to include this information to begin with, but there might be a reason for it. (E.g. if the format embed can be very large, but this doesn’t seem to be the case.)

We could leave the option to pass embeds=[] explicitly if this is useful (for performance? I’m really struggling to understand why it is useful to know about the name of a file, the size of a file, the MD5 hash of a file, but not the format of a file 🤔)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair question - it comes down to DSpace modelling.. a bitstream format is itself an addressable object in the database and might need to be accessed or inspected outside the context of a bitstream or file (e.g. managing supported formats and their descriptions, known extensions, etc). So it's a separate resource, and is linked from a bitstream (as well as other places). You're right that it's usually embedded in the UI requests since... why wouldn't you want it.

And the idea of linked resources (where you can either follow the HAL link or ask for the resource to be embedded in the original request in advance) does give either option.

All good questions! With regards to how the backend resources look and work, we can question and change those in the scope of those projects (DSpace/DSpace, DSpace/RestContract, dspace-angular), but for this library I've been simply trying to work with how the REST API does work in practice, and try to play nice with the HAL paradigm

@dpk
Copy link
Contributor

dpk commented Nov 12, 2024

This feature should also be added to get_bitstreams_iter once #27 is merged

…t in response

_embedded in HALResource
new BitstreamFormat model
get_bitstreams takes e.g. (embeds=['format'])
optional list of resources to embed instead of just link
@kshepherd
Copy link
Collaborator Author

@alanorth @dpk now most get, create, update (any method that ultimately returns a DSpace Object or list of DSpace Objects) can take an embeds list param, which will mean those links resources get embedded in the response JSON instead of linked, so you don't have to go off and do an extra request to see e.g. bitstream format, or community logo, or collection owningCommunity, etc.

for the search objects methods, the embeds param will apply to each object in the list of search results

@kshepherd kshepherd changed the title #18: Allow embed=[] in get_bitstreams and embed format in response #18: Allow embed=[] in methods and embed resources in response Nov 21, 2024
@dpk
Copy link
Contributor

dpk commented Nov 21, 2024

I wonder how plausible it would be to auto-detect object types and wrap them in the correct model object class (for embeds and possibly also dso search)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants